Commit 2c2848dc authored by rossberg's avatar rossberg Committed by Commit bot

[es6] Scoping & initialization for var shadowing non-simple parameters

Var-bindings may shadow parameters from a non-simple parameter list. When that happens: they create separate bindings, but are initialised with the respective parameter value. Thus:

(function(x, f = () => x) { var x; var y = x; x = 2; return [x, y, f()] })(1) -->  [2, 1, 1]

This CL implements that by inserting a suitable assignment for every shadwowing var-variable (e.g., x = outer_x above) at the beginning of the function's body block.

R=adamk@chromium.org
BUG=v8:4440,v8:811
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31042}
parent af33cccf
......@@ -4682,6 +4682,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
inner_scope = inner_scope->FinalizeBlockScope();
if (inner_scope != nullptr) {
CheckConflictingVarDeclarations(inner_scope, CHECK_OK);
InsertShadowingVarBindingInitializers(inner_block);
}
result->Add(init_block, zone());
......@@ -4941,8 +4942,7 @@ Literal* Parser::GetLiteralUndefined(int position) {
void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
Declaration* decl = scope->CheckConflictingVarDeclarations();
if (decl != NULL) {
// In harmony mode we treat conflicting variable bindinds as early
// errors. See ES5 16 for a definition of early errors.
// In ES6, conflicting variable bindings are early errors.
const AstRawString* name = decl->proxy()->raw_name();
int position = decl->proxy()->position();
Scanner::Location location = position == RelocInfo::kNoPosition
......@@ -4955,6 +4955,31 @@ void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
}
void Parser::InsertShadowingVarBindingInitializers(Block* inner_block) {
// For each var-binding that shadows a parameter, insert an assignment
// initializing the variable with the parameter.
Scope* inner_scope = inner_block->scope();
DCHECK(inner_scope->is_declaration_scope());
Scope* function_scope = inner_scope->outer_scope();
DCHECK(function_scope->is_function_scope());
ZoneList<Declaration*>* decls = inner_scope->declarations();
for (int i = 0; i < decls->length(); ++i) {
Declaration* decl = decls->at(i);
if (decl->mode() != VAR || !decl->IsVariableDeclaration()) continue;
const AstRawString* name = decl->proxy()->raw_name();
Variable* parameter = function_scope->LookupLocal(name);
if (parameter == nullptr) continue;
VariableProxy* to = inner_scope->NewUnresolved(factory(), name);
VariableProxy* from = factory()->NewVariableProxy(parameter);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, to, from, RelocInfo::kNoPosition);
Statement* statement = factory()->NewExpressionStatement(
assignment, RelocInfo::kNoPosition);
inner_block->statements()->InsertAt(0, statement, zone());
}
}
void Parser::InsertSloppyBlockFunctionVarBindings(Scope* scope, bool* ok) {
// For each variable which is used as a function declaration in a sloppy
// block,
......
......@@ -1123,8 +1123,8 @@ class Parser : public ParserBase<ParserTraits> {
// Get odd-ball literals.
Literal* GetLiteralUndefined(int position);
// For harmony block scoping mode: Check if the scope has conflicting var/let
// declarations from different scopes. It covers for example
// Check if the scope has conflicting var/let declarations from different
// scopes. This covers for example
//
// function f() { { { var x; } let x; } }
// function g() { { var x; let x; } }
......@@ -1134,6 +1134,10 @@ class Parser : public ParserBase<ParserTraits> {
// hoisted over such a scope.
void CheckConflictingVarDeclarations(Scope* scope, bool* ok);
// Insert initializer statements for var-bindings shadowing parameter bindings
// from a non-simple parameter list.
void InsertShadowingVarBindingInitializers(Block* block);
// Implement sloppy block-scoped functions, ES2015 Annex B 3.3
void InsertSloppyBlockFunctionVarBindings(Scope* scope, bool* ok);
......
......@@ -601,10 +601,6 @@ Declaration* Scope::CheckConflictingVarDeclarations() {
const AstRawString* name = decl->proxy()->raw_name();
// Iterate through all scopes until and including the declaration scope.
// If the declaration scope is a (declaration) block scope, also continue
// (that is to handle the special inner scope of functions with
// destructuring parameters, which may not shadow any variables from
// the surrounding function scope).
Scope* previous = NULL;
Scope* current = decl->scope();
// Lexical vs lexical conflicts within the same scope have already been
......@@ -620,7 +616,7 @@ Declaration* Scope::CheckConflictingVarDeclarations() {
}
previous = current;
current = current->outer_scope_;
} while (!previous->is_declaration_scope() || previous->is_block_scope());
} while (!previous->is_declaration_scope());
}
return NULL;
}
......
......@@ -957,11 +957,85 @@
assertEquals(1, ok1(1));
function ok2(x) { 'use strict'; { let x = 2; return x; } };
assertEquals(2, ok2(1));
}());
(function TestShadowingOfParameters() {
function f1({x}) { var x = 2; return x }
assertEquals(2, f1({x: 1}));
function f2({x}) { { var x = 2; } return x; }
assertEquals(2, f2({x: 1}));
function f3({x}) { var y = x; var x = 2; return y; }
assertEquals(1, f3({x: 1}));
function f4({x}) { { var y = x; var x = 2; } return y; }
assertEquals(1, f4({x: 1}));
function f5({x}, g = () => x) { var x = 2; return g(); }
assertEquals(1, f5({x: 1}));
function f6({x}, g = () => x) { { var x = 2; } return g(); }
assertEquals(1, f6({x: 1}));
function f7({x}) { var g = () => x; var x = 2; return g(); }
assertEquals(2, f7({x: 1}));
function f8({x}) { { var g = () => x; var x = 2; } return g(); }
assertEquals(2, f8({x: 1}));
function f9({x}, g = () => eval("x")) { var x = 2; return g(); }
assertEquals(1, f9({x: 1}));
function f10({x}, y) { var y; return y }
assertEquals(2, f10({x: 6}, 2));
function f11({x}, y) { var z = y; var y = 2; return z; }
assertEquals(1, f11({x: 6}, 1));
function f12(y, g = () => y) { var y = 2; return g(); }
assertEquals(1, f12(1));
function f13({x}, y, [z], v) { var x, y, z; return x*y*z*v }
assertEquals(210, f13({x: 2}, 3, [5], 7));
function f20({x}) { function x() { return 2 }; return x(); }
assertEquals(2, f20({x: 1}));
function f21({x}) { { function x() { return 2 } } return x(); }
assertEquals(2, f21({x: 1}));
var g1 = ({x}) => { var x = 2; return x };
assertEquals(2, g1({x: 1}));
var g2 = ({x}) => { { var x = 2; } return x; };
assertEquals(2, g2({x: 1}));
var g3 = ({x}) => { var y = x; var x = 2; return y; };
assertEquals(1, g3({x: 1}));
var g4 = ({x}) => { { var y = x; var x = 2; } return y; };
assertEquals(1, g4({x: 1}));
var g5 = ({x}, g = () => x) => { var x = 2; return g(); };
assertEquals(1, g5({x: 1}));
var g6 = ({x}, g = () => x) => { { var x = 2; } return g(); };
assertEquals(1, g6({x: 1}));
var g7 = ({x}) => { var g = () => x; var x = 2; return g(); };
assertEquals(2, g7({x: 1}));
var g8 = ({x}) => { { var g = () => x; var x = 2; } return g(); };
assertEquals(2, g8({x: 1}));
var g9 = ({x}, g = () => eval("x")) => { var x = 2; return g(); };
assertEquals(1, g9({x: 1}));
var g10 = ({x}, y) => { var y; return y };
assertEquals(2, g10({x: 6}, 2));
var g11 = ({x}, y) => { var z = y; var y = 2; return z; };
assertEquals(1, g11({x: 6}, 1));
var g12 = (y, g = () => y) => { var y = 2; return g(); };
assertEquals(1, g12(1));
var g13 = ({x}, y, [z], v) => { var x, y, z; return x*y*z*v };
assertEquals(210, g13({x: 2}, 3, [5], 7));
var g20 = ({x}) => { function x() { return 2 }; return x(); }
assertEquals(2, g20({x: 1}));
var g21 = ({x}) => { { function x() { return 2 } } return x(); }
assertEquals(2, g21({x: 1}));
assertThrows("function f({x}) { var x; }; f({});", SyntaxError);
assertThrows("function f({x}) { { var x; } }; f({});", SyntaxError);
assertThrows("'use strict'; function f(x) { let x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; function f({x}) { let x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; function f(x) { const x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; function f({x}) { const x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; let g = (x) => { let x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; let g = ({x}) => { let x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; let g = (x) => { const x = 0; }; f({});", SyntaxError);
assertThrows("'use strict'; let g = ({x}) => { const x = 0; }; f({});", SyntaxError);
}());
......
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