Skip to content

Commit 8d4e7ca

Browse files
authored
Clarifications to dictionary semantics
This rewrites some of the text around dictionaries to clear up the confusion in #852, as well as generally try to smooth out the reading experience and definitions. Concrete changes: * Rewrote "dictionaries are always passed by value" to be more specific and clear. * Moved "dictionaries must not be used as the type of an attribute or constant" near the top of the dictionary section, commensurate with its importance. * Removed the terms "present" and "not present"; instead we can use Infra's "exists" for maps. * Fixed several cases where the overload resolution algorithm discussed arguments being "not present", even though that was only defined for dictionaries. Instead it now correctly talks about "missing". * Rewrote the discussion of required dictionary members and dictionary members with default values to make it clear how they contribute to the dictionary's entries. * Added a note to the IDL-to-ES conversion algorithm for dictionaries explicitly pointing out that default-value dictionary members will always be present, and thus always show up in the output. Closes #852. Closes #524.
1 parent 31871e8 commit 8d4e7ca

File tree

1 file changed

+63
-54
lines changed

1 file changed

+63
-54
lines changed

index.bs

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2287,7 +2287,7 @@ corresponding argument omitted.
22872287
If the type of an argument is a [=dictionary type=] or a [=union type=] that has
22882288
a [=dictionary type=] as one
22892289
of its [=flattened member types=], and that dictionary type and its ancestors have no
2290-
[=required dictionary member|required members=], and the argument is either the final argument or is
2290+
[=dictionary member/required=] [=dictionary member|members=], and the argument is either the final argument or is
22912291
followed only by [=optional arguments=], then the argument must be specified as
22922292
optional and have a default value provided.
22932293

@@ -3885,8 +3885,8 @@ defined with [=optional arguments=] and merged into one,
38853885
};
38863886
</pre>
38873887

3888-
the [=overload resolution algorithm=] would treat the <var ignore>path</var> argument as [=not
3889-
present=] given the same call <code>stroke(undefined)</code>, and not throw any exceptions.
3888+
the [=overload resolution algorithm=] would treat the <var ignore>path</var> argument as missing
3889+
given the same call <code>stroke(undefined)</code>, and not throw any exceptions.
38903890

38913891
Note: For this particular example, the latter behavior is actually what Web developers would
38923892
generally expect. If {{CanvasDrawPath}} were to be designed today, [=optional arguments=] would be
@@ -4070,8 +4070,7 @@ determine what Web IDL language feature to use:
40704070
class="idl">foo(|arg|)</code> operation, with |arg| set to null, while <code>foo()</code> alone
40714071
would go to the first overload. This can be a surprising behavior for many API users. Instead,
40724072
specification authors are encouraged to use an [=optional argument=], which would categorize
4073-
both <code>foo()</code> and <code>foo(undefined)</code> as "|arg| is [=not
4074-
present=]".
4073+
both <code>foo()</code> and <code>foo(undefined)</code> as "|arg| is missing".
40754074

40764075
<pre highlight="webidl">
40774076
interface A {
@@ -4748,9 +4747,15 @@ where [=map/keys=] are strings and [=map/values=] are of a particular type speci
47484747
};
47494748
</pre>
47504749

4751-
Dictionaries are always passed by value. In language bindings where a dictionary is represented by an object of some kind, passing a
4752-
dictionary to a [=platform object=] will not result in a reference to the dictionary being kept by that object.
4753-
Similarly, any dictionary returned from a platform object will be a copy and modifications made to it will not be visible to the platform object.
4750+
Dictionary instances do not retain a reference to their language-specific representations (e.g.,
4751+
the corresponding ECMAScript object). So for example, returning a dictionary from an [=operation=]
4752+
will result in a new ECMAScript object being created from the current values of the dictionary. And,
4753+
an operation that accepts a dictionary as an argument will perform a one-time conversion from the
4754+
given ECMAScript value into the dictionary, based on the current properties of the ECMAScript
4755+
object. Modifications to the dictionary will not be reflected in the corresponding ECMAScript
4756+
object, and vice-versa.
4757+
4758+
Dictionaries must not be used as the type of an [=attribute=] or [=constant=].
47544759

47554760
A dictionary can be defined to <dfn id="dfn-inherit-dictionary" for="dictionary" export>inherit</dfn> from another dictionary.
47564761
If the identifier of the dictionary is followed by a colon and a [=identifier=],
@@ -4779,30 +4784,49 @@ from another dictionary, then the set is empty. Otherwise, the set
47794784
includes the dictionary |E| that |D| [=interface/inherits=]
47804785
from and all of |E|’s [=inherited dictionaries=].
47814786

4782-
A dictionary value of type |D| can have key–value pairs corresponding
4783-
to the dictionary members defined on |D| and on any of |D|’s
4784-
[=inherited dictionaries=].
4785-
On a given dictionary value, the presence of each dictionary member
4786-
is optional, unless that member is specified as required.
4787-
A dictionary member is said to be
4788-
<dfn id="dfn-present" export lt="present|not present" for="dictionary member">present</dfn>
4789-
in a dictionary value if the value [=map/exists|contains an entry with the key=]
4790-
given by the member's [=identifier=], otherwise it is [=not present=].
4791-
Dictionary members can also optionally have a <dfn id="dfn-dictionary-member-default-value" for="dictionary member" export>default value</dfn>, which is
4792-
the value to use for the dictionary member when passing a value to a
4793-
[=platform object=] that does
4794-
not have a specified value. Dictionary members with default values are
4795-
always considered to be present.
4787+
[=Dictionary members=] can be specified as
4788+
<dfn id="required-dictionary-member" export for="dictionary member">required</dfn>, meaning that
4789+
converting a language-specific value to a dictionary requires providing a value for that member. Any
4790+
dictionary member that is not [=dictionary member/required=] is
4791+
<dfn export for="dictionary member">optional</dfn>.
4792+
4793+
Note that specifying [=dictionary members=] as [=dictionary member/required=] only has
4794+
an observable effect when converting other representations of dictionaries (like an ECMAScript value
4795+
supplied as an argument to an [=operation=]) to an IDL dictionary. Specification authors should
4796+
leave the members [=dictionary member/optional=] in all other cases, including when a dictionary
4797+
type is used solely as the [=return type=] of [=operations=].
4798+
4799+
[=dictionary member/Optional=] [=dictionary members=] can also be specified as having a
4800+
<dfn id="dfn-dictionary-member-default-value" for="dictionary member" export>default value</dfn>,
4801+
which is the value used by default when author code or specification text does not provide a value
4802+
for that member.
4803+
4804+
A given dictionary value of type |D| can have [=map/entries=] for each of the dictionary members
4805+
defined on |D| and on any of |D|’s [=inherited dictionaries=]. Dictionary members that are specified
4806+
as [=dictionary member/required=], or that are specified as having a
4807+
[=dictionary member/default value=], will always have such corresponding [=map/entries=]. Other
4808+
members' entries might or might not [=map/exist=] in the dictionary value.
47964809

47974810
<p class="note">
4798-
In the ECMAScript binding, a value of <emu-val>undefined</emu-val> is treated as
4799-
[=not present=], or will trigger the [=dictionary member/default value=] where applicable.
4811+
In the ECMAScript binding, a value of <emu-val>undefined</emu-val> for the property
4812+
corresponding to a [=dictionary member=] is treated the same as omitting that property. Thus, it
4813+
will cause an error if the member is [=dictionary member/required=], or will trigger the
4814+
[=dictionary member/default value=] if one is present, or will result in no [=map/entry=]
4815+
existing in the dictionary value otherwise.
4816+
</p>
4817+
4818+
<p class="advisement">
4819+
As with [=optional argument/default value|operation argument default values=], it is strongly
4820+
encouraged not to use <emu-val>true</emu-val> as the [=dictionary member/default value=] for
4821+
{{boolean}}-typed [=dictionary members=], as this can be confusing for authors who might
4822+
otherwise expect the default conversion of <emu-val>undefined</emu-val> to be used (i.e.,
4823+
<emu-val>false</emu-val>).
48004824
</p>
48014825

48024826
An [=ordered map=] with string [=map/keys=] can be implicitly treated as a dictionary value of a
4803-
specific dictionary |D| if all of its [=map/entries=] correspond to [=dictionary members=], in the
4804-
correct order and with the correct types, and with appropriate [=map/entries=] for any required
4805-
dictionary members.
4827+
specific dictionary |D| if all of its [=map/entries=] correspond to [=dictionary members=], as long
4828+
as those entries have the correct types, and there are [=map/entries=] present for any
4829+
[=dictionary member/required=] or [=dictionary member/default value|defaulted=] dictionary members.
48064830

48074831
<div class="example">
48084832
<pre highlight="webidl">
@@ -4818,16 +4842,6 @@ dictionary members.
48184842
1. Return «[ "name" → "test", "serviceIdentifiers" → |identifiers| ]».
48194843
</div>
48204844

4821-
<p class="advisement">
4822-
As with [=optional argument/default value|operation argument default values=],
4823-
it is strongly suggested not to use <emu-val>true</emu-val> as the
4824-
[=dictionary member/default value=] for
4825-
{{boolean}}-typed
4826-
[=dictionary members=],
4827-
as this can be confusing for authors who might otherwise expect the default
4828-
conversion of <emu-val>undefined</emu-val> to be used (i.e., <emu-val>false</emu-val>).
4829-
</p>
4830-
48314845
Each [=dictionary member=] (matching
48324846
<emu-nt><a href="#prod-DictionaryMember">DictionaryMember</a></emu-nt>) is specified
48334847
as a type (matching <emu-nt><a href="#prod-Type">Type</a></emu-nt>) followed by an
@@ -4883,9 +4897,8 @@ is an [=enumeration=], then its
48834897
be one of the [=enumeration values|enumeration’s values=].
48844898

