| Index: src/parsing/rewriter.cc
|
| diff --git a/src/parsing/rewriter.cc b/src/parsing/rewriter.cc
|
| index 57009bd20707af3260e2e6e367d72eef374dffb6..19e9e4cd80d05d9cc07138f6c61e91d16a06c8d3 100644
|
| --- a/src/parsing/rewriter.cc
|
| +++ b/src/parsing/rewriter.cc
|
| @@ -20,6 +20,7 @@ class Processor final : public AstVisitor<Processor> {
|
| result_assigned_(false),
|
| replacement_(nullptr),
|
| is_set_(false),
|
| + breakable_(false),
|
| zone_(ast_value_factory->zone()),
|
| closure_scope_(closure_scope),
|
| factory_(ast_value_factory) {
|
| @@ -33,6 +34,7 @@ class Processor final : public AstVisitor<Processor> {
|
| result_assigned_(false),
|
| replacement_(nullptr),
|
| is_set_(false),
|
| + breakable_(false),
|
| zone_(ast_value_factory->zone()),
|
| closure_scope_(closure_scope),
|
| factory_(ast_value_factory) {
|
| @@ -77,6 +79,22 @@ class Processor final : public AstVisitor<Processor> {
|
| // was hoping for.
|
| bool is_set_;
|
|
|
| + bool breakable_;
|
| +
|
| + class BreakableScope final {
|
| + public:
|
| + explicit BreakableScope(Processor* processor, bool breakable = true)
|
| + : processor_(processor), previous_(processor->breakable_) {
|
| + processor->breakable_ = processor->breakable_ || breakable;
|
| + }
|
| +
|
| + ~BreakableScope() { processor_->breakable_ = previous_; }
|
| +
|
| + private:
|
| + Processor* processor_;
|
| + bool previous_;
|
| + };
|
| +
|
| Zone* zone_;
|
| DeclarationScope* closure_scope_;
|
| AstNodeFactory factory_;
|
| @@ -106,7 +124,13 @@ Statement* Processor::AssignUndefinedBefore(Statement* s) {
|
|
|
|
|
| void Processor::Process(ZoneList<Statement*>* statements) {
|
| - for (int i = statements->length() - 1; i >= 0; --i) {
|
| + // If we're in a breakable scope (named block, iteration, or switch), we walk
|
| + // all statements. The last value producing statement before the break needs
|
| + // to assign to .result. If we're not in a breakable scope, only the last
|
| + // value producing statement in the block assigns to .result, so we can stop
|
| + // early.
|
| + for (int i = statements->length() - 1; i >= 0 && (breakable_ || !is_set_);
|
| + --i) {
|
| Visit(statements->at(i));
|
| statements->Set(i, replacement_);
|
| }
|
| @@ -122,7 +146,10 @@ void Processor::VisitBlock(Block* node) {
|
| // with some JS VMs: For instance, using smjs, print(eval('var x = 7'))
|
| // returns 'undefined'. To obtain the same behavior with v8, we need
|
| // to prevent rewriting in that case.
|
| - if (!node->ignore_completion_value()) Process(node->statements());
|
| + if (!node->ignore_completion_value()) {
|
| + BreakableScope scope(this, node->labels() != nullptr);
|
| + Process(node->statements());
|
| + }
|
| replacement_ = node;
|
| }
|
|
|
| @@ -157,6 +184,7 @@ void Processor::VisitIfStatement(IfStatement* node) {
|
|
|
|
|
| void Processor::VisitIterationStatement(IterationStatement* node) {
|
| + BreakableScope scope(this);
|
| // Rewrite the body.
|
| bool set_after = is_set_;
|
| is_set_ = false; // We are in a loop, so we can't rely on [set_after].
|
| @@ -253,6 +281,7 @@ void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
|
|
|
|
|
| void Processor::VisitSwitchStatement(SwitchStatement* node) {
|
| + BreakableScope scope(this);
|
| // Rewrite statements in all case clauses (in reverse order).
|
| ZoneList<CaseClause*>* clauses = node->cases();
|
| bool set_after = is_set_;
|
|
|