Continuing woes in reading the ES6 spec language

Jason Orendorff jason.orendorff at gmail.com
Thu Sep 12 12:40:15 PDT 2013


Just as a worked example, answering Oliver's question about default
expressions took me the better part of an hour.

The default-expression h() in code like

    function f(x=h(), y) {
        function h() { ... }
    }

is evaluated in the scope of f, as I expected, so {x, h, y} are all in
scope there.

But I was surprised by the timing; h() is evaluated
(a) before y is populated with the actual argument value;
(b) before the arguments object is fully initialized.

That last surprise seems really unfortunate, since the expression can
access the arguments object, observing it before and after
CompleteMapped/StrictArgumentsObject, or even cause those to fail. Can
we change that?

It also means that we have to store actual arguments "somewhere else"
while default-expressions are being evaluated, since the spec requires
y to be undefined while the default-expression h() is being evaluated.

Here are the steps (many are elided).

- Start in 8.1.16.1 Ordinary Function [[Call]] (thisArgument, argumentsList).
- Step 8 or 9 creates localEnv (8 if we're calling an arrow function, 9
  otherwise)
- Steps 10-12 make localEnv the current scope
- Step 13 calls 9.1.16.11 Function Declaration Initialisation, passing
  localEnv
- Step 5 of that algorithm calls VarScopedDeclarations(code).
  - 13.1.1 and 15.1.1 contain implementations of VarScopedDeclarations
    and TopLevelVarScopedDeclarations.
  - VarScopedDeclarations(code) returns a list of the top-level
    VariableStatements and FunctionDeclarations in the function body.
    Note that VariableStatements in blocks are not included. (A bug?)
- Step 8 creates bindings for nested functions.
- Step 9 creates bindings for parameters. They're all undefined so far.
- Step 11 creates a binding for "arguments".
- Step 13 creates bindings for vars (not using the result of step 5,
  but a different static semantic routine, VarDeclaredNames, which
  I assumed, but did not check, is accurate for this purpose).
- Step 15 creates bindings for let/const/class declarations.
- Step 16 actually creates functions (in the scope of the new
  environment) to populate each nested function binding created in step 8.
- Steps 18-20 create the arguments object and populates the binding
  created in step 11.
- Step 21 does Binding Instantiation, which is defined in many places
  but here we care about 14.1.1.2.
  - That invokes Indexed Binding Instantiation on the parts of the
    FormalParameters, which is defined further down in 14.1.1.2.
  - Which ultimately performs Indexed Binding Initialisation for each
    FormalParameter. (This call site is step 3 of the 8th algorithm in
    14.1.1.2.)
  - I wasn't able to find a definition for that, but syntactically
    a FormalParameter is just a BindingElement, and Indexed Binding
Initialization
    is defined for those, in the 15th algorithm in 13.2.3.2.
    - Step 6 of that says, if an Initialiser_opt (that is, a default
      value expression) is present, and the value is undefined,
      we evaluate the Initializer. Since it doesn't say anything
      about in what scope, I assume it uses the current scope,
      established way back at the beginning of this adventure.

-j


More information about the es-discuss mailing list