Improve strictness of Annex B 3.3 for generators and async functions

Annex B 3.3 applies only for ordinary FunctionDeclarations, not
GeneratorDeclarations or AsyncFunctionDeclarations. This patch
- Skips applying Annex B 3.3 to async functions
- Adds a flag to refrain from applying it to generators
- UseCounter for how often duplicate function in block occurs
  with generators (unclear how to measure need for hoisting from block)

BUG=v8:4806

Review-Url: https://codereview.chromium.org/1995863002
Cr-Commit-Position: refs/heads/master@{#36557}
This commit is contained in:
littledan 2016-05-27 11:22:29 -07:00 committed by Commit bot
parent dc28c14606
commit 6390282f96
8 changed files with 209 additions and 3 deletions

View File

@ -2529,6 +2529,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_string_padding)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(icu_case_mapping)
#endif
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_async_await)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_generators)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) {
@ -3122,6 +3123,7 @@ bool Genesis::InstallExperimentalNatives() {
#endif
static const char* harmony_async_await_natives[] = {
"native harmony-async-await.js", nullptr};
static const char* harmony_restrictive_generators_natives[] = {nullptr};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {

View File

@ -202,6 +202,8 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_restrictive_generators, \
"harmony restrictions on generator declarations") \
V(harmony_regexp_property, "harmony unicode regexp property classes") \
V(harmony_async_await, "harmony async-await")

View File

@ -198,7 +198,8 @@ class ParserBase : public Traits {
allow_harmony_for_in_(false),
allow_harmony_function_name_(false),
allow_harmony_function_sent_(false),
allow_harmony_async_await_(false) {}
allow_harmony_async_await_(false),
allow_harmony_restrictive_generators_(false) {}
#define ALLOW_ACCESSORS(name) \
bool allow_##name() const { return allow_##name##_; } \
@ -219,6 +220,7 @@ class ParserBase : public Traits {
ALLOW_ACCESSORS(harmony_function_name);
ALLOW_ACCESSORS(harmony_function_sent);
ALLOW_ACCESSORS(harmony_async_await);
ALLOW_ACCESSORS(harmony_restrictive_generators);
SCANNER_ACCESSORS(harmony_exponentiation_operator);
#undef SCANNER_ACCESSORS
@ -1176,6 +1178,7 @@ class ParserBase : public Traits {
bool allow_harmony_function_name_;
bool allow_harmony_function_sent_;
bool allow_harmony_async_await_;
bool allow_harmony_restrictive_generators_;
};
template <class Traits>

View File

@ -803,6 +803,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_exponentiation_operator(
FLAG_harmony_exponentiation_operator);
set_allow_harmony_async_await(FLAG_harmony_async_await);
set_allow_harmony_restrictive_generators(FLAG_harmony_restrictive_generators);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
@ -1971,10 +1972,24 @@ Variable* Parser::Declare(Declaration* declaration,
} else if (IsLexicalVariableMode(mode) ||
IsLexicalVariableMode(var->mode())) {
// Allow duplicate function decls for web compat, see bug 4693.
bool duplicate_allowed = false;
if (is_sloppy(language_mode()) && is_function_declaration &&
var->is_function()) {
DCHECK(IsLexicalVariableMode(mode) &&
IsLexicalVariableMode(var->mode()));
// If the duplication is allowed, then the var will show up
// in the SloppyBlockFunctionMap and the new FunctionKind
// will be a permitted duplicate.
FunctionKind function_kind =
declaration->AsFunctionDeclaration()->fun()->kind();
duplicate_allowed =
scope->DeclarationScope()->sloppy_block_function_map()->Lookup(
const_cast<AstRawString*>(name), name->hash()) != nullptr &&
!IsAsyncFunction(function_kind) &&
!(allow_harmony_restrictive_generators() &&
IsGeneratorFunction(function_kind));
}
if (duplicate_allowed) {
++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition];
} else {
// The name was declared in this scope before; check for conflicting
@ -2188,7 +2203,13 @@ Statement* Parser::ParseHoistableDeclaration(
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
if (names) names->Add(name, zone());
EmptyStatement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
if (is_sloppy(language_mode()) && !scope_->is_declaration_scope()) {
// Async functions don't undergo sloppy mode block scoped hoisting, and don't
// allow duplicates in a block. Both are represented by the
// sloppy_block_function_map. Don't add them to the map for async functions.
// Generators are also supposed to be prohibited; currently doing this behind
// a flag and UseCounting violations to assess web compatibility.
if (is_sloppy(language_mode()) && !scope_->is_declaration_scope() &&
!is_async && !(allow_harmony_restrictive_generators() && is_generator)) {
SloppyBlockFunctionStatement* delegate =
factory()->NewSloppyBlockFunctionStatement(empty, scope_);
scope_->DeclarationScope()->sloppy_block_function_map()->Declare(name,

View File

@ -1509,7 +1509,8 @@ enum ParserFlag {
kAllowHarmonyRestrictiveDeclarations,
kAllowHarmonyExponentiationOperator,
kAllowHarmonyForIn,
kAllowHarmonyAsyncAwait
kAllowHarmonyAsyncAwait,
kAllowHarmonyRestrictiveGenerators,
};
enum ParserSyncTestResult {
@ -1532,6 +1533,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
parser->set_allow_harmony_for_in(flags.Contains(kAllowHarmonyForIn));
parser->set_allow_harmony_async_await(
flags.Contains(kAllowHarmonyAsyncAwait));
parser->set_allow_harmony_restrictive_generators(
flags.Contains(kAllowHarmonyRestrictiveGenerators));
}
@ -7731,3 +7734,60 @@ TEST(RestrictiveForInErrors) {
RunParserSyncTest(context_data, error_data, kError, nullptr, 0, always_flags,
arraysize(always_flags));
}
TEST(NoDuplicateGeneratorsInBlock) {
const char* block_context_data[][2] = {
{"'use strict'; {", "}"},
{"{", "}"},
{"(function() { {", "} })()"},
{"(function() {'use strict'; {", "} })()"},
{NULL, NULL}};
const char* top_level_context_data[][2] = {
{"'use strict';", ""},
{"", ""},
{"(function() {", "})()"},
{"(function() {'use strict';", "})()"},
{NULL, NULL}};
const char* error_data[] = {"function* x() {} function* x() {}",
"function x() {} function* x() {}",
"function* x() {} function x() {}", NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyRestrictiveGenerators};
// The preparser doesn't enforce the restriction, so turn it off.
bool test_preparser = false;
RunParserSyncTest(block_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags), NULL, 0, false,
test_preparser);
RunParserSyncTest(top_level_context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(NoDuplicateAsyncFunctionInBlock) {
const char* block_context_data[][2] = {
{"'use strict'; {", "}"},
{"{", "}"},
{"(function() { {", "} })()"},
{"(function() {'use strict'; {", "} })()"},
{NULL, NULL}};
const char* top_level_context_data[][2] = {
{"'use strict';", ""},
{"", ""},
{"(function() {", "})()"},
{"(function() {'use strict';", "})()"},
{NULL, NULL}};
const char* error_data[] = {"async function x() {} async function x() {}",
"function x() {} async function x() {}",
"async function x() {} function x() {}",
"function* x() {} async function x() {}",
"function* x() {} async function x() {}",
"async function x() {} function* x() {}",
"function* x() {} async function x() {}",
NULL};
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncAwait};
// The preparser doesn't enforce the restriction, so turn it off.
bool test_preparser = false;
RunParserSyncTest(block_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags), NULL, 0, false,
test_preparser);
RunParserSyncTest(top_level_context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}

View File

@ -0,0 +1,60 @@
// Copyright 2016 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: --no-harmony-restrictive-generators
// In legacy mode, generators get sloppy-mode block-scoped function hoisting
// Hoisting to the global scope
{
function* foo() {}
assertEquals('function', typeof foo);
}
//assertEquals('function', typeof foo);
// Hoisting within a function scope
(function() {
{ function* bar() {} }
assertEquals('function', typeof bar);
})();
// Lexical shadowing allowed; hoisting happens
(function() {
function* x() { yield 1; }
{ function* x() { yield 2 } }
assertEquals(2, x().next().value);
})();
// Duplicates allowed
(function() {
function* y() { yield 1; }
function* y() { yield 2 }
assertEquals(2, y().next().value);
})();
// Functions and generators may duplicate each other
(function() {
function* z() { yield 1; }
function z() { return 2 }
assertEquals(2, z());
function a() { return 1; }
function* a() { yield 2 }
assertEquals(2, a().next().value);
})();
// In strict mode, none of this happens
(function() {
'use strict';
{ function* bar() {} }
assertEquals('undefined', typeof bar);
// Lexical shadowing allowed; hoisting happens
function* x() { yield 1; }
{ function* x() { yield 2 } }
assertEquals(1, x().next().value);
})();

View File

@ -0,0 +1,30 @@
// Copyright 2016 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-async-await
// Async functions don't get sloppy-mode block-scoped function hoisting
// No hoisting to the global scope
{
async function foo() {}
assertEquals('function', typeof foo);
}
assertEquals('undefined', typeof foo);
// No hoisting within a function scope
(function() {
{ async function bar() {} }
assertEquals('undefined', typeof bar);
})();
// Lexical shadowing allowed, no hoisting
(function() {
var y;
async function x() { y = 1; }
{ async function x() { y = 2; } }
x();
assertEquals(1, y);
})();

View File

@ -0,0 +1,28 @@
// Copyright 2016 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-restrictive-generators
// Generators don't get sloppy-mode block-scoped function hoisting
// No hoisting to the global scope
{
function* foo() {}
assertEquals('function', typeof foo);
}
assertEquals('undefined', typeof foo);
// No hoisting within a function scope
(function() {
{ function* bar() {} }
assertEquals('undefined', typeof bar);
})();
// Lexical shadowing allowed, no hoisting
(function() {
function* x() { yield 1; }
{ function* x() { yield 2 } }
assertEquals(1, x().next().value);
})();