Use Scope::function_kind_ to distinguish arrow function scopes
Previously, arrow function scopes had a separate ScopeType. However, Scope::DeserializeScopeChain() erroneously deserialized ARROW_SCOPE ScopeInfos as FUNCTION_SCOPE. This could lead to bugs such as the attached one, where "super" was disallowed where it should have been allowed. This patch utilizes the Scope's FunctionKind to distinguish arrow functions from others. Besides fixing the above bug, this also simplifies code in various places that had to deal with two different ScopeTypes both of which meant "function". BUG=v8:4466 LOG=n Review URL: https://codereview.chromium.org/1386253002 Cr-Commit-Position: refs/heads/master@{#31154}
This commit is contained in:
parent
6c97e54f27
commit
24565b8598
@ -75,8 +75,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
|
||||
context_ = Handle<Context>(context_->previous(), isolate_);
|
||||
}
|
||||
}
|
||||
if (scope_info->scope_type() == FUNCTION_SCOPE ||
|
||||
scope_info->scope_type() == ARROW_SCOPE) {
|
||||
if (scope_info->scope_type() == FUNCTION_SCOPE) {
|
||||
nested_scope_chain_.Add(scope_info);
|
||||
}
|
||||
} else {
|
||||
@ -86,8 +85,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
|
||||
|
||||
// Check whether we are in global, eval or function code.
|
||||
Zone zone;
|
||||
if (scope_info->scope_type() != FUNCTION_SCOPE &&
|
||||
scope_info->scope_type() != ARROW_SCOPE) {
|
||||
if (scope_info->scope_type() != FUNCTION_SCOPE) {
|
||||
// Global or eval code.
|
||||
ParseInfo info(&zone, script);
|
||||
if (scope_info->scope_type() == SCRIPT_SCOPE) {
|
||||
@ -183,7 +181,6 @@ ScopeIterator::ScopeType ScopeIterator::Type() {
|
||||
Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
|
||||
switch (scope_info->scope_type()) {
|
||||
case FUNCTION_SCOPE:
|
||||
case ARROW_SCOPE:
|
||||
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
|
||||
return ScopeTypeLocal;
|
||||
case MODULE_SCOPE:
|
||||
|
@ -757,8 +757,7 @@ enum ScopeType {
|
||||
SCRIPT_SCOPE, // The top-level scope for a script or a top-level eval.
|
||||
CATCH_SCOPE, // The scope introduced by catch.
|
||||
BLOCK_SCOPE, // The scope introduced by a new block.
|
||||
WITH_SCOPE, // The scope introduced by with.
|
||||
ARROW_SCOPE // The top-level scope for an arrow function literal.
|
||||
WITH_SCOPE // The scope introduced by with.
|
||||
};
|
||||
|
||||
// The mips architecture prior to revision 5 has inverted encoding for sNaN.
|
||||
|
@ -1190,7 +1190,7 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info,
|
||||
|
||||
if (shared_info->is_arrow()) {
|
||||
Scope* scope =
|
||||
NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction);
|
||||
NewScope(scope_, FUNCTION_SCOPE, FunctionKind::kArrowFunction);
|
||||
scope->SetLanguageMode(shared_info->language_mode());
|
||||
scope->set_start_position(shared_info->start_position());
|
||||
ExpressionClassifier formals_classifier;
|
||||
|
@ -111,8 +111,7 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction(
|
||||
FunctionState top_state(&function_state_, &scope_, top_scope, kNormalFunction,
|
||||
&top_factory);
|
||||
scope_->SetLanguageMode(language_mode);
|
||||
Scope* function_scope = NewScope(
|
||||
scope_, IsArrowFunction(kind) ? ARROW_SCOPE : FUNCTION_SCOPE, kind);
|
||||
Scope* function_scope = NewScope(scope_, FUNCTION_SCOPE, kind);
|
||||
if (!has_simple_parameters) function_scope->SetHasNonSimpleParameters();
|
||||
PreParserFactory function_factory(NULL);
|
||||
FunctionState function_state(&function_state_, &scope_, function_scope, kind,
|
||||
|
@ -307,16 +307,14 @@ class ParserBase : public Traits {
|
||||
};
|
||||
|
||||
Scope* NewScope(Scope* parent, ScopeType scope_type) {
|
||||
// Must always pass the function kind for FUNCTION_SCOPE and ARROW_SCOPE.
|
||||
// Must always pass the function kind for FUNCTION_SCOPE.
|
||||
DCHECK(scope_type != FUNCTION_SCOPE);
|
||||
DCHECK(scope_type != ARROW_SCOPE);
|
||||
return NewScope(parent, scope_type, kNormalFunction);
|
||||
}
|
||||
|
||||
Scope* NewScope(Scope* parent, ScopeType scope_type, FunctionKind kind) {
|
||||
DCHECK(ast_value_factory());
|
||||
DCHECK(scope_type != MODULE_SCOPE || FLAG_harmony_modules);
|
||||
DCHECK(!IsArrowFunction(kind) || scope_type == ARROW_SCOPE);
|
||||
Scope* result = new (zone())
|
||||
Scope(zone(), parent, scope_type, ast_value_factory(), kind);
|
||||
result->Initialize();
|
||||
@ -2929,7 +2927,7 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
|
||||
parenthesized_formals, CHECK_OK);
|
||||
Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos);
|
||||
Scope* scope =
|
||||
this->NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction);
|
||||
this->NewScope(scope_, FUNCTION_SCOPE, FunctionKind::kArrowFunction);
|
||||
FormalParametersT parameters(scope);
|
||||
if (!arrow_formals_classifier.is_simple_parameter_list()) {
|
||||
scope->SetHasNonSimpleParameters();
|
||||
|
@ -343,7 +343,6 @@ int ScopeInfo::ContextLength() {
|
||||
scope_type() == WITH_SCOPE ||
|
||||
(scope_type() == BLOCK_SCOPE && CallsSloppyEval() &&
|
||||
is_declaration_scope()) ||
|
||||
(scope_type() == ARROW_SCOPE && CallsSloppyEval()) ||
|
||||
(scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) ||
|
||||
scope_type() == MODULE_SCOPE;
|
||||
|
||||
|
@ -844,16 +844,18 @@ void Scope::ReportMessage(int start_position, int end_position,
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char* Header(ScopeType scope_type, bool is_declaration_scope) {
|
||||
static const char* Header(ScopeType scope_type, FunctionKind function_kind,
|
||||
bool is_declaration_scope) {
|
||||
switch (scope_type) {
|
||||
case EVAL_SCOPE: return "eval";
|
||||
case FUNCTION_SCOPE: return "function";
|
||||
// TODO(adamk): Should we print concise method scopes specially?
|
||||
case FUNCTION_SCOPE:
|
||||
return IsArrowFunction(function_kind) ? "arrow" : "function";
|
||||
case MODULE_SCOPE: return "module";
|
||||
case SCRIPT_SCOPE: return "global";
|
||||
case CATCH_SCOPE: return "catch";
|
||||
case BLOCK_SCOPE: return is_declaration_scope ? "varblock" : "block";
|
||||
case WITH_SCOPE: return "with";
|
||||
case ARROW_SCOPE: return "arrow";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
@ -935,7 +937,7 @@ void Scope::Print(int n) {
|
||||
int n1 = n0 + 2; // indentation
|
||||
|
||||
// Print header.
|
||||
Indent(n0, Header(scope_type_, is_declaration_scope()));
|
||||
Indent(n0, Header(scope_type_, function_kind_, is_declaration_scope()));
|
||||
if (!scope_name_->IsEmpty()) {
|
||||
PrintF(" ");
|
||||
PrintName(scope_name_);
|
||||
|
@ -302,15 +302,15 @@ class Scope: public ZoneObject {
|
||||
|
||||
// Specific scope types.
|
||||
bool is_eval_scope() const { return scope_type_ == EVAL_SCOPE; }
|
||||
bool is_function_scope() const {
|
||||
return scope_type_ == FUNCTION_SCOPE || scope_type_ == ARROW_SCOPE;
|
||||
}
|
||||
bool is_function_scope() const { return scope_type_ == FUNCTION_SCOPE; }
|
||||
bool is_module_scope() const { return scope_type_ == MODULE_SCOPE; }
|
||||
bool is_script_scope() const { return scope_type_ == SCRIPT_SCOPE; }
|
||||
bool is_catch_scope() const { return scope_type_ == CATCH_SCOPE; }
|
||||
bool is_block_scope() const { return scope_type_ == BLOCK_SCOPE; }
|
||||
bool is_with_scope() const { return scope_type_ == WITH_SCOPE; }
|
||||
bool is_arrow_scope() const { return scope_type_ == ARROW_SCOPE; }
|
||||
bool is_arrow_scope() const {
|
||||
return is_function_scope() && IsArrowFunction(function_kind_);
|
||||
}
|
||||
bool is_declaration_scope() const { return is_declaration_scope_; }
|
||||
|
||||
void set_is_declaration_scope() { is_declaration_scope_ = true; }
|
||||
|
@ -1211,9 +1211,9 @@ TEST(ScopePositions) {
|
||||
" }", "\n"
|
||||
" more;", i::FUNCTION_SCOPE, i::SLOPPY },
|
||||
{ " start;\n", "(a,b) => a + b", "; more;",
|
||||
i::ARROW_SCOPE, i::SLOPPY },
|
||||
i::FUNCTION_SCOPE, i::SLOPPY },
|
||||
{ " start;\n", "(a,b) => { return a+b; }", "\nmore;",
|
||||
i::ARROW_SCOPE, i::SLOPPY },
|
||||
i::FUNCTION_SCOPE, i::SLOPPY },
|
||||
{ " start;\n"
|
||||
" (function fun", "(a,b) { infunction; }", ")();",
|
||||
i::FUNCTION_SCOPE, i::SLOPPY },
|
||||
|
26
test/mjsunit/es6/regress/regress-4466.js
Normal file
26
test/mjsunit/es6/regress/regress-4466.js
Normal file
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
"use strict";
|
||||
|
||||
class Parent {
|
||||
parentMethod(x, y) {
|
||||
assertEquals(42, x);
|
||||
assertEquals('hello world', y);
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
method(x) {
|
||||
let outer = (y) => {
|
||||
let inner = () => {
|
||||
super.parentMethod(x, y);
|
||||
};
|
||||
inner();
|
||||
};
|
||||
outer('hello world');
|
||||
}
|
||||
}
|
||||
|
||||
new Child().method(42);
|
Loading…
Reference in New Issue
Block a user