Hey folks,

As you mentioned, we should probably discuss my block scope on a higher
level than individual bugs.

What I have is a series of patches that implement "block scoping" for
const in strict mode, in the ES6 sense of the phrase.

I wasn't familiar with JSC internals before developing this patch
series, so it has been an educational experience ;)  Therefore, if in
the end we need to take another implementation strategy, I'm fine with
that.  I just needed to dive in and get something done in order to get
productive with the code base.

Now, on to specifics.

There is a similarity between ES6 block scope and the scoping of the
function name, in named function expressions, and the exception
identifier, in catch clauses.  It seems to me that whatever ES6 block
scope implementation that JSC gets should also be available in
non-strict mode in these two specific cases.

To implement block scoping, you start with the parser.  In general there
is a tree of block scopes for every function, and the existence of block
scope imposes constraints for normal "var" declarations, like:

  // assuming strict mode 
  { const x = 10; var x = 20 }      // error

  { const x = 10; { var x = 20; } } // error

  { var x = 10; const x = 20; }     // error

  { const x = 10; const x = 20; }   // error

  { var x = 10; { const x = 20; } } // OK

  { const x = 10; { const x = 20; } } // OK

Before block scope, you don't have to bother with much of this, as all
vars are hoisted to the function, global, or eval code node.  With block
scope, you need to maintain nested scopes at compile time.

You also need to maintain nested scopes at runtime, in the general case:

  function foo(x)
  {
    var y = 10;
    if (x) { const z = 20; { return function(x){eval(x);} } }
    return function(x){eval(x)};
  }
  foo(1)('y') => 10
  foo(1)('z') => 20
  foo(0)('z') => error

But it would be nice to have access to multiple scopes without pushing
scope objects ever:

  function foo()
  {
    var x = 10;
    {
      const y = 20; // allocate in a register in the function codeblock
      return x + y;
    }
  }

So.  Let me walk you through my set of patches.  It took me a while to
rebase them on top of the suggested refactor, and I haven't had time yet
to properly consider the lazy tearoff suggestion, so do keep it in mind
-- but I wanted to show what I have so that at least we are talking
about the same thing.

* Refactor identifier resolution in BytecodeGenerator
  https://bugs.webkit.org/show_bug.cgi?id=76285

This is ResolveResult location = generator.resolve(m_ident).
Performance-neutral, seems like a win refactoring-wise.

* Interpose CodeNode between ScopeNode and ProgramNode et al
  https://bugs.webkit.org/show_bug.cgi?id=74509

This one pulls apart the concerns of holding a codeblock and being a
variable scope into separate classes, looking forward to adding a
BlockScopeNode.

You do need something to hold scoping information at parse and compile
time, and I think this is a fine element of the AST for that.  A block
scope node may or may not correspond to an object in the scope chain at
runtime though: it could be that all of the bindings may be hoisted to
an outer scope.  It could be that some bindings get hoisted.  You might
need a symbol table, you might not.  But I think there are enough things
that are different about functions and blocks in ES that it's worth
dividing this.

* Avoid double-lookup when setting static-scoped vars in strict mode
  https://bugs.webkit.org/show_bug.cgi?id=74628

* Allow JSStaticScope to bind more than one variable
  https://bugs.webkit.org/show_bug.cgi?id=74633

These makes sense iff we go with a JSStaticScope chain.  Dunno, for me
they win either way, but there is room to differ here...

* Optimize access to block-scoped local variables
  https://bugs.webkit.org/show_bug.cgi?id=74708

Here we get interesting!  We track block scopes at compile time.  Note,
we have to do this with either implementation strategy; it's not
sufficient to just push on a block and call it the current compilation
unit, because of how many ES things work on a function level (e.g.
`return').

I really like what this patch does to the bytecompiler, but the runtime
support is nasty: I still have those negative depths in there.  Not sure
what to do about it, but I suppose it's related to the ultimate choice
of how block scopes should work.

I guess I should mention that this patch will allocate all block-scoped
variables in JSStaticScope objects on the heap.  This is not good.  In
many cases, we will be able to allocate them in registers in the
function instead, in the future.  However there is a bright side here,
and that's that `var' access still goes through registers.  The lazy
tear-off solution does have the disadvantage that it penalizes `var'
access within blocks, or access to any block-scoped let/const in an
outer scope.  (A block doesn't logically capture a variable; only a
function or eval does.)

Anyway.  It's there.

* Remove the `value' argument to op_push_new_scope
  https://bugs.webkit.org/show_bug.cgi?id=74718

A refactor, assuming the use of static scope objects..

* Implement block-scoped const in strict mode --harmony
  https://bugs.webkit.org/show_bug.cgi?id=74725

I removed the pieces in this patch that referenced runtime options, and
indeed I removed the #if ENABLE(ES6) stuff as well; it was hindering the
discussion, and in the end things should be able to work together, so I
figured for the time being, it would be OK without the guards.

                             *   *   *

I'm sorry this mail is so long; as the man said, I didn't have time to
make it shorter.  But this does give a general overview of the work.  I
pushed new versions of my patches just as talking points, really; I want
something to go in, but I agree we need to talk it over first, and maybe
come to some alternate strategies.

So.  I'm all ears :)  Let me know what you think.

Andy
_______________________________________________
squirrelfish-dev mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/squirrelfish-dev

Reply via email to