Hi,

I have been plugging away at the block scope stuff.  My current work is
on https://bugs.webkit.org/show_bug.cgi?id=74708.  Like some other
patches, it uses catch clauses as the proof of concept, because there
are so many tests around that area.  It does lazy creation and tear-off
if possible, and only in the case in which a scope has captured
variables.

I have two remaining things to fix before it is fully baked.

One is static lookup of free variables (e.g. ResolveResult::Lexical
variables).  The issue is that with lazy scope creation ("reification")
the depth in the scope chain cannot be computed statically.  Let's say
you are resolving "foo" in:

  (function () {
    var foo = 1;
    return function () {
      var bar = 3;
      function qux(x) { return bar+x; }
      return bar + foo;
    }
  })()

Here, both functions will lazily create their activations.  (I put in
the qux to make sure the inner function has a lazy activation.)  Here is
their bytecode:

Outer:
    32 m_instructions; 256 bytes at 0xb10cf0; 1 parameter(s); 12 callee 
register(s); 4 variable(s)

    [   0] enter
    [   1] init_lazy_reg         r0
    [   3] init_lazy_reg         r2
    [   5] init_lazy_reg         r1
    [   7] mov           r3, Int32: 1(@k0)
    [  10] create_activation r0
    [  12] new_func_exp  r4, f0
    [  15] mov           r5, Undefined(@k1)
    [  18] call  r4, 1, 12
    [  24] op_call_put_result            r4
    [  27] tear_off_activation   r0, r2
    [  30] ret           r4

    Constants:
       k0 = Int32: 1
       k1 = Undefined

Inner:
    27 m_instructions; 216 bytes at 0xb0c930; 1 parameter(s); 6 callee 
register(s); 5 variable(s)

    [   0] enter
    [   1] init_lazy_reg         r0
    [   3] init_lazy_reg         r2
    [   5] init_lazy_reg         r1
    [   7] init_lazy_reg         r4
    [   9] mov           r3, Int32: 3(@k0)
    [  12] get_scoped_var        r5, 3, 1
    [  17] add           r5, r3, r5
    [  22] tear_off_activation   r0, r2
    [  25] ret           r5

    Constants:
       k0 = Int32: 3

Note the get_scoped_var in the inner function, corresponding to the
resolution of "foo".  It indicates that "foo" can be found in the third
register of the scope object one scope deep.  However there might be an
additional object on the scope chain, corresponding to the lazily
created activation of the inner function.

There is explicit code to account for skipping over an activation in the
implementations of put_scoped_var and get_scoped_var.  But with more
lazily reified scopes that may or may not be on the scope chain
(depending on whether the code path traversed a with, eval, or function
expression), such an approach doesn't work.  You can't determine the
static depth from the top of the scope chain, because you'd like to
avoid creating the entire scope chain if you can.

I suggest that we change the users of scope chains (eval, with, and
function expressions) to receive the scope chain register as a
parameter.  The compiler already has to handle lazy scope creation, so
it's just as easy to put the scope in a temporary as it is to put it in
the callframe.

Free variable lookup will continue to use the register in the
callframe.  As a bonus, that code can be simpler because it doesn't have
to check if the there is an activation or not.

OK, that's one.


The second issue is handling temporal dead zone errors.  In ES6 it is a
SyntaxError if you access the value of a let- or const-bound variable
before it is initialized.  In practice this means that in the general
case, we mark uninitialized variables with a sentinel value (JSValue(),
via init_lazy_reg).  Access to let- or const-bound variables that need a
barrier is preceded by a check that the variable is initialized.  We can
prove for some cases that no barrier is needed, and elide the barrier,
but OK.

My question is how to structure this check.  Currently I have an
assert_lazy_reg_init opcode that throws an error if the value
isEmpty().  I guess we need to associate a name with that error too, so
that opcode should take an identifier as a parameter as well.  WDYT?

Currently there is also an assert_lazy_reg_not_init opcode, but that was
based on a misunderstanding -- you can only initialize const variables,
not assign to them.  Hence initialization doesn't need such a check.


Finally, two more thoughts.  One is that we can use op_reify_scope /
op_tear_off_scope for parameters and locals in some cases, if a function
does not have non-strict eval, as Gavin suggested.  Also I hope to get
a patch for "const" in today.  The break-the-web aspect is a bit
tricky, but we'll see how that goes.

Thanks for comments!

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

Reply via email to