This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 23056 - Function's length property is inconsistent with EcmaScript
Summary: Function's length property is inconsistent with EcmaScript
Status: NEW
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: WebIDL (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Cameron McCormack
QA Contact: public-webapps-bugzilla
URL:
Whiteboard: [v1]
Keywords:
Depends on:
Blocks:
 
Reported: 2013-08-25 07:21 UTC by Chris Dumez
Modified: 2019-02-28 13:25 UTC (History)
7 users (show)

See Also:


Attachments

Description Chris Dumez 2013-08-25 07:21:42 UTC
The length property of an Function object returns the "the length of the shortest argument list of the entries" in the effective overload set. To my understanding, this means that optional parameters are not counted.

E.g.
void myOperation(DOMString a, optional DOMString b);

The length property of this function would return 1 as per Web IDL, not 2.
However, the same function would have a length of 2 as per EcmaScript I believe, which seems a bit inconsistent.

The EcmaScript doc says:
"Every built-in Function object described in this clause—whether as a constructor, an ordinary function, or both—has a length property whose value is an integer. Unless otherwise specified, this value is equal to the largest number of named arguments shown in the subclause headings for the function description, including optional parameters."

If I understand correctly, optional parameters are accounted for when calculating the value returned by the length property in EcmaScript but not in Web IDL. Was this done on purpose?
Comment 1 Boris Zbarsky 2013-08-25 14:13:25 UTC
Which exact ES spec are you looking at?  Back when this was defined, WebIDL _did_ match the ES6 drafts: see bug 19571.
Comment 2 Chris Dumez 2013-08-25 14:59:12 UTC
I checked both the ES 5.1 spec:
http://www.ecma-international.org/ecma-262/5.1/#sec-15

And also the latest following ES6 draft (page 225):
http://wiki.ecmascript.org/lib/exe/fetch.php?id=harmony%3Aspecification_drafts&cache=cache&media=harmony:working_draft_ecma-262_edition_6_08-23-13-nomarkup.pdf

I'm not an EcmaScript expert so I might be misunderstanding.
Comment 3 Boris Zbarsky 2013-08-25 16:08:21 UTC
The ES6 spec is a bit unclear here.

In particular, see section 8.3.16.6 in the draft you link to, which explicitly sets the length to the ExpectedArgumentCount of the ParameterList.  And the definition of that is on page 201 (section 13.1) and returns:

  the number of FormalParameters to the left of either the rest parameter or the
  first FormalParameter with an Initialiser.  A FormalParameter without an
  initialiser is allowed after the first parameter with an initialiser but such
  parameters are considered to be optional with undefined as their default value.

So it sort of depends on what one means by "optional argument", I guess....  We should take this to public-script-coord.
Comment 4 Allen Wirfs-Brock 2013-08-27 23:08:26 UTC
(In reply to comment #3)
> The ES6 spec is a bit unclear here.

what exactly is unclear?

> 
> In particular, see section 8.3.16.6 in the draft you link to, which
> explicitly sets the length to the ExpectedArgumentCount of the
> ParameterList.  And the definition of that is on page 201 (section 13.1) and
> returns:
> 
>   the number of FormalParameters to the left of either the rest parameter or
> the
>   first FormalParameter with an Initialiser.  A FormalParameter without an
>   initialiser is allowed after the first parameter with an initialiser but
> such
>   parameters are considered to be optional with undefined as their default
> value.
> 

The above quote is an informal description of ExpectedArgumentCount from an informative note.  If you trace through the actual normative algorithm so see what it computes for any specific parameter list.  The note should be consistent with the results you get.

> So it sort of depends on what one means by "optional argument", I guess.... 
> We should take this to public-script-coord.

Right, in particular what WebIDL means by "optional argument".  I optional argument is equivalent to "A FormalParameter without an initialiser" then it should exactly match the ES6 rule (for the shortest WebIDL overload).
Comment 5 Boris Zbarsky 2013-08-28 01:34:16 UTC
> what exactly is unclear?

How to map the ES setup to WebIDL.

> The above quote is an informal description of ExpectedArgumentCount from an
> informative note.

Sure.  I read the formal algorithm too.

> Right, in particular what WebIDL means by "optional argument". 

In WebIDL, a function can generally speaking have some number of non-optional arguments, then some number of optional arguments, with or without default values.  Optional arguments are ones that can be not passed in the arguments.length sense.  An example:

  void foo(long arg1, long arg2, optional long arg3, optional long arg4 = 5,
           optional long arg5);

This produces behavior similar to this ES function, as far as I can tell:

  void foo(arg1, arg2, arg3, arg4 = 5, arg5) {
    if (arguments.length < 2) { throw TypeError(); }
    // stuff
  }

If I read the ES spec correctly, the .length of such a function would be 3, correct?  The .length of the WebIDL function shown above is 2 at the moment...
Comment 6 Allen Wirfs-Brock 2013-08-28 04:31:47 UTC
(In reply to comment #5)
>...
> 
> In WebIDL, a function can generally speaking have some number of
> non-optional arguments, then some number of optional arguments, with or
> without default values.  Optional arguments are ones that can be not passed
> in the arguments.length sense.  An example:
> 
>   void foo(long arg1, long arg2, optional long arg3, optional long arg4 = 5,
>            optional long arg5);
> 
> This produces behavior similar to this ES function, as far as I can tell:
> 
>   void foo(arg1, arg2, arg3, arg4 = 5, arg5) {
>     if (arguments.length < 2) { throw TypeError(); }
>     // stuff
>   }
> 
> If I read the ES spec correctly, the .length of such a function would be 3,
> correct?  The .length of the WebIDL function shown above is 2 at the
> moment...

A better mapping is:
   foo(arg1, arg2, arg3 = 0, arg4 = 5, arg5) {
     if (arguments.length < 2) { throw TypeError(); }
     // stuff
   }

Which ES6 will assign a 'length' property of 2.

The appropriate translation of WebIDL to ES6 is that WebIDL "optional" always turns into an ES6 parameter with a default value initialiser. If the WebIDL source includes an explicit default value then that value should be used as the ES6 default value.  If the WebIDL does not have an explicit default value then the implicit WebIDL default should be used as the ES6 default value.

If you do this, then the WebIDL length based upon "optional" should match the ES6 length based upon default value initialisers.
Comment 7 Boris Zbarsky 2013-08-28 05:08:12 UTC
> A better mapping is:

No, that's semantically different.  In your mapping, if I call foo(1, 2) that's the same as calling foo(1, 2, 0), but in the WebIDL behavior arg3 does not have 0 as a value, or indeed any value at all.  It's simply "not passed".  As is arg5.  But arg4 has the value 5.

> The appropriate translation of WebIDL to ES6 is that WebIDL "optional" always
> turns into an ES6 parameter with a default value initialiser.

Why?

> the implicit WebIDL default should be used as the ES6 default value.

There is no implicit default value.

Might be worth having this conversation in only one spot, by the way; I suggest the list.
Comment 8 Tab Atkins Jr. 2013-08-28 05:12:41 UTC
(In reply to comment #7)
> > A better mapping is:
> 
> No, that's semantically different.  In your mapping, if I call foo(1, 2)
> that's the same as calling foo(1, 2, 0), but in the WebIDL behavior arg3
> does not have 0 as a value, or indeed any value at all.  It's simply "not
> passed".  As is arg5.  But arg4 has the value 5.

"Not passed" means passing undefined, so you could just pass that as the default value.
Comment 9 Boris Zbarsky 2013-08-28 05:56:06 UTC
We could, but why do we want to do that?

Again, why is mapping a WebIDL "optional" argument to something like "arg3 = undefined" better conceptually than mapping it to "arg3" without an initializer?
Comment 10 Erik Arvidsson 2013-08-28 13:22:09 UTC
This is pretty straightforward to spec in a way that matches ES6 without resorting to ES6 syntax.

1. Let length be the number of parameters, including optional and rest parameters.
2. For each parameter of the parameters list, iterating backwards:
   1. If the parameter is an optional parameter, let length be length - 1.
   2. Else if the parameter is a rest parameter, let length be length - 1.
   3. Else return length.
3. Return length.

If there are overloaded operations, return the smallest length.


Boris, ES6 defines undefined to be treated as the sentinel used for no argument. This is important because a user can pass you undefined as a place holder.

function f(y = 1) {
  return y;
}
assert(f(undefined) == 1);

function g(x = 1, y = 2) {
  return x + y;
}
assert(g(undefined, 5) == 6);
Comment 11 Allen Wirfs-Brock 2013-08-28 16:16:02 UTC
(In reply to comment #10)
> This is pretty straightforward to spec in a way that matches ES6 without
> resorting to ES6 syntax.
> ...
>

But even better then trying to replicate the results of the ES6 ExpectedArguemntCount would be to just specify the translation of WebIDL argument declarations into ES6 FormalParameter syntax.  Then it becomes completely up to ES to define the initial value of the 'length' property.

In general we should be looking for ways to eliminate unnecessary redundancy between WebIDL and ES. Much better to have a single concept spec'ed at exactly one place.
Comment 12 Boris Zbarsky 2013-08-28 21:10:48 UTC
> Boris, ES6 defines undefined to be treated as the sentinel used for no
> argument. 

Yes, I'm well aware.  Please see the posts on public-script-coord from this morning.

> would be to just specify the translation of WebIDL argument declarations into
> ES6 FormalParameter syntax.

That sounds just fine to me; we should define such a translation.  That's what the public-script-coord thread is about at this point.
Comment 13 Cameron McCormack 2014-01-27 22:05:23 UTC
Also see thread at: ttp://www.w3.org/mid/CAD649j7JA6rHSrRg60vaJY+4w9mFXq4a_Uv-HdjWtKHxF1mxAg@mail.gmail.c
Comment 14 Ms2ger 2019-02-28 13:25:45 UTC
(In reply to Cameron McCormack from comment #13)
> Also see thread at:
> ttp://www.w3.org/mid/CAD649j7JA6rHSrRg60vaJY+4w9mFXq4a_Uv-
> HdjWtKHxF1mxAg@mail.gmail.c

That would be https://lists.w3.org/Archives/Public/public-script-coord/2013JulSep/0490.html