Commit 0e7f095c authored by littledan's avatar littledan Committed by Commit bot

Restrict FunctionDeclarations in Statement position

ES2015 generally bans FunctionDeclarations in positions which expect a Statement,
as opposed to a StatementListItem, such as a FunctionDeclaration which constitutes
the body of a for loop. However, Annex B 3.2 and 3.4 make exceptions for labeled
function declarations and function declarations as the body of an if statement in
sloppy mode, in the latter case specifying that the semantics are as if the
function declaration occurred in a block. Chrome has historically permitted
further extensions, for the body of any flow control construct.

This patch addresses both the syntactic and semantic mismatches between V8 and
the spec. For the semantic mismatch, function declarations as the body of if
statements change from unconditionally hoisting in certain cases to acquiring
the sloppy mode function in block semantics (based on Annex B 3.3). For the
extra syntax permitted, this patch adds a flag,
--harmony-restrictive-declarations, which excludes disallowed function declaration
cases. A new UseCounter, LegacyFunctionDeclaration, is added to count how often
function declarations occur as the body of other constructs in sloppy mode. With
this patch, the code generally follows the form of the specification with respect
to parsing FunctionDeclarations, rather than allowing them in arbitrary Statement
positions, and makes it more clear where our extensions occur.

BUG=v8:4647
R=adamk
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#34470}
parent 045fa997
......@@ -5485,6 +5485,7 @@ class V8_EXPORT Isolate {
kArrayPrototypeConstructorModified = 26,
kArrayInstanceProtoModified = 27,
kArrayInstanceConstructorModified = 28,
kLegacyFunctionDeclaration = 29,
// If you add new values here, you'll also need to update V8Initializer.cpp
// in Chromium.
......
......@@ -2303,6 +2303,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(promise_extra)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_instanceof)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_declarations)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) {
......@@ -2956,6 +2957,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_regexp_subclass_natives[] = {nullptr};
static const char* harmony_regexp_lookbehind_natives[] = {nullptr};
static const char* harmony_instanceof_natives[] = {nullptr};
static const char* harmony_restrictive_declarations_natives[] = {nullptr};
static const char* harmony_regexp_property_natives[] = {nullptr};
static const char* harmony_function_name_natives[] = {nullptr};
static const char* harmony_function_sent_natives[] = {nullptr};
......
......@@ -204,15 +204,17 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
DEFINE_IMPLICATION(es_staging, harmony_tailcalls)
// Features that are still work in progress (behind individual flags).
#define HARMONY_INPROGRESS(V) \
V(harmony_object_observe, "harmony Object.observe") \
V(harmony_modules, "harmony modules") \
V(harmony_function_sent, "harmony function.sent") \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_tailcalls, "harmony tail calls") \
V(harmony_regexp_property, "harmony unicode regexp property classes")
#define HARMONY_INPROGRESS(V) \
V(harmony_object_observe, "harmony Object.observe") \
V(harmony_modules, "harmony modules") \
V(harmony_function_sent, "harmony function.sent") \
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_tailcalls, "harmony tail calls") \
V(harmony_regexp_property, "harmony unicode regexp property classes") \
V(harmony_restrictive_declarations, \
"harmony limitations on sloppy mode function declarations")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
......
......@@ -423,6 +423,9 @@ class CallSite {
T(ParenthesisInArgString, "Function arg string contains parenthesis") \
T(RuntimeWrongNumArgs, "Runtime function given wrong number of arguments") \
T(SingleFunctionLiteral, "Single function literal required") \
T(SloppyFunction, \
"In non-strict mode code, functions can only be declared at top level, " \
"inside a block, or as the body of an if statement.") \
T(SloppyLexical, \
"Block-scoped declarations (let, const, function, class) not yet " \
"supported outside strict mode") \
......
......@@ -114,6 +114,7 @@ class ParserBase : public Traits {
allow_harmony_default_parameters_(false),
allow_harmony_destructuring_bind_(false),
allow_harmony_destructuring_assignment_(false),
allow_harmony_restrictive_declarations_(false),
allow_strong_mode_(false),
allow_legacy_const_(true),
allow_harmony_do_expressions_(false),
......@@ -132,6 +133,7 @@ class ParserBase : public Traits {
ALLOW_ACCESSORS(harmony_default_parameters);
ALLOW_ACCESSORS(harmony_destructuring_bind);
ALLOW_ACCESSORS(harmony_destructuring_assignment);
ALLOW_ACCESSORS(harmony_restrictive_declarations);
ALLOW_ACCESSORS(strong_mode);
ALLOW_ACCESSORS(legacy_const);
ALLOW_ACCESSORS(harmony_do_expressions);
......@@ -951,6 +953,7 @@ class ParserBase : public Traits {
bool allow_harmony_default_parameters_;
bool allow_harmony_destructuring_bind_;
bool allow_harmony_destructuring_assignment_;
bool allow_harmony_restrictive_declarations_;
bool allow_strong_mode_;
bool allow_legacy_const_;
bool allow_harmony_do_expressions_;
......
......@@ -774,6 +774,8 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
set_allow_harmony_function_name(FLAG_harmony_function_name);
set_allow_harmony_function_sent(FLAG_harmony_function_sent);
set_allow_harmony_restrictive_declarations(
FLAG_harmony_restrictive_declarations);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -1839,26 +1841,18 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
case Token::SWITCH:
return ParseSwitchStatement(labels, ok);
case Token::FUNCTION: {
// FunctionDeclaration is only allowed in the context of SourceElements
// (Ecma 262 5th Edition, clause 14):
// SourceElement:
// Statement
// FunctionDeclaration
// Common language extension is to allow function declaration in place
// of any statement. This language extension is disabled in strict mode.
//
// In Harmony mode, this case also handles the extension:
// Statement:
// GeneratorDeclaration
if (is_strict(language_mode())) {
ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kStrictFunction);
*ok = false;
return NULL;
}
return ParseFunctionDeclaration(NULL, ok);
}
case Token::FUNCTION:
// FunctionDeclaration only allowed as a StatementListItem, not in
// an arbitrary Statement position. Exceptions such as
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// are handled by calling ParseScopedStatement rather than
// ParseSubStatement directly.
ReportMessageAt(scanner()->peek_location(),
is_strict(language_mode())
? MessageTemplate::kStrictFunction
: MessageTemplate::kSloppyFunction);
*ok = false;
return nullptr;
case Token::DEBUGGER:
return ParseDebuggerStatement(ok);
......@@ -2581,6 +2575,10 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
// during the scope processing.
scope_->RemoveUnresolved(var);
Expect(Token::COLON, CHECK_OK);
// ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
return ParseFunctionDeclaration(labels, ok);
}
return ParseStatement(labels, ok);
}
......@@ -2621,11 +2619,11 @@ IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels,
Expect(Token::LPAREN, CHECK_OK);
Expression* condition = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Statement* then_statement = ParseSubStatement(labels, CHECK_OK);
Statement* then_statement = ParseScopedStatement(labels, false, CHECK_OK);
Statement* else_statement = NULL;
if (peek() == Token::ELSE) {
Next();
else_statement = ParseSubStatement(labels, CHECK_OK);
else_statement = ParseScopedStatement(labels, false, CHECK_OK);
} else {
else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
}
......@@ -2824,25 +2822,10 @@ Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels,
scope_->DeclarationScope()->RecordWithStatement();
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
Block* body;
Statement* body;
{ BlockState block_state(&scope_, with_scope);
with_scope->set_start_position(scanner()->peek_location().beg_pos);
// The body of the with statement must be enclosed in an additional
// lexical scope in case the body is a FunctionDeclaration.
body = factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition);
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
block_scope->set_start_position(scanner()->location().beg_pos);
{
BlockState block_state(&scope_, block_scope);
Target target(&this->target_stack_, body);
Statement* stmt = ParseSubStatement(labels, CHECK_OK);
body->statements()->Add(stmt, zone());
block_scope->set_end_position(scanner()->location().end_pos);
block_scope = block_scope->FinalizeBlockScope();
body->set_scope(block_scope);
}
body = ParseScopedStatement(labels, true, CHECK_OK);
with_scope->set_end_position(scanner()->location().end_pos);
}
return factory()->NewWithStatement(with_scope, expr, body, pos);
......@@ -3183,7 +3166,7 @@ DoWhileStatement* Parser::ParseDoWhileStatement(
Target target(&this->target_stack_, loop);
Expect(Token::DO, CHECK_OK);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
......@@ -3213,7 +3196,7 @@ WhileStatement* Parser::ParseWhileStatement(
Expect(Token::LPAREN, CHECK_OK);
Expression* cond = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
if (loop != NULL) loop->Initialize(cond, body);
return loop;
......@@ -3595,6 +3578,28 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
return outer_block;
}
Statement* Parser::ParseScopedStatement(ZoneList<const AstRawString*>* labels,
bool legacy, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
(legacy && allow_harmony_restrictive_declarations())) {
return ParseSubStatement(labels, ok);
} else {
if (legacy) {
++use_counts_[v8::Isolate::kLegacyFunctionDeclaration];
}
// Make a block around the statement for a lexical binding
// is introduced by a FunctionDeclaration.
Scope* body_scope = NewScope(scope_, BLOCK_SCOPE);
BlockState block_state(&scope_, body_scope);
Block* block = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
Statement* body = ParseFunctionDeclaration(NULL, CHECK_OK);
block->statements()->Add(body, zone());
body_scope->set_end_position(scanner()->location().end_pos);
body_scope = body_scope->FinalizeBlockScope();
block->set_scope(body_scope);
return block;
}
}
Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
bool* ok) {
......@@ -3708,7 +3713,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
{
BlockState block_state(&scope_, body_scope);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
auto each_initialization_block =
factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition);
......@@ -3825,25 +3830,11 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
Expect(Token::RPAREN, CHECK_OK);
// Make a block around the statement in case a lexical binding
// is introduced, e.g. by a FunctionDeclaration.
// This block must not use for_scope as its scope because if a
// lexical binding is introduced which overlaps with the for-in/of,
// expressions in head of the loop should actually have variables
// resolved in the outer scope.
Scope* body_scope = NewScope(for_scope, BLOCK_SCOPE);
{
BlockState block_state(&scope_, body_scope);
Block* block =
factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
block->statements()->Add(body, zone());
InitializeForEachStatement(loop, expression, enumerable, block,
is_destructuring);
body_scope->set_end_position(scanner()->location().end_pos);
body_scope = body_scope->FinalizeBlockScope();
block->set_scope(body_scope);
}
// For legacy compat reasons, give for loops similar treatment to
// if statements in allowing a function declaration for a body
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
InitializeForEachStatement(loop, expression, enumerable, body,
is_destructuring);
Statement* final_loop = loop->IsForOfStatement()
? FinalizeForOfStatement(
......@@ -3900,7 +3891,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
}
Expect(Token::RPAREN, CHECK_OK);
body = ParseSubStatement(NULL, CHECK_OK);
body = ParseScopedStatement(NULL, true, CHECK_OK);
}
Statement* result = NULL;
......
......@@ -919,6 +919,14 @@ class Parser : public ParserBase<ParserTraits> {
class CollectExpressionsInTailPositionToListScope;
TryStatement* ParseTryStatement(bool* ok);
DebuggerStatement* ParseDebuggerStatement(bool* ok);
// Parse a SubStatement in strict mode, or with an extra block scope in
// sloppy mode to handle
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// The legacy parameter indicates whether function declarations are
// banned by the ES2015 specification in this location, and they are being
// permitted here to match previous V8 behavior.
Statement* ParseScopedStatement(ZoneList<const AstRawString*>* labels,
bool legacy, bool* ok);
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
......
......@@ -323,6 +323,14 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
return ParseSubStatement(ok);
}
PreParser::Statement PreParser::ParseScopedStatement(bool legacy, bool* ok) {
if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
(legacy && allow_harmony_restrictive_declarations())) {
return ParseSubStatement(ok);
} else {
return ParseFunctionDeclaration(CHECK_OK);
}
}
PreParser::Statement PreParser::ParseSubStatement(bool* ok) {
// Statement ::
......@@ -397,20 +405,18 @@ PreParser::Statement PreParser::ParseSubStatement(bool* ok) {
case Token::TRY:
return ParseTryStatement(ok);
case Token::FUNCTION: {
Scanner::Location start_location = scanner()->peek_location();
Statement statement = ParseFunctionDeclaration(CHECK_OK);
Scanner::Location end_location = scanner()->location();
if (is_strict(language_mode())) {
PreParserTraits::ReportMessageAt(start_location.beg_pos,
end_location.end_pos,
MessageTemplate::kStrictFunction);
*ok = false;
return Statement::Default();
} else {
return statement;
}
}
case Token::FUNCTION:
// FunctionDeclaration only allowed as a StatementListItem, not in
// an arbitrary Statement position. Exceptions such as
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// are handled by calling ParseScopedStatement rather than
// ParseSubStatement directly.
ReportMessageAt(scanner()->peek_location(),
is_strict(language_mode())
? MessageTemplate::kStrictFunction
: MessageTemplate::kSloppyFunction);
*ok = false;
return Statement::Default();
case Token::DEBUGGER:
return ParseDebuggerStatement(ok);
......@@ -698,6 +704,10 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
DCHECK(is_sloppy(language_mode()) ||
!IsFutureStrictReserved(expr.AsIdentifier()));
Consume(Token::COLON);
// ES#sec-labelled-function-declarations Labelled Function Declarations
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
return ParseFunctionDeclaration(ok);
}
Statement statement = ParseStatement(ok);
return statement.IsJumpStatement() ? Statement::Default() : statement;
// Preparsing is disabled for extensions (because the extension details
......@@ -726,10 +736,10 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Statement stat = ParseSubStatement(CHECK_OK);
Statement stat = ParseScopedStatement(false, CHECK_OK);
if (peek() == Token::ELSE) {
Next();
Statement else_stat = ParseSubStatement(CHECK_OK);
Statement else_stat = ParseScopedStatement(false, CHECK_OK);
stat = (stat.IsJumpStatement() && else_stat.IsJumpStatement()) ?
Statement::Jump() : Statement::Default();
} else {
......@@ -825,7 +835,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
BlockState block_state(&scope_, with_scope);
ParseSubStatement(CHECK_OK);
ParseScopedStatement(true, CHECK_OK);
return Statement::Default();
}
......@@ -875,7 +885,7 @@ PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
// 'do' Statement 'while' '(' Expression ')' ';'
Expect(Token::DO, CHECK_OK);
ParseSubStatement(CHECK_OK);
ParseScopedStatement(true, CHECK_OK);
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
......@@ -893,7 +903,7 @@ PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(ok);
ParseScopedStatement(true, ok);
return Statement::Default();
}
......@@ -945,7 +955,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
}
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
ParseScopedStatement(true, CHECK_OK);
return Statement::Default();
}
} else {
......@@ -983,7 +993,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
}
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
ParseScopedStatement(true, CHECK_OK);
return Statement::Default();
}
}
......@@ -1009,7 +1019,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
}
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(ok);
ParseScopedStatement(true, ok);
return Statement::Default();
}
......
......@@ -1030,6 +1030,7 @@ class PreParser : public ParserBase<PreParserTraits> {
Scanner::BookmarkScope* bookmark = nullptr);
Statement ParseStatement(bool* ok);
Statement ParseSubStatement(bool* ok);
Statement ParseScopedStatement(bool legacy, bool* ok);
Statement ParseFunctionDeclaration(bool* ok);
Statement ParseClassDeclaration(bool* ok);
Statement ParseBlock(bool* ok);
......
......@@ -1511,7 +1511,8 @@ enum ParserFlag {
kAllowHarmonyNewTarget,
kAllowStrongMode,
kNoLegacyConst,
kAllowHarmonyFunctionSent
kAllowHarmonyFunctionSent,
kAllowHarmonyRestrictiveDeclarations,
};
enum ParserSyncTestResult {
......@@ -1537,6 +1538,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
parser->set_allow_legacy_const(!flags.Contains(kNoLegacyConst));
parser->set_allow_harmony_function_sent(
flags.Contains(kAllowHarmonyFunctionSent));
parser->set_allow_harmony_restrictive_declarations(
flags.Contains(kAllowHarmonyRestrictiveDeclarations));
}
......@@ -8013,3 +8016,72 @@ TEST(NewTargetErrors) {
// clang-format on
RunParserSyncTest(context_data, error_data, kError);
}
TEST(FunctionDeclarationError) {
// clang-format off
const char* strict_context[][2] = {
{ "'use strict';", "" },
{ "'use strict'; { ", "}" },
{"(function() { 'use strict';", "})()"},
{"(function() { 'use strict'; {", "} })()"},
{ NULL, NULL }
};
const char* sloppy_context[][2] = {
{ "", "" },
{ "{", "}" },
{"(function() {", "})()"},
{"(function() { {", "} })()"},
{ NULL, NULL }
};
const char* error_data[] = {
"try function foo() {} catch (e) {}",
NULL
};
const char* unrestricted_data[] = {
"do function foo() {} while (0);",
"for (;false;) function foo() {}",
"for (var i = 0; i < 1; i++) function f() { };",
"for (var x in {a: 1}) function f() { };",
"for (var x in {}) function f() { };",
"for (var x in {}) function foo() {}",
"for (x in {a: 1}) function f() { };",
"for (x in {}) function f() { };",
"var x; for (x in {}) function foo() {}",
"with ({}) function f() { };",
NULL
};
const char* sloppy_data[] = {
"if (true) function foo() {}",
"if (false) {} else function f() { };",
"label: function f() { }",
"label: if (true) function f() { }",
"label: if (true) {} else function f() { }",
"if (true) label: function f() {}",
"if (true) {} else label: function f() {}",
NULL
};
// clang-format on
static const ParserFlag restrictive_flags[] = {
kAllowHarmonyRestrictiveDeclarations};
RunParserSyncTest(strict_context, error_data, kError);
RunParserSyncTest(strict_context, error_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(strict_context, unrestricted_data, kError);
RunParserSyncTest(strict_context, unrestricted_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(strict_context, sloppy_data, kError);
RunParserSyncTest(strict_context, sloppy_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(sloppy_context, error_data, kError);
RunParserSyncTest(sloppy_context, error_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(sloppy_context, unrestricted_data, kSuccess);
RunParserSyncTest(sloppy_context, unrestricted_data, kError, NULL, 0,
restrictive_flags, arraysize(restrictive_flags));
RunParserSyncTest(sloppy_context, sloppy_data, kSuccess);
RunParserSyncTest(sloppy_context, sloppy_data, kSuccess, restrictive_flags,
arraysize(restrictive_flags));
}
// 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-declarations
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// Annex B 3.4 FunctionDeclarations in IfStatement Statement Clauses
// In sloppy mode, function declarations in if statements act like
// they have a block around them. Prohibited in strict mode.
(function() {
assertEquals(undefined, f);
if (false) function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (true) function f() { };
assertEquals('function', typeof f);
})();
(function() {
assertEquals(undefined, f);
if (true) {} else function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (false) {} else function f() { };
assertEquals('function', typeof f);
})();
// For legacy reasons, we also support these types of semantics as
// the body of a for or with statement.
(function() {
for (;false;) function f() { };
assertEquals(undefined, f);
})();
(function() {
for (var x in {}) function f() { };
assertEquals(undefined, f);
})();
(function() {
var x;
for (x in {}) function f() { };
assertEquals(undefined, f);
})();
(function() {
for (var i = 0; i < 1; i++) function f() { };
assertEquals('function', typeof f);
})();
(function() {
for (var x in {a: 1}) function f() { };
assertEquals('function', typeof f);
})();
(function() {
var x;
for (x in {a: 1}) function f() { };
assertEquals('function', typeof f);
})();
(function() {
with ({}) function f() { };
assertEquals('function', typeof f);
})();
(function() {
do function f() {} while (0);
assertEquals('function', typeof f);
})();
// Labeled function declarations undergo the same hoisting/FiB semantics as if
// they were unalbeled.
(function() {
function bar() {
return f;
x: function f() {}
}
assertEquals('function', typeof bar());
})();
(function() {
function bar() {
return f;
{
x: function f() {}
}
}
assertEquals(undefined, bar());
})();
// 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-declarations
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
// Annex B 3.4 FunctionDeclarations in IfStatement Statement Clauses
// In sloppy mode, function declarations in if statements act like
// they have a block around them. Prohibited in strict mode.
(function() {
if (false) function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (true) function f() { };
assertEquals('function', typeof f);
})();
(function() {
assertEquals(undefined, f);
if (true) {} else function f() { };
assertEquals(undefined, f);
})();
(function() {
assertEquals(undefined, f);
if (false) {} else function f() { };
assertEquals('function', typeof f);
})();
// Labeled function declarations undergo the same hoisting/FiB semantics as if
// they were unalbeled.
(function() {
function bar() {
return f;
x: function f() {}
}
assertEquals('function', typeof bar());
})();
(function() {
function bar() {
return f;
{
x: function f() {}
}
}
assertEquals(undefined, bar());
})();
......@@ -885,6 +885,9 @@
'regress/regress-crbug-42414': [SKIP],
'regress/regress-1853': [SKIP],
'regress/regress-crbug-424142': [SKIP],
# with statements no longer always have a block as their body
'regress/regress-95485': [SKIP],
}], # ignition == True
['ignition == True and arch == arm64', {
......
......@@ -672,9 +672,6 @@
# We do not correctly handle assignments within "with"
'ecma_3/Statements/12.10-01': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=4647
'ecma_3/FunExpr/fe-001': [FAIL_OK],
##################### MOZILLA EXTENSION TESTS #####################
'ecma/extensions/15.1.2.1-1': [FAIL_OK],
......
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