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:
parent
dc28c14606
commit
6390282f96
@ -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++) {
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
}
|
||||
|
60
test/mjsunit/harmony/sloppy-legacy-duplicate-generators.js
Normal file
60
test/mjsunit/harmony/sloppy-legacy-duplicate-generators.js
Normal 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);
|
||||
})();
|
30
test/mjsunit/harmony/sloppy-no-duplicate-async.js
Normal file
30
test/mjsunit/harmony/sloppy-no-duplicate-async.js
Normal 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);
|
||||
})();
|
28
test/mjsunit/harmony/sloppy-no-duplicate-generators.js
Normal file
28
test/mjsunit/harmony/sloppy-no-duplicate-generators.js
Normal 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);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user