Commit d8bccfe9 authored by conradw's avatar conradw Committed by Commit bot

[strong] Implement static restrictions on switch statement

Implements the strong mode proposal's restrictions on the syntax of the
switch statement. Also fixes a minor bug with empty statements in strong
mode and improves StrongUndefinedArrow parser synch tests.

BUG=v8:3956
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#27885}
parent 71a19439
...@@ -161,6 +161,7 @@ var kMessages = { ...@@ -161,6 +161,7 @@ var kMessages = {
strong_arguments: ["In strong mode, 'arguments' is deprecated, use '...args' instead"], strong_arguments: ["In strong mode, 'arguments' is deprecated, use '...args' instead"],
strong_undefined: ["In strong mode, binding or assigning to 'undefined' is deprecated"], strong_undefined: ["In strong mode, binding or assigning to 'undefined' is deprecated"],
strong_direct_eval: ["In strong mode, direct calls to eval are deprecated"], strong_direct_eval: ["In strong mode, direct calls to eval are deprecated"],
strong_switch_fallthrough : ["In strong mode, switch fall-through is deprecated, terminate each case with 'break', 'continue', 'return' or 'throw'"],
strong_equal: ["In strong mode, '==' and '!=' are deprecated, use '===' and '!==' instead"], strong_equal: ["In strong mode, '==' and '!=' are deprecated, use '===' and '!==' instead"],
strong_delete: ["In strong mode, 'delete' is deprecated, use maps or sets instead"], strong_delete: ["In strong mode, 'delete' is deprecated, use maps or sets instead"],
strong_var: ["In strong mode, 'var' is deprecated, use 'let' or 'const' instead"], strong_var: ["In strong mode, 'var' is deprecated, use 'let' or 'const' instead"],
......
...@@ -1821,36 +1821,29 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels, ...@@ -1821,36 +1821,29 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
return ParseForStatement(labels, ok); return ParseForStatement(labels, ok);
case Token::CONTINUE: case Token::CONTINUE:
return ParseContinueStatement(ok);
case Token::BREAK: case Token::BREAK:
return ParseBreakStatement(labels, ok);
case Token::RETURN: case Token::RETURN:
return ParseReturnStatement(ok);
case Token::WITH:
return ParseWithStatement(labels, ok);
case Token::SWITCH:
return ParseSwitchStatement(labels, ok);
case Token::THROW: case Token::THROW:
return ParseThrowStatement(ok);
case Token::TRY: { case Token::TRY: {
// NOTE: It is somewhat complicated to have labels on // These statements must have their labels preserved in an enclosing
// try-statements. When breaking out of a try-finally statement, // block
// one must take great care not to treat it as a if (labels == NULL) {
// fall-through. It is much easier just to wrap the entire return ParseStatementAsUnlabelled(labels, ok);
// try-statement in a statement block and put the labels there } else {
Block* result = Block* result =
factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition); factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition);
Target target(&this->target_stack_, result); Target target(&this->target_stack_, result);
TryStatement* statement = ParseTryStatement(CHECK_OK); Statement* statement = ParseStatementAsUnlabelled(labels, CHECK_OK);
if (result) result->AddStatement(statement, zone()); if (result) result->AddStatement(statement, zone());
return result; return result;
} }
}
case Token::WITH:
return ParseWithStatement(labels, ok);
case Token::SWITCH:
return ParseSwitchStatement(labels, ok);
case Token::FUNCTION: { case Token::FUNCTION: {
// FunctionDeclaration is only allowed in the context of SourceElements // FunctionDeclaration is only allowed in the context of SourceElements
...@@ -1892,6 +1885,30 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels, ...@@ -1892,6 +1885,30 @@ Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
} }
} }
Statement* Parser::ParseStatementAsUnlabelled(
ZoneList<const AstRawString*>* labels, bool* ok) {
switch (peek()) {
case Token::CONTINUE:
return ParseContinueStatement(ok);
case Token::BREAK:
return ParseBreakStatement(labels, ok);
case Token::RETURN:
return ParseReturnStatement(ok);
case Token::THROW:
return ParseThrowStatement(ok);
case Token::TRY:
return ParseTryStatement(ok);
default:
UNREACHABLE();
return NULL;
}
}
VariableProxy* Parser::NewUnresolved(const AstRawString* name, VariableProxy* Parser::NewUnresolved(const AstRawString* name,
VariableMode mode) { VariableMode mode) {
...@@ -2847,13 +2864,19 @@ CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) { ...@@ -2847,13 +2864,19 @@ CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) {
int pos = position(); int pos = position();
ZoneList<Statement*>* statements = ZoneList<Statement*>* statements =
new(zone()) ZoneList<Statement*>(5, zone()); new(zone()) ZoneList<Statement*>(5, zone());
Statement* stat = NULL;
while (peek() != Token::CASE && while (peek() != Token::CASE &&
peek() != Token::DEFAULT && peek() != Token::DEFAULT &&
peek() != Token::RBRACE) { peek() != Token::RBRACE) {
Statement* stat = ParseStatementListItem(CHECK_OK); stat = ParseStatementListItem(CHECK_OK);
statements->Add(stat, zone()); statements->Add(stat, zone());
} }
if (is_strong(language_mode()) && stat != NULL && !stat->IsJump() &&
peek() != Token::RBRACE) {
ReportMessageAt(scanner()->location(), "strong_switch_fallthrough");
*ok = false;
return NULL;
}
return factory()->NewCaseClause(label, statements, pos); return factory()->NewCaseClause(label, statements, pos);
} }
...@@ -4470,7 +4493,6 @@ void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) { ...@@ -4470,7 +4493,6 @@ void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Parser support // Parser support
bool Parser::TargetStackContainsLabel(const AstRawString* label) { bool Parser::TargetStackContainsLabel(const AstRawString* label) {
for (Target* t = target_stack_; t != NULL; t = t->previous()) { for (Target* t = target_stack_; t != NULL; t = t->previous()) {
if (ContainsLabel(t->statement()->labels(), label)) return true; if (ContainsLabel(t->statement()->labels(), label)) return true;
......
...@@ -924,6 +924,8 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -924,6 +924,8 @@ class Parser : public ParserBase<ParserTraits> {
ZoneList<ImportDeclaration*>* ParseNamedImports(int pos, bool* ok); ZoneList<ImportDeclaration*>* ParseNamedImports(int pos, bool* ok);
Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok); Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok);
Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels, bool* ok); Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels, bool* ok);
Statement* ParseStatementAsUnlabelled(ZoneList<const AstRawString*>* labels,
bool* ok);
Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names, Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
bool* ok); bool* ok);
Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names, Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names,
......
...@@ -235,6 +235,11 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { ...@@ -235,6 +235,11 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
// Statement :: // Statement ::
// EmptyStatement // EmptyStatement
// ... // ...
if (peek() == Token::SEMICOLON) {
Next();
return Statement::Default();
}
return ParseSubStatement(ok); return ParseSubStatement(ok);
} }
...@@ -395,15 +400,16 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) { ...@@ -395,15 +400,16 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) {
// (ECMA-262, 3rd, 12.2) // (ECMA-262, 3rd, 12.2)
// //
Expect(Token::LBRACE, CHECK_OK); Expect(Token::LBRACE, CHECK_OK);
Statement final = Statement::Default();
while (peek() != Token::RBRACE) { while (peek() != Token::RBRACE) {
if (is_strict(language_mode())) { if (is_strict(language_mode())) {
ParseStatementListItem(CHECK_OK); final = ParseStatementListItem(CHECK_OK);
} else { } else {
ParseStatement(CHECK_OK); final = ParseStatement(CHECK_OK);
} }
} }
Expect(Token::RBRACE, ok); Expect(Token::RBRACE, ok);
return Statement::Default(); return final;
} }
...@@ -545,7 +551,8 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { ...@@ -545,7 +551,8 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
DCHECK(is_sloppy(language_mode()) || DCHECK(is_sloppy(language_mode()) ||
!IsFutureStrictReserved(expr.AsIdentifier())); !IsFutureStrictReserved(expr.AsIdentifier()));
Consume(Token::COLON); Consume(Token::COLON);
return ParseStatement(ok); Statement statement = ParseStatement(ok);
return statement.IsJumpStatement() ? Statement::Default() : statement;
// Preparsing is disabled for extensions (because the extension details // Preparsing is disabled for extensions (because the extension details
// aren't passed to lazily compiled functions), so we don't // aren't passed to lazily compiled functions), so we don't
// accept "native function" in the preparser. // accept "native function" in the preparser.
...@@ -571,12 +578,16 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) { ...@@ -571,12 +578,16 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK); Expect(Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK); Statement stat = ParseSubStatement(CHECK_OK);
if (peek() == Token::ELSE) { if (peek() == Token::ELSE) {
Next(); Next();
ParseSubStatement(CHECK_OK); Statement else_stat = ParseSubStatement(CHECK_OK);
stat = (stat.IsJumpStatement() && else_stat.IsJumpStatement()) ?
Statement::Jump() : Statement::Default();
} else {
stat = Statement::Default();
} }
return Statement::Default(); return stat;
} }
...@@ -594,7 +605,7 @@ PreParser::Statement PreParser::ParseContinueStatement(bool* ok) { ...@@ -594,7 +605,7 @@ PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
} }
ExpectSemicolon(CHECK_OK); ExpectSemicolon(CHECK_OK);
return Statement::Default(); return Statement::Jump();
} }
...@@ -612,7 +623,7 @@ PreParser::Statement PreParser::ParseBreakStatement(bool* ok) { ...@@ -612,7 +623,7 @@ PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
} }
ExpectSemicolon(CHECK_OK); ExpectSemicolon(CHECK_OK);
return Statement::Default(); return Statement::Jump();
} }
...@@ -647,7 +658,7 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) { ...@@ -647,7 +658,7 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
} }
ExpectSemicolon(CHECK_OK); ExpectSemicolon(CHECK_OK);
return Statement::Default(); return Statement::Jump();
} }
...@@ -691,12 +702,19 @@ PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) { ...@@ -691,12 +702,19 @@ PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) {
} }
Expect(Token::COLON, CHECK_OK); Expect(Token::COLON, CHECK_OK);
token = peek(); token = peek();
Statement statement = Statement::Jump();
while (token != Token::CASE && while (token != Token::CASE &&
token != Token::DEFAULT && token != Token::DEFAULT &&
token != Token::RBRACE) { token != Token::RBRACE) {
ParseStatementListItem(CHECK_OK); statement = ParseStatementListItem(CHECK_OK);
token = peek(); token = peek();
} }
if (is_strong(language_mode()) && !statement.IsJumpStatement() &&
token != Token::RBRACE) {
ReportMessageAt(scanner()->location(), "strong_switch_fallthrough");
*ok = false;
return Statement::Default();
}
} }
Expect(Token::RBRACE, ok); Expect(Token::RBRACE, ok);
return Statement::Default(); return Statement::Default();
...@@ -827,7 +845,7 @@ PreParser::Statement PreParser::ParseThrowStatement(bool* ok) { ...@@ -827,7 +845,7 @@ PreParser::Statement PreParser::ParseThrowStatement(bool* ok) {
} }
ParseExpression(true, CHECK_OK); ParseExpression(true, CHECK_OK);
ExpectSemicolon(ok); ExpectSemicolon(ok);
return Statement::Default(); return Statement::Jump();
} }
......
...@@ -1104,6 +1104,10 @@ class PreParserStatement { ...@@ -1104,6 +1104,10 @@ class PreParserStatement {
return PreParserStatement(kUnknownStatement); return PreParserStatement(kUnknownStatement);
} }
static PreParserStatement Jump() {
return PreParserStatement(kJumpStatement);
}
static PreParserStatement FunctionDeclaration() { static PreParserStatement FunctionDeclaration() {
return PreParserStatement(kFunctionDeclaration); return PreParserStatement(kFunctionDeclaration);
} }
...@@ -1139,9 +1143,14 @@ class PreParserStatement { ...@@ -1139,9 +1143,14 @@ class PreParserStatement {
return code_ == kFunctionDeclaration; return code_ == kFunctionDeclaration;
} }
bool IsJumpStatement() {
return code_ == kJumpStatement;
}
private: private:
enum Type { enum Type {
kUnknownStatement, kUnknownStatement,
kJumpStatement,
kStringLiteralExpressionStatement, kStringLiteralExpressionStatement,
kUseStrictExpressionStatement, kUseStrictExpressionStatement,
kUseStrongExpressionStatement, kUseStrongExpressionStatement,
......
...@@ -5805,7 +5805,7 @@ TEST(StrongEmptySubStatements) { ...@@ -5805,7 +5805,7 @@ TEST(StrongEmptySubStatements) {
const char* strict_context_data[][2] = {{"'use strict';", ""}, {NULL}}; const char* strict_context_data[][2] = {{"'use strict';", ""}, {NULL}};
const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}}; const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}};
const char* data[] = { const char* data_error[] = {
"if (1);", "if (1);",
"if (1) {} else;", "if (1) {} else;",
"while (1);", "while (1);",
...@@ -5818,15 +5818,24 @@ TEST(StrongEmptySubStatements) { ...@@ -5818,15 +5818,24 @@ TEST(StrongEmptySubStatements) {
"for (const x of []);", "for (const x of []);",
NULL}; NULL};
const char* data_success[] = {
"if (1) {} else {}",
"switch(1) {}",
"1+1;;",
"1+1; ;",
NULL};
static const ParserFlag always_flags[] = { static const ParserFlag always_flags[] = {
kAllowStrongMode, kAllowStrongMode,
}; };
RunParserSyncTest(sloppy_context_data, data, kSuccess, NULL, 0, always_flags, RunParserSyncTest(sloppy_context_data, data_error, kSuccess, NULL, 0,
arraysize(always_flags)); always_flags, arraysize(always_flags));
RunParserSyncTest(strict_context_data, data, kSuccess, NULL, 0, always_flags, RunParserSyncTest(strict_context_data, data_error, kSuccess, NULL, 0,
arraysize(always_flags)); always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, data, kError, NULL, 0, always_flags, RunParserSyncTest(strong_context_data, data_error, kError, NULL, 0,
arraysize(always_flags)); always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, data_success, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
} }
...@@ -5975,31 +5984,93 @@ TEST(StrongUndefinedArrow) { ...@@ -5975,31 +5984,93 @@ TEST(StrongUndefinedArrow) {
TEST(StrongDirectEval) { TEST(StrongDirectEval) {
const char* context_data[][2] = {{"", ""}, {NULL}}; const char* sloppy_context_data[][2] = {{"", ""}, {NULL}};
const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}};
const char* error_data[] = { const char* error_data[] = {
"'use strong'; eval();", "eval();",
"'use strong'; eval([]);", "eval([]);",
"'use strong'; (eval)();", "(eval)();",
"'use strong'; (((eval)))();", "(((eval)))();",
"'use strong'; eval('function f() {}');", "eval('function f() {}');",
"'use strong'; function f() {eval()}", "function f() {eval()}",
NULL}; NULL};
const char* success_data[] = { const char* success_data[] = {
"'use strong'; eval;", "eval;",
"'use strong'; eval`foo`;", "eval`foo`;",
"'use strong'; let foo = eval; foo();", "let foo = eval; foo();",
"'use strong'; (1, eval)();", "(1, eval)();",
NULL}; NULL};
static const ParserFlag always_flags[] = { static const ParserFlag always_flags[] = {
kAllowStrongMode kAllowStrongMode
}; };
RunParserSyncTest(context_data, error_data, kError, NULL, 0, RunParserSyncTest(sloppy_context_data, error_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags)); always_flags, arraysize(always_flags));
RunParserSyncTest(context_data, success_data, kSuccess, NULL, 0, RunParserSyncTest(strong_context_data, error_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, success_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(StrongSwitchFallthrough) {
const char* sloppy_context_data[][2] = {
{"function f() { foo:for(;;) { switch(1) {", "};}}"},
{NULL, NULL}
};
const char* strong_context_data[][2] = {
{"function f() { 'use strong'; foo:for(;;) { switch(1) {", "};}}"},
{NULL, NULL}
};
const char* data_success[] = {
"",
"case 1:",
"case 1: case 2:",
"case 1: break;",
"default: throw new TypeError();",
"case 1: case 2: null",
"case 1: case 2: default: 1+1",
"case 1: break; case 2: return; default:",
"case 1: break foo; case 2: return; default:",
"case 1: case 2: break; case 3: continue; case 4: default:",
"case 1: case 2: break; case 3: continue foo; case 4: default:",
"case 1: case 2: {{return;}} case 3: default:",
"case 1: case 2: case 3: default: {1+1;{continue;}}",
"case 1: case 2: {1+1;{1+1;{continue;}}} case 3: default:",
"case 1: if (1) break; else continue; case 2: case 3: default:",
"case 1: case 2: if (1) {{break;}} else break; case 3: default:",
"case 1: if (1) break; else {if (1) break; else break;} case 2: default:",
"case 1: if (1) {if (1) break; else break;} else break; case 2: default:",
NULL};
const char* data_error[] = {
"case 1: case 2: (function(){return}); default:",
"case 1: 1+1; case 2:",
"case 1: bar: break bar; case 2: break;",
"case 1: bar:return; case 2:",
"case 1: bar:{ continue;} case 2:",
"case 1: break; case 2: bar:{ throw new TypeError() } default:",
"case 1: case 2: { bar:{ { break;} } } default: break;",
"case 1: if (1) break; else {}; case 2: default:",
"case 1: case 2: if (1) break; default:",
"case 1: case 2: if (1) break; else 0; default:",
"case 1: case 2: if (1) 0; else break; default:",
"case 1: case 2: case 3: if (1) {} default:",
"case 1: bar:if (1) break; else continue; case 2: case 3: default:",
NULL};
static const ParserFlag always_flags[] = {
kAllowStrongMode
};
RunParserSyncTest(strong_context_data, data_success, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(sloppy_context_data, data_error, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
RunParserSyncTest(strong_context_data, data_error, kError, NULL, 0,
always_flags, arraysize(always_flags)); always_flags, arraysize(always_flags));
} }
......
// 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.
// Flags: --strong-mode
"use strict";
function CheckSwitch() {
let jumpStatements = [
"break; ",
"continue; ",
"break foo; ",
"continue foo; ",
"return; ",
"throw new TypeError(); ",
"if(1) break; else continue; ",
"if(1) {1+1; {break;}} else continue; "
]
let otherStatements = [
"null; ",
"1+1; ",
"{} ",
"for(;false;) {break;} ",
"for(;false;) {1+1; {throw new TypeError();}} ",
"(function(){return});",
"(function(){throw new TypeError();});",
"{break; 1+1;} ",
"if(1) break; ",
"if(1) break; else 1+1; ",
"if(1) 1+1; else break; ",
]
let successContexts = [
["switch(1) {case 1: ", "case 2: }"],
["switch(1) {case 1: case 2: ", "default: }"],
["switch(1) {case 1: case 2: ", "default: {}}"],
["switch(1) {case 1: case 2: ", "default: 1+1}"],
["switch(1) {case 1: break; case 2: ", "default: }"],
["switch(1) {case 1: case 2: break; case 3: ", "case 4: default: }"],
["switch(1) {case 1: if(1) break; else {", "} default: break;}"]
]
let strongThrowContexts = [
["switch(1) {case 1: 1+1; case 2: ", "}"],
["switch(1) {case 1: bar: break foo; case 2: ", "}"],
["switch(1) {case 1: bar:", " case 2: }"],
["switch(1) {case 1: bar:{ ", "} case 2: }"],
["switch(1) {case 1: bar:{ ", "} default: break;}"],
["switch(1) {case 1: { bar:{ { ", "} } } default: break;}"],
["switch(1) {case 1: { { { ", "} 1+1;} } default: break;}"],
["switch(1) {case 1: if(1) {", "} default: break;}"],
["switch(1) {case 1: bar:if(1) break; else {", "} default: break;}"]
]
let sloppy_wrap = ["function f() { foo:for(;;) {", "}}"];
let strong_wrap = ["function f() { 'use strong'; foo:for(;;) {", "}}"];
for (let context of successContexts) {
let sloppy_prefix = sloppy_wrap[0] + context[0];
let sloppy_suffix = context[1] + sloppy_wrap[1];
let strong_prefix = strong_wrap[0] + context[0];
let strong_suffix = context[1] + strong_wrap[1];
for (let code of jumpStatements) {
assertDoesNotThrow(strong_wrap[0] + "switch(1) {case 1: " + code + "}}}");
assertDoesNotThrow(strong_prefix + code + strong_suffix);
assertDoesNotThrow(strong_prefix + "{ 1+1; " + code + "}" +
strong_suffix);
assertDoesNotThrow(strong_prefix + "{ 1+1; { 1+1; " + code + "}}" +
strong_suffix);
assertDoesNotThrow(strong_prefix + "if(1) " + code + "else break;" +
strong_suffix);
assertDoesNotThrow(strong_prefix + "if(1) " + code +
"else if (1) break; else " + code + strong_suffix);
}
for (let code of otherStatements) {
assertDoesNotThrow(sloppy_prefix + code + sloppy_suffix);
assertThrows(strong_prefix + code + strong_suffix, SyntaxError);
}
}
for (let context of strongThrowContexts) {
let sloppy_prefix = sloppy_wrap[0] + context[0];
let sloppy_suffix = context[1] + sloppy_wrap[1];
let strong_prefix = strong_wrap[0] + context[0];
let strong_suffix = context[1] + strong_wrap[1];
for (let code of jumpStatements.concat(otherStatements)) {
assertDoesNotThrow(sloppy_prefix + code + sloppy_suffix);
assertThrows(strong_prefix + code + strong_suffix, SyntaxError);
}
}
for (let code of otherStatements) {
assertDoesNotThrow("switch(1) {default: " + code + "}");
assertDoesNotThrow("switch(1) {case 1: " + code + "}");
assertDoesNotThrow("switch(1) {case 1: default: " + code + "}");
assertDoesNotThrow("switch(1) {case 1: break; default: " + code + "}");
assertDoesNotThrow("switch(1) {case 1: " + code + "break; default: }");
}
}
CheckSwitch();
assertDoesNotThrow("'use strong'; switch(1) {}");
assertDoesNotThrow("'use strong'; switch(1) {case 1:}");
assertDoesNotThrow("'use strong'; switch(1) {default:}");
assertDoesNotThrow("'use strong'; switch(1) {case 1: case 2: default:}");
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