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

feat: Add [NonEnumerableMethods] extended attribute #825

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 170 additions & 11 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -9432,6 +9432,80 @@ corresponding to [=interface members=].
</div>


<h4 id="LegacyEnumerableMethod" extended-attribute lt="LegacyEnumerableMethod">[LegacyEnumerableMethod]</h4>

<p class="advisement">
The [{{LegacyEnumerableMethod}}] [=extended attribute=] is an undesirable feature.
It exists only so that legacy Web platform features can be specified.
It should not be used in specifications
unless required to specify the behavior of legacy APIs,
or for consistency with these APIs.
Editors who wish to use this feature are strongly advised to discuss this
by <a href="https://github.com/heycam/webidl/issues/new?title=Intent%20to%20use%20[LegacyEnumerableMethod]">filing an issue</a>
before proceeding.
</p>

If the [{{LegacyEnumerableMethod}}]
[=extended attribute=]
appears on a [=regular operation|regular=]
or [=static operations|static=]
[=operation=] specified on an interface with [{{NonEnumerableMethods}}],
then it indicates that the ECMAScript property will be enumerable.

The [{{LegacyEnumerableMethod}}]
extended attribute must
[=takes no arguments|take no arguments=]
and must not appear on methods of an interface
that does not have [{{NonEnumerableMethods}}].

<div class="example">

The following [=IDL fragment=] defines an interface with
[{{NonEnumerableMethods}}], which also specifies an instance
and static operation with [{{LegacyEnumerableMethod}}]:

<pre highlight="webidl">
[Exposed=Window,
NonEnumerableMethods]
interface HasEnumerableAndNonEnumerableMethods {
[LegacyEnumerableMethod]
static void staticFoo();
static void staticBar();
static attribute any staticDataProperty;

[LegacyEnumerableMethod]
void instanceFoo();
void instanceBar();
attribute any instanceDataProperty;
};
</pre>

An ECMAScript implementation of the above IDL would have the methods
of <code class="idl">HasEnumerableAndNonEnumerableMethods</code>
non-enumerable by default, but operations with [{{LegacyEnumerableMethod}}]
would be enumerable.

<pre highlight="js">
for (const key in HasEnumerableAndNonEnumerableMethods) {
// logs only "staticFoo" and "staticDataProperty"
console.log(key);
}

for (const key in HasEnumerableAndNonEnumerableMethods.prototype) {
// logs only "instanceFoo" and "instanceDataProperty"
console.log(key);
}

// logs an array containing "staticFoo", "staticBar" and "staticDataProperty"
console.log(Object.getOwnPropertyNames(HasEnumerableAndNonEnumerableMethods));

// logs an array containing "instanceFoo", "instanceBar" and "instanceDataProperty"
console.log(Object.getOwnPropertyNames(HasEnumerableAndNonEnumerableMethods.prototype));
</pre>

</div>


<h4 id="LegacyNamespace" extended-attribute lt="LegacyNamespace">[LegacyNamespace]</h4>

<p class="advisement">
Expand Down Expand Up @@ -10026,6 +10100,82 @@ for the specific requirements that the use of
</div>


<h4 id="NonEnumerableMethods" extended-attribute lt="NonEnumerableMethods">[NonEnumerableMethods]</h4>

If the [{{NonEnumerableMethods}}] [=extended attribute=]
appears on an [=interface=], [=interface mixin=] or [=namespace=],
it indicates that all methods defined on that interface
without [{{LegacyEnumerableMethod}}] will be non-enumerable.

The [{{NonEnumerableMethods}}]
extended attribute must
[=takes no arguments|take no arguments=].

Note: Combining the [{{NonEnumerableMethods}}] and [{{LegacyUnenumerableNamedProperties}}]
extended attributes is not forbidden.

<div class="example">

The following [=IDL fragment=] defines two interfaces, one which
has [{{NonEnumerableMethods}}], and one which doesn’t:

<pre highlight="webidl">
[Exposed=Window]
interface HasEnumerableMethods {
static void staticFoo();
static attribute any staticDataProperty;

void instanceFoo();
attribute any instanceDataProperty;
};

[Exposed=Window,
NonEnumerableMethods]
interface NonEnumerableMethods {
static void staticBar();
static attribute any staticDataProperty;

void instanceBar();
attribute any instanceDataProperty;
};
</pre>

An ECMAScript implementation of the above IDL would have
the methods of <code class="idl">HasEnumerableMethods</code>
enumerable, but <code class="idl">NonEnumerableMethods</code>
would only have data properties enumerable.

<pre highlight="js">
for (const key in HasEnumerableMethods) {
// logs both "staticFoo" and "staticDataProperty"
console.log(key);
}

for (const key in HasEnumerableMethods.prototype) {
// logs both "instanceFoo" and "instanceDataProperty"
console.log(key);
}

for (const key in NonEnumerableMethods) {
// logs only "staticDataProperty"
console.log(key);
}

for (const key in NonEnumerableMethods.prototype) {
// logs only "instanceDataProperty"
console.log(key);
}

// logs an array containing "staticBar" and "staticDataProperty"
console.log(Object.getOwnPropertyNames(NonEnumerableMethods));

// logs an array containing "instanceBar" and "instanceDataProperty"
console.log(Object.getOwnPropertyNames(NonEnumerableMethods.prototype));
</pre>

</div>


<h4 id="OverrideBuiltins" extended-attribute lt="OverrideBuiltins">[OverrideBuiltins]</h4>

<div class="advisement">
Expand Down Expand Up @@ -11757,8 +11907,12 @@ in which case they are exposed on every object that [=implements=] the interface
given |op|, |definition|, and |realm|.
1. Let |modifiable| be <emu-val>false</emu-val> if |op| is [=unforgeable=]
and <emu-val>true</emu-val> otherwise.
1. Let |enumerable| be <emu-val>true</emu-val>
1. If |definition| is declared with [{{NonEnumerableMethods}}]
and |op| is not declared with [{{LegacyEnumerableMethod}}]
set |enumerable| to <emu-val>false</emu-val>
1. Let |desc| be the PropertyDescriptor{\[[Value]]: |method|,
\[[Writable]]: |modifiable|, \[[Enumerable]]: <emu-val>true</emu-val>,
\[[Writable]]: |modifiable|, \[[Enumerable]]: |enumerable|,
\[[Configurable]]: |modifiable|}.
1. Let |id| be |op|'s [=identifier=].
1. Perform [=!=] <a abstract-op>DefinePropertyOrThrow</a>(|target|, |id|, |desc|).
Expand Down Expand Up @@ -11995,9 +12149,10 @@ then there must exist a property with the following characteristics:
then the property exists on every object that [=implements=] the interface.
Otherwise, the property exists on the [=interface prototype object=].
* The property has attributes
{ \[[Writable]]: |B|, \[[Enumerable]]: <emu-val>true</emu-val>, \[[Configurable]]: |B| },
{ \[[Writable]]: |B|, \[[Enumerable]]: |E|, \[[Configurable]]: |B| },
where |B| is <emu-val>false</emu-val> if the stringifier is [=unforgeable=] on the interface,
and <emu-val>true</emu-val> otherwise.
and <emu-val>true</emu-val> otherwise, and where |E| is <emu-val>false</emu-val> if the interface
is declared with [{{NonEnumerableMethods}}] and <emu-val>true</emu-val> otherwise.
* <div algorithm="to invoke the toString method of interfaces">

The value of the property is a [=built-in function object=], which behaves as follows:
Expand Down Expand Up @@ -12166,8 +12321,9 @@ If the [=interface=] has any of the following:
* a [=setlike declaration=]

then a <code class="idl">forEach</code> data property must exist with attributes
{ \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: <emu-val>true</emu-val>, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=].
{ \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: |E|, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=], where |E| is <emu-val>false</emu-val> if the interface
is declared with [{{NonEnumerableMethods}}] and <emu-val>true</emu-val> otherwise.

The location of the property is determined as follows:

Expand Down Expand Up @@ -12263,8 +12419,9 @@ property is the String value "<code>forEach</code>".

If the [=interface=] has an [=iterable declaration=] or an [=asynchronously iterable declaration=],
then an <code class="idl">entries</code> data property must exist with attributes
{ \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: <emu-val>true</emu-val>, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=].
{ \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: |E|, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=], where |E| is <emu-val>false</emu-val> if the interface
is declared with [{{NonEnumerableMethods}}] and <emu-val>true</emu-val> otherwise.

The location of the property is determined as follows:

Expand All @@ -12286,8 +12443,9 @@ the value of the {{@@asyncIterator}} property.

If the [=interface=] has an [=iterable declaration=] or an [=asynchronously iterable declaration=],
then a <code class="idl">keys</code> data property must exist with attributes
{ \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: <emu-val>true</emu-val>, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=].
{ \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: |E|, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=], where |E| is <emu-val>false</emu-val> if the interface
is declared with [{{NonEnumerableMethods}}] and <emu-val>true</emu-val> otherwise.

The location of the property is determined as follows:

Expand Down Expand Up @@ -12350,8 +12508,9 @@ The value of the [=function object=]’s <code class="idl">name</code> property

If the [=interface=] has an [=iterable declaration=] or an [=asynchronously iterable declaration=],
then a <code class="idl">values</code> data property must exist
with attributes { \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: <emu-val>true</emu-val>, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=].
with attributes { \[[Writable]]: <emu-val>true</emu-val>, \[[Enumerable]]: |E|, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=function object=], where |E| is <emu-val>false</emu-val> if the interface
is declared with [{{NonEnumerableMethods}}] and <emu-val>true</emu-val> otherwise.

The location of the property is determined as follows:

Expand Down