[es6] Fix function context check for super and new.target
authorrossberg <rossberg@chromium.org>
Thu, 23 Jul 2015 09:58:38 +0000 (02:58 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 23 Jul 2015 09:58:59 +0000 (09:58 +0000)
R=adamk@chromium.org
BUG=v8:3330
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#29803}

src/preparser.h
src/scopes.cc
src/scopes.h
test/mjsunit/harmony/new-target.js
test/mjsunit/harmony/super.js

index 8680f88..5ed1cd6 100644 (file)
@@ -3504,13 +3504,7 @@ ParserBase<Traits>::ParseSuperExpression(bool is_new,
   int pos = position();
   Expect(Token::SUPER, CHECK_OK);
 
-  Scope* scope = scope_->DeclarationScope();
-  while (scope->is_eval_scope() || scope->is_arrow_scope()) {
-    scope = scope->outer_scope();
-    DCHECK_NOT_NULL(scope);
-    scope = scope->DeclarationScope();
-  }
-
+  Scope* scope = scope_->ReceiverScope();
   FunctionKind kind = scope->function_kind();
   if (IsConciseMethod(kind) || IsAccessorFunction(kind) ||
       i::IsConstructor(kind)) {
@@ -3548,14 +3542,7 @@ ParserBase<Traits>::ParseNewTargetExpression(bool* ok) {
   Consume(Token::PERIOD);
   ExpectContextualKeyword(CStrVector("target"), CHECK_OK);
 
-  Scope* scope = scope_->DeclarationScope();
-  while (scope->is_eval_scope() || scope->is_arrow_scope()) {
-    scope = scope->outer_scope();
-    DCHECK_NOT_NULL(scope);
-    scope = scope->DeclarationScope();
-  }
-
-  if (!scope->is_function_scope()) {
+  if (!scope_->ReceiverScope()->is_function_scope()) {
     ReportMessageAt(scanner()->location(),
                     MessageTemplate::kUnexpectedNewTarget);
     *ok = false;
index a044b76..6e1c18d 100644 (file)
@@ -763,24 +763,26 @@ int Scope::ContextChainLength(Scope* scope) {
 }
 
 
-Scope* Scope::ScriptScope() {
+Scope* Scope::DeclarationScope() {
   Scope* scope = this;
-  while (!scope->is_script_scope()) {
+  while (!scope->is_declaration_scope()) {
     scope = scope->outer_scope();
   }
   return scope;
 }
 
 
-Scope* Scope::DeclarationScope() {
+Scope* Scope::ReceiverScope() {
   Scope* scope = this;
-  while (!scope->is_declaration_scope()) {
+  while (!scope->is_script_scope() &&
+         (!scope->is_function_scope() || scope->is_arrow_scope())) {
     scope = scope->outer_scope();
   }
   return scope;
 }
 
 
+
 Handle<ScopeInfo> Scope::GetScopeInfo(Isolate* isolate) {
   if (scope_info_.is_null()) {
     scope_info_ = ScopeInfo::Create(isolate, zone(), this);
index f935418..e565ff6 100644 (file)
@@ -483,15 +483,14 @@ class Scope: public ZoneObject {
   // The number of contexts between this and scope; zero if this == scope.
   int ContextChainLength(Scope* scope);
 
-  // Find the script scope.
-  // Used in modules implemenetation to find hosting scope.
-  // TODO(rossberg): is this needed?
-  Scope* ScriptScope();
-
-  // Find the first function, global, or eval scope.  This is the scope
-  // where var declarations will be hoisted to in the implementation.
+  // Find the first function, script, eval or (declaration) block scope. This is
+  // the scope where var declarations will be hoisted to in the implementation.
   Scope* DeclarationScope();
 
+  // Find the first (non-arrow) function or script scope.  This is where
+  // 'this' is bound, and what determines the function kind.
+  Scope* ReceiverScope();
+
   Handle<ScopeInfo> GetScopeInfo(Isolate* isolate);
 
   // Get the chain of nested scopes within this scope for the source statement
index 0d2873b..9498099 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Flags: --harmony-new-target --harmony-reflect
+// Flags: --harmony-new-target --harmony-reflect --harmony-destructuring
 // Flags: --harmony-rest-parameters --harmony-arrow-functions
 
 
   a2 = 3;
   f(1, 2, 3);
 })();
+
+
+(function TestOtherScopes() {
+  function f1() { return eval("'use strict'; new.target") }
+  assertSame(f1, new f1);
+  function f2() { with ({}) return new.target }
+  assertSame(f2, new f2);
+  function f3({a}) { return new.target }
+  assertSame(f3, new f3({}));
+  function f4(...a) { return new.target }
+  assertSame(f4, new f4);
+  function f5() { 'use strict'; { let x; return new.target } }
+  assertSame(f5, new f5);
+})();
index ab572b0..601adda 100644 (file)
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 // Flags: --harmony-arrow-functions --allow-natives-syntax
-// Flags: --harmony-spreadcalls
+// Flags: --harmony-spreadcalls --harmony-destructuring
+// Flags: --harmony-rest-parameters --harmony-sloppy
 
 (function TestSuperNamedLoads() {
   function Base() { }
@@ -2122,6 +2123,34 @@ TestKeyedSetterCreatingOwnPropertiesNonConfigurable(42, 43, 44);
 })();
 
 
+(function TestSuperInOtherScopes() {
+  var p = {x: 99};
+  var o0 = {__proto__: p, f() { return eval("'use strict'; super.x") }};
+  assertEquals(p.x, o0.f());
+  var o1 = {__proto__: p, f() { with ({}) return super.x }};
+  assertEquals(p.x, o1.f());
+  var o2 = {__proto__: p, f({a}) { return super.x }};
+  assertEquals(p.x, o2.f({}));
+  var o3 = {__proto__: p, f(...a) { return super.x }};
+  assertEquals(p.x, o3.f());
+  var o4 = {__proto__: p, f() { 'use strict'; { let x; return super.x } }};
+  assertEquals(p.x, o4.f());
+})();
+
+
+(function TestSuperCallInOtherScopes() {
+  class C {constructor() { this.x = 99 }}
+  class D0 extends C {constructor() { eval("'use strict'; super()") }}
+  assertEquals(99, (new D0).x);
+  class D2 extends C {constructor({a}) { super() }}
+  assertEquals(99, (new D2({})).x);
+  class D3 extends C {constructor(...a) { super() }}
+  assertEquals(99, (new D3()).x);
+  class D4 extends C {constructor() { { let x; super() } }}
+  assertEquals(99, (new D4).x);
+})();
+
+
 (function TestSuperCallInEval() {
   'use strict';
   class Base {