48854899
If the type of the dictionary member is preceded by the
4886-
<emu-t>required</emu-t> keyword, the member is considered a
4887-
<dfn id="required-dictionary-member" export>required dictionary member</dfn>
4888-
and must be present on the dictionary.
4900+
<emu-t>required</emu-t> keyword, the member is considered a [=dictionary member/required=]
4901+
[=dictionary member=].
48894902

48904903
<pre highlight="webidl" class="syntax">
48914904
dictionary identifier {
@@ -5007,10 +5020,6 @@ The identifier of a dictionary member must not be
50075020
the same as that of another dictionary member defined on the dictionary or
50085021
on that dictionary’s [=inherited dictionaries=].
50095022

5010-
Dictionaries must not be used as the type of an
5011-
[=attribute=] or
5012-
[=constant=].
5013-
50145023
No [=extended attributes=] are applicable to dictionaries.
50155024

50165025
<div data-fill-with="grammar-Partial"></div>
@@ -7890,9 +7899,7 @@ the object (or its prototype chain) correspond to [=dictionary members=].
78907899
running the following algorithm (where |D| is the [=dictionary type=]):
78917900

78927901
1. If <a abstract-op>Type</a>(|esDict|) is not Undefined, Null or Object, then [=ECMAScript/throw=] a {{ECMAScript/TypeError}}.
7893-
1. Let |idlDict| be an empty dictionary value of type |D|;
7894-
every [=dictionary member=]
7895-
is initially considered to be [=not present=].
7902+
1. Let |idlDict| be an empty [=ordered map=], representing a dictionary of type |D|.
78967903
1. Let |dictionaries| be a list consisting of |D| and all of |D|’s [=inherited dictionaries=],
78977904
in order from least to most derived.
78987905
1. For each dictionary |dictionary| in |dictionaries|, in order:
@@ -7908,13 +7915,12 @@ the object (or its prototype chain) correspond to [=dictionary members=].
79087915
</dl>
79097916
1. If |esMemberValue| is not <emu-val>undefined</emu-val>, then:
79107917
1. Let |idlMemberValue| be the result of [=converted to an IDL value|converting=] |esMemberValue| to an IDL value whose type is the type |member| is declared to be of.
7911-
1. Set the dictionary member on |idlDict| with key name |key| to the value |idlMemberValue|. This dictionary member is considered to be [=present=].
7918+
1. [=map/Set=] |idlDict|[|key|] to |idlMemberValue|.
79127919
1. Otherwise, if |esMemberValue| is <emu-val>undefined</emu-val> but |member| has a [=dictionary member/default value=], then:
79137920
1. Let |idlMemberValue| be |member|’s default value.
7914-
1. Set the dictionary member on |idlDict| with key name |key| to the value |idlMemberValue|. This dictionary member is considered to be [=present=].
7915-
1. Otherwise, if |esMemberValue| is
7916-
<emu-val>undefined</emu-val> and |member| is a
7917-
[=required dictionary member=], then throw a {{ECMAScript/TypeError}}.
7921+
1. [=map/Set=] |idlDict|[|key|] to |idlMemberValue|.
7922+
1. Otherwise, if |esMemberValue| is <emu-val>undefined</emu-val> and |member| is
7923+
[=dictionary member/required=], then throw a {{ECMAScript/TypeError}}.
79187924
1. Return |idlDict|.
79197925
</div>
79207926

@@ -7933,10 +7939,13 @@ up on the ECMAScript object are not necessarily the same as the object’s prope
79337939
1. For each dictionary |dictionary| in |dictionaries|, in order:
79347940
1. For each dictionary member |member| declared on |dictionary|, in lexicographical order:
79357941
1. Let |key| be the [=identifier=] of |member|.
7936-
1. If the dictionary member named |key| is [=present=] in |V|, then:
7937-
1. Let |idlValue| be the value of |member| on |V|.
7942+
1. If |V|[|key|] [=map/exists=], then:
7943+
1. Let |idlValue| be |V|[|key|].
79387944
1. Let |value| be the result of [=converted to an ECMAScript value|converting=] |idlValue| to an ECMAScript value.
79397945
1. Perform [=!=] <a abstract-op>CreateDataProperty</a>(|O|, |key|, |value|).
7946+
7947+
<p class="note">Recall that if |member| has a [=dictionary member/default value=],
7948+
then |key| will always [=map/exist=] in |V|.</p>
79407949
1. Return |O|.
79417950
</div>
79427951

0 commit comments

Comments
 (0)