Commit 7a0a6820 authored by neis's avatar neis Committed by Commit bot

[es6] Implement completion value reform (--harmony-completion).

This CL depends on #1362363002.

R=rossberg
BUG=

Review URL: https://codereview.chromium.org/1361403003

Cr-Commit-Position: refs/heads/master@{#31180}
parent 3feba644
......@@ -1880,6 +1880,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_concat_spreadable)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_completion)
void Genesis::InitializeGlobal_harmony_tolength() {
......@@ -2606,6 +2607,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_simd_natives[] = {"native harmony-simd.js",
nullptr};
static const char* harmony_tolength_natives[] = {nullptr};
static const char* harmony_completion_natives[] = {nullptr};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
......
......@@ -199,7 +199,8 @@ DEFINE_BOOL(legacy_const, true, "legacy semantics for const in sloppy mode")
V(harmony_destructuring, "harmony destructuring") \
V(harmony_default_parameters, "harmony default parameters") \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd")
V(harmony_simd, "harmony simd") \
V(harmony_completion, "harmony completion value semantics")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
......
......@@ -3339,16 +3339,18 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
Scope* inner_scope, bool is_const, ZoneList<const AstRawString*>* names,
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
Statement* body, bool* ok) {
// ES6 13.6.3.4 specifies that on each loop iteration the let variables are
// copied into a new environment. After copying, the "next" statement of the
// loop is executed to update the loop variables. The loop condition is
// checked and the loop body is executed.
// ES6 13.7.4.8 specifies that on each loop iteration the let variables are
// copied into a new environment. Moreover, the "next" statement must be
// evaluated not in the environment of the just completed iteration but in
// that of the upcoming one. We achieve this with the following desugaring.
// Extra care is needed to preserve the completion value of the original loop.
//
// We rewrite a for statement of the form
// We are given a for statement of the form
//
// labels: for (let/const x = i; cond; next) body
//
// into
// and rewrite it as follows. Here we write {{ ... }} for init-blocks, ie.,
// blocks whose ignore_completion_value_ flag is set.
//
// {
// let/const x = i;
......@@ -3356,29 +3358,21 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
// first = 1;
// undefined;
// outer: for (;;) {
// { // This block's only function is to ensure that the statements it
// // contains do not affect the normal completion value. This is
// // accomplished by setting its ignore_completion_value bit.
// // No new lexical scope is introduced, so lexically scoped variables
// // declared here will be scoped to the outer for loop.
// let/const x = temp_x;
// if (first == 1) {
// first = 0;
// } else {
// next;
// }
// flag = 1;
// }
// let/const x = temp_x;
// {{ if (first == 1) {
// first = 0;
// } else {
// next;
// }
// flag = 1;
// if (!cond) break;
// }}
// labels: for (; flag == 1; flag = 0, temp_x = x) {
// if (cond) {
// body
// } else {
// break outer;
// }
// }
// if (flag == 1) {
// break;
// body
// }
// {{ if (flag == 1) // Body used break.
// break;
// }}
// }
// }
......@@ -3386,7 +3380,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
Scope* for_scope = scope_;
ZoneList<Variable*> temps(names->length(), zone());
Block* outer_block = factory()->NewBlock(NULL, names->length() + 3, false,
Block* outer_block = factory()->NewBlock(NULL, names->length() + 4, false,
RelocInfo::kNoPosition);
// Add statement: let/const x = i.
......@@ -3443,7 +3437,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
Block* inner_block =
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
Block* ignore_completion_block = factory()->NewBlock(
NULL, names->length() + 2, true, RelocInfo::kNoPosition);
NULL, names->length() + 3, true, RelocInfo::kNoPosition);
ZoneList<Variable*> inner_vars(names->length(), zone());
// For each let variable x:
// make statement: let/const x = temp_x.
......@@ -3502,6 +3496,16 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
ignore_completion_block->statements()->Add(assignment_statement, zone());
}
// Make statement: if (!cond) break.
if (cond) {
Statement* stop =
factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition);
Statement* noop = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
ignore_completion_block->statements()->Add(
factory()->NewIfStatement(cond, noop, stop, cond->position()), zone());
}
inner_block->statements()->Add(ignore_completion_block, zone());
// Make cond expression for main loop: flag == 1.
Expression* flag_cond = NULL;
......@@ -3540,23 +3544,14 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
compound_next, RelocInfo::kNoPosition);
}
// Make statement: if (cond) { body; } else { break outer; }
Statement* body_or_stop = body;
if (cond) {
Statement* stop =
factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition);
body_or_stop =
factory()->NewIfStatement(cond, body, stop, cond->position());
}
// Make statement: labels: for (; flag == 1; flag = 0, temp_x = x)
// Note that we re-use the original loop node, which retains its labels
// and ensures that any break or continue statements in body point to
// the right place.
loop->Initialize(NULL, flag_cond, compound_next_statement, body_or_stop);
loop->Initialize(NULL, flag_cond, compound_next_statement, body);
inner_block->statements()->Add(loop, zone());
// Make statement: if (flag == 1) { break; }
// Make statement: {{if (flag == 1) break;}}
{
Expression* compare = NULL;
// Make compare expresion: flag == 1.
......@@ -3571,7 +3566,10 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
Statement* if_flag_break =
factory()->NewIfStatement(compare, stop, empty, RelocInfo::kNoPosition);
inner_block->statements()->Add(if_flag_break, zone());
Block* ignore_completion_block =
factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition);
ignore_completion_block->statements()->Add(if_flag_break, zone());
inner_block->statements()->Add(ignore_completion_block, zone());
}
inner_scope->set_end_position(scanner()->location().end_pos);
......
......@@ -62,6 +62,9 @@ class Processor: public AstVisitor {
Token::ASSIGN, result_proxy, value, RelocInfo::kNoPosition);
}
// Inserts '.result = undefined' in front of the given statement.
Statement* AssignUndefinedBefore(Statement* s);
// Node visitors.
#define DEF_VISIT(type) virtual void Visit##type(type* node) override;
AST_NODE_LIST(DEF_VISIT)
......@@ -73,6 +76,20 @@ class Processor: public AstVisitor {
};
Statement* Processor::AssignUndefinedBefore(Statement* s) {
Expression* result_proxy = factory()->NewVariableProxy(result_);
Expression* undef = factory()->NewUndefinedLiteral(RelocInfo::kNoPosition);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, result_proxy, undef, RelocInfo::kNoPosition);
Block* b = factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
b->statements()->Add(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
zone());
b->statements()->Add(s, zone());
return b;
}
void Processor::Process(ZoneList<Statement*>* statements) {
for (int i = statements->length() - 1; i >= 0; --i) {
Visit(statements->at(i));
......@@ -116,6 +133,11 @@ void Processor::VisitIfStatement(IfStatement* node) {
node->set_else_statement(replacement_);
is_set_ = is_set_ && set_in_then;
replacement_ = node;
if (FLAG_harmony_completion && !is_set_) {
is_set_ = true;
replacement_ = AssignUndefinedBefore(node);
}
}
......@@ -127,6 +149,11 @@ void Processor::VisitIterationStatement(IterationStatement* node) {
node->set_body(replacement_);
is_set_ = is_set_ && set_after;
replacement_ = node;
if (FLAG_harmony_completion && !is_set_) {
is_set_ = true;
replacement_ = AssignUndefinedBefore(node);
}
}
......@@ -166,6 +193,11 @@ void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
node->set_catch_block(static_cast<Block*>(replacement_));
is_set_ = is_set_ && set_in_try;
replacement_ = node;
if (FLAG_harmony_completion && !is_set_) {
is_set_ = true;
replacement_ = AssignUndefinedBefore(node);
}
}
......@@ -198,6 +230,11 @@ void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
Visit(node->try_block());
node->set_try_block(replacement_->AsBlock());
replacement_ = node;
if (FLAG_harmony_completion && !is_set_) {
is_set_ = true;
replacement_ = AssignUndefinedBefore(node);
}
}
......@@ -211,6 +248,11 @@ void Processor::VisitSwitchStatement(SwitchStatement* node) {
}
is_set_ = is_set_ && set_after;
replacement_ = node;
if (FLAG_harmony_completion && !is_set_) {
is_set_ = true;
replacement_ = AssignUndefinedBefore(node);
}
}
......@@ -230,6 +272,11 @@ void Processor::VisitWithStatement(WithStatement* node) {
Visit(node->statement());
node->set_statement(replacement_);
replacement_ = node;
if (FLAG_harmony_completion && !is_set_) {
is_set_ = true;
replacement_ = AssignUndefinedBefore(node);
}
}
......
......@@ -189,10 +189,10 @@ assertEquals(undefined, eval("for (let i = 0; i < 10; i++) { continue; i; }"));
assertEquals(0, eval("for (let i = 0; true;) { i; break; }"));
assertEquals(0, eval("for (const i = 0; true;) { i; break; }"));
assertEquals(9, eval("for (let i = 0; i < 10; i++) { i; continue; }"));
assertEquals(3, eval("for (let i = 0; true; i++) { i; if (i >= 3) break; }"));
assertEquals(2, eval("for (let i = 0; true; i++) { if (i >= 3) break; i; }"));
assertEquals(3, eval("for (let i = 0; true; i++) { i; if (i >= 3) break; }")); // --harmony-completion: undefined
assertEquals(2, eval("for (let i = 0; true; i++) { if (i >= 3) break; i; }")); // --harmony-completion: undefined
assertEquals(
2, eval("for (let i = 0; i < 10; i++) { if (i >= 3) continue; i; }"));
2, eval("for (let i = 0; i < 10; i++) { if (i >= 3) continue; i; }")); // --harmony-completion: undefined
assertEquals(undefined, eval("foo: for (let i = 0; true;) { break foo; }"));
assertEquals(undefined, eval("foo: for (const i = 0; true;) { break foo; }"));
assertEquals(3, eval("foo: for (let i = 3; true;) { i; break foo; }"));
......@@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --no-legacy-const --harmony-sloppy --harmony-sloppy-let
// Flags: --harmony-completion
function props(x) {
var array = [];
......@@ -190,10 +191,12 @@ assertEquals(undefined, eval("for (let i = 0; i < 10; i++) { continue; i; }"));
assertEquals(0, eval("for (let i = 0; true;) { i; break; }"));
assertEquals(0, eval("for (const i = 0; true;) { i; break; }"));
assertEquals(9, eval("for (let i = 0; i < 10; i++) { i; continue; }"));
assertEquals(3, eval("for (let i = 0; true; i++) { i; if (i >= 3) break; }"));
assertEquals(2, eval("for (let i = 0; true; i++) { if (i >= 3) break; i; }"));
assertEquals(
2, eval("for (let i = 0; i < 10; i++) { if (i >= 3) continue; i; }"));
undefined, eval("for (let i = 0; true; i++) { i; if (i >= 3) break; }"));
assertEquals(
undefined, eval("for (let i = 0; true; i++) { if (i >= 3) break; i; }"));
assertEquals(
undefined, eval("for (let i = 0; i < 10; i++) { if (i >= 3) continue; i; }"));
assertEquals(undefined, eval("foo: for (let i = 0; true;) { break foo; }"));
assertEquals(undefined, eval("foo: for (const i = 0; true;) { break foo; }"));
assertEquals(3, eval("foo: for (let i = 3; true;) { i; break foo; }"));
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-completion --harmony-sloppy-let --no-legacy-const
function assertUndef(x) {
assertEquals(undefined, x);
}
// IfStatement [13.6.7]
assertUndef(eval('42; if (true) ; else 0;')); // ES5: 42
assertUndef(eval('42; if (true) ;')); // ES5: 42
assertUndef(eval('42; if (false) 0;')); // ES5: 42
assertEquals(1, eval('42; if (true) 1;'));
assertEquals(1, eval('42; if (true) 1; else 0;'));
assertEquals(0, eval('42; if (false) 1; else 0;'));
// IterationStatement [13.7]
assertUndef(eval('42; do ; while (false);')); // ES5: 42
assertUndef(eval('42; var x = 1; do ; while (x--);')); // ES5: 42
assertUndef(eval('42; while (false) 0;')); // ES5: 42
assertUndef(eval('42; while (true) break;')); // ES5: 42
assertUndef(eval('42; bla: while (true) break bla;')); // ES5: 42
assertUndef(eval('42; var x = 1; while (x--) ;')); // ES5: 42
assertUndef(eval('42; for (; false; ) 0;')); // ES5: 42
assertUndef(eval('42; for (var x = 1; x; x--) ;')); // ES5: 42
assertUndef(eval('42; for (var x in ["foo", "bar"]) ;'));
assertUndef(eval('42; for (var x of ["foo", "bar"]) ;'));
assertUndef(eval('42; for (let x = 1; x; x--) ;'));
assertUndef(eval('42; for (let x in ["foo", "bar"]) ;'));
assertUndef(eval('42; for (let x of ["foo", "bar"]) ;'));
assertUndef(eval('42; for (const x in ["foo", "bar"]) ;'));
assertUndef(eval('42; for (const x of ["foo", "bar"]) ;'));
assertEquals(1, eval('42; var x = 10; do x--; while (x);'));
assertEquals(1, eval('42; var x = 10; while (x) x--;'));
assertEquals(1, eval('42; for (var x = 10; x; x--) x;'));
assertEquals(1, eval('42; for (var x = 10; x; --x) x;'));
assertEquals(1, eval('42; for (let x = 10; x; --x) x;'));
assertEquals(1, eval('42; var y = 2; for (var x in ["foo", "bar"]) y--;'));
assertEquals(1, eval('42; var y = 2; for (const x in ["foo", "bar"]) y--;'));
assertEquals(1, eval('42; var y = 2; for (let x in ["foo", "bar"]) y--;'));
assertEquals(1, eval('42; var y = 2; for (var x of ["foo", "bar"]) y--;'));
assertEquals(1, eval('42; var y = 2; for (const x of ["foo", "bar"]) y--;'));
assertEquals(1, eval('42; var y = 2; for (let x of ["foo", "bar"]) y--;'));
// WithStatement [13.11.7]
assertUndef(eval('42; with ({}) ;')); // ES5: 42
assertEquals(1, eval('42; with ({}) 1;'));
// SwitchStatement [13.12.11]
assertUndef(eval('42; switch (0) {};')); // ES5: 42
assertUndef(eval('42; switch (0) { case 1: 1; };')); // ES5: 42
assertUndef(eval('42; switch (0) { case 0: ; };')); // ES5: 42
assertUndef(eval('42; switch (0) { default: ; };')); // ES5: 42
assertUndef(eval('42; switch (0) { case 0: break; }')); // ES5: 42
assertEquals(1, eval('42; switch (0) { case 0: 1; }'));
assertEquals(1, eval('42; switch (0) { case 0: 1; break; }'));
assertEquals(1, eval('42; switch (0) { case 0: 1; case 666: break; }'));
assertEquals(2, eval('42; switch (0) { case 0: 1; case 666: 2; break; }'));
// TryStatement [13.15.8]
assertUndef(eval('42; try { } catch(e) { };')); // ES5: 42
assertUndef(eval('42; try { } catch(e) { 0; };')); // ES5: 42
assertUndef(eval('42; try { throw "" } catch(e) { };')); // ES5: 42
assertUndef(eval('42; try { throw "" } catch(e) { } finally { };')); // ES5: 42
assertUndef(eval('42; try { } finally { 666 };')); // ES5: 42
// Some combinations
assertUndef(eval('42; switch (0) { case 0: if (true) break; }')); // ES5: 42
assertUndef(eval('42; switch (0) { case 0: 1; if (true) ; }')); // ES5: 1
assertUndef(eval('42; switch (0) { case 0: 1; try { break } catch(e) { }; }')); // ES5: 1
assertEquals(0, eval('42; switch (0) { case 0: 0; case 1: break; }'));
assertEquals(0, eval('42; while (1) { 0; break; }'))
assertEquals(0, eval('42; bla: while (1) { 0; break bla; }'))
assertEquals(0, eval('42; while (1) { with ({}) { 0; break; } }'))
assertEquals(0, eval('42; while (1) { try { 0; break } catch(e) {666} }'))
assertEquals(0, eval(
'42; while (1) { try { 0; break } catch(e) {666} finally {666} }'))
assertEquals(0, eval(
'42; while (1) { try { throw "" } catch(e) {666} finally {0; break} }'))
assertEquals(0, eval(
'42; while (1) { try { throw "" } catch(e) {0; break} finally {666} }'))
assertEquals(0, eval(
'42; while (1) { try { 666 } finally {0; break} }'));
assertEquals(0, eval(
'42; while (1) { try { 666; break } finally {0; break} }'));
assertEquals(0, eval(
'42; lab: try { 666; break lab } finally {0; break lab}'));
assertEquals(undefined, eval(
'var b = 1; ' +
'outer: while (1) { while (1) { if (b--) 42; else break outer; }; 666 }'));
// The following is not what ES6 says, but see ES bug 4540.
assertUndef(eval('42; switch (0) { case 0: 1; if (true) break; }')); // ES5: 1
////////////////////////////////////////////////////////////////////////////////
//
// The following are copied from webkit/eval-throw-return and adapted.
function throwFunc() {
throw "";
}
function throwOnReturn(){
1;
return throwFunc();
}
function twoFunc() {
2;
}
assertEquals(1, eval("1;"));
assertUndef(eval("1; try { foo = [2,3,throwFunc(), 4]; } catch (e){}"));
assertUndef(eval("1; try { 2; throw ''; } catch (e){}"));
assertUndef(eval("1; try { 2; throwFunc(); } catch (e){}"));
assertEquals(3, eval("1; try { 2; throwFunc(); } catch (e){3;} finally {}"));
assertEquals(3, eval("1; try { 2; throwFunc(); } catch (e){3;} finally {4;}"));
assertUndef(eval("function blah() { 1; }; blah();"));
assertUndef(eval("var x = 1;"));
assertEquals(1, eval("if (true) { 1; } else { 2; }"));
assertEquals(2, eval("if (false) { 1; } else { 2; }"));
assertUndef(eval("try{1; if (true) { 2; throw ''; } else { 2; }} catch(e){}"));
assertEquals(2, eval("1; var i = 0; do { ++i; 2; } while(i!=1);"));
assertUndef(eval(
"try{1; var i = 0; do { ++i; 2; throw ''; } while (i!=1);} catch(e){}"));
assertUndef(eval("1; try{2; throwOnReturn();} catch(e){}"));
assertUndef(eval("1; twoFunc();"));
assertEquals(2, eval("1; with ( { a: 0 } ) { 2; }"));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment