svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.96k stars 516 forks source link

ES2015 Block Scoping #284

Open kphillisjr opened 9 years ago

kphillisjr commented 9 years ago

This may be a somewhat easier task to handle, but it does offer some serious improvements.

fatcerberus commented 7 years ago

It seems that this could be implemented in the compiler by pushing a new declarative environment upon entering a block and popping it when the block closes. That's the easy part. Hoisting and TDZ semantics will be more complicated.

svaarala commented 7 years ago

Pushing an explicit scope object would be quite slow and it should be avoided in the very common case where it's not needed.

Doing so needs more compiler smarts though.

Try-catch catch block binding uses an explicit scope object and it too is slow. But it's less of an issue for try-catch because error catching is not usually in a hot path.

svaarala commented 7 years ago

So cases where each let variable should just be a register includes things like:

if (something) {
    let x = somethingElse;
    frobnicate(x);
}

Because no closure can be created inside the if statement, there's no need to use explicit scope objects for the binding. A register allocated for the duration of the "if" is sufficient.

However, here a scope object is needed:

if (something) {
    let x = somethingElse;
    frobnicate(function () { /*...*/ });
}

Depending on the contents of the inner function it's possible to prove that the binding won't be accessed from the inner function either, but this becomes a bit more difficult for each step.

What I would like as the minimum good baseline is that the former case worked without creating a scope object while any potentially unsafe case (inner function, eval, etc) could conservatively create one.