Commit d0651bd1 authored by Kevin Gibbons's avatar Kevin Gibbons Committed by Commit Bot

[language] Implement optional catch binding proposal

This allows the syntax `try {} catch {}` (with no binding after the
`catch`).

See https://github.com/michaelficarra/optional-catch-binding-proposal/

Currently behind --harmony-optional-catch-binding.

As part of the implementation, this allows TryCatchStatements to not
have an associated catch scope; various paths which assumed they
would have been updated to handle this case.

Cq-Include-Trybots: master.tryserver.v8:v8_linux_noi18n_rel_ng
Change-Id: Ic525b45199eef025eb05da562e10fbd4f3d7465f
Reviewed-on: https://chromium-review.googlesource.com/571453Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Kevin Gibbons <bakkot@gmail.com>
Cr-Commit-Position: refs/heads/master@{#48300}
parent f83d0e0b
......@@ -924,8 +924,10 @@ void AstPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
UNREACHABLE();
}
Print(" %s\n", prediction);
PrintLiteralWithModeIndented("CATCHVAR", node->scope()->catch_variable(),
node->scope()->catch_variable()->name());
if (node->scope()) {
PrintLiteralWithModeIndented("CATCHVAR", node->scope()->catch_variable(),
node->scope()->catch_variable()->name());
}
PrintIndentedVisit("CATCH", node->catch_block());
}
......
......@@ -4224,6 +4224,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_template_escapes)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrict_constructor_return)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_strict_legacy_accessor_builtins)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_optional_catch_binding)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) {
......
......@@ -194,7 +194,8 @@ DEFINE_IMPLICATION(es_staging, harmony)
V(harmony_function_sent, "harmony function.sent") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony public fields in class literals") \
V(harmony_bigint, "harmony arbitrary precision integers")
V(harmony_bigint, "harmony arbitrary precision integers") \
V(harmony_optional_catch_binding, "allow omitting binding in catch blocks")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED_BASE(V) \
......
......@@ -1615,9 +1615,11 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
}
try_control_builder.EndTry();
// Create a catch scope that binds the exception.
BuildNewLocalCatchContext(stmt->scope());
builder()->StoreAccumulatorInRegister(context);
if (stmt->scope()) {
// Create a catch scope that binds the exception.
BuildNewLocalCatchContext(stmt->scope());
builder()->StoreAccumulatorInRegister(context);
}
// If requested, clear message object as we enter the catch block.
if (stmt->ShouldClearPendingException(outer_catch_prediction)) {
......@@ -1629,7 +1631,11 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
// Evaluate the catch-block.
BuildIncrementBlockCoverageCounterIfEnabled(stmt, SourceRangeKind::kCatch);
VisitInScope(stmt->catch_block(), stmt->scope());
if (stmt->scope()) {
VisitInScope(stmt->catch_block(), stmt->scope());
} else {
VisitBlock(stmt->catch_block());
}
try_control_builder.EndCatch();
BuildIncrementBlockCoverageCounterIfEnabled(stmt,
SourceRangeKind::kContinuation);
......
......@@ -73,7 +73,7 @@ void Reparenter::VisitVariableProxy(VariableProxy* proxy) {
}
void Reparenter::VisitBlock(Block* stmt) {
if (stmt->scope() != nullptr)
if (stmt->scope())
stmt->scope()->ReplaceOuterScope(scope_);
else
VisitStatements(stmt->statements());
......@@ -81,7 +81,11 @@ void Reparenter::VisitBlock(Block* stmt) {
void Reparenter::VisitTryCatchStatement(TryCatchStatement* stmt) {
Visit(stmt->try_block());
stmt->scope()->ReplaceOuterScope(scope_);
if (stmt->scope()) {
stmt->scope()->ReplaceOuterScope(scope_);
} else {
Visit(stmt->catch_block());
}
}
void Reparenter::VisitWithStatement(WithStatement* stmt) {
......
......@@ -278,7 +278,8 @@ class ParserBase {
allow_harmony_object_rest_spread_(false),
allow_harmony_dynamic_import_(false),
allow_harmony_async_iteration_(false),
allow_harmony_template_escapes_(false) {}
allow_harmony_template_escapes_(false),
allow_harmony_optional_catch_binding_(false) {}
#define ALLOW_ACCESSORS(name) \
bool allow_##name() const { return allow_##name##_; } \
......@@ -293,6 +294,7 @@ class ParserBase {
ALLOW_ACCESSORS(harmony_dynamic_import);
ALLOW_ACCESSORS(harmony_async_iteration);
ALLOW_ACCESSORS(harmony_template_escapes);
ALLOW_ACCESSORS(harmony_optional_catch_binding);
#undef ALLOW_ACCESSORS
......@@ -1497,6 +1499,7 @@ class ParserBase {
bool allow_harmony_dynamic_import_;
bool allow_harmony_async_iteration_;
bool allow_harmony_template_escapes_;
bool allow_harmony_optional_catch_binding_;
friend class DiscardableZoneScope;
};
......@@ -5467,50 +5470,60 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement(
{
SourceRangeScope catch_range_scope(scanner(), &catch_range);
if (Check(Token::CATCH)) {
Expect(Token::LPAREN, CHECK_OK);
catch_info.scope = NewScope(CATCH_SCOPE);
catch_info.scope->set_start_position(scanner()->location().beg_pos);
{
BlockState catch_block_state(&scope_, catch_info.scope);
bool has_binding;
if (allow_harmony_optional_catch_binding()) {
has_binding = Check(Token::LPAREN);
} else {
has_binding = true;
Expect(Token::LPAREN, CHECK_OK);
}
catch_block = factory()->NewBlock(16, false);
if (has_binding) {
catch_info.scope = NewScope(CATCH_SCOPE);
catch_info.scope->set_start_position(scanner()->location().beg_pos);
// Create a block scope to hold any lexical declarations created
// as part of destructuring the catch parameter.
{
BlockState catch_variable_block_state(zone(), &scope_);
scope()->set_start_position(scanner()->location().beg_pos);
typename Types::Target target(this, catch_block);
// This does not simply call ParsePrimaryExpression to avoid
// ExpressionFromIdentifier from being called in the first
// branch, which would introduce an unresolved symbol and mess
// with arrow function names.
if (peek_any_identifier()) {
catch_info.name =
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
} else {
ExpressionClassifier pattern_classifier(this);
catch_info.pattern = ParsePrimaryExpression(CHECK_OK);
ValidateBindingPattern(CHECK_OK);
}
BlockState catch_block_state(&scope_, catch_info.scope);
catch_block = factory()->NewBlock(16, false);
// Create a block scope to hold any lexical declarations created
// as part of destructuring the catch parameter.
{
BlockState catch_variable_block_state(zone(), &scope_);
scope()->set_start_position(scanner()->location().beg_pos);
// This does not simply call ParsePrimaryExpression to avoid
// ExpressionFromIdentifier from being called in the first
// branch, which would introduce an unresolved symbol and mess
// with arrow function names.
if (peek_any_identifier()) {
catch_info.name =
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
} else {
ExpressionClassifier pattern_classifier(this);
catch_info.pattern = ParsePrimaryExpression(CHECK_OK);
ValidateBindingPattern(CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
impl()->RewriteCatchPattern(&catch_info, CHECK_OK);
if (!impl()->IsNull(catch_info.init_block)) {
catch_block->statements()->Add(catch_info.init_block, zone());
}
Expect(Token::RPAREN, CHECK_OK);
impl()->RewriteCatchPattern(&catch_info, CHECK_OK);
if (!impl()->IsNull(catch_info.init_block)) {
catch_block->statements()->Add(catch_info.init_block, zone());
}
catch_info.inner_block = ParseBlock(nullptr, CHECK_OK);
catch_block->statements()->Add(catch_info.inner_block, zone());
impl()->ValidateCatchBlock(catch_info, CHECK_OK);
scope()->set_end_position(scanner()->location().end_pos);
catch_block->set_scope(scope()->FinalizeBlockScope());
catch_info.inner_block = ParseBlock(nullptr, CHECK_OK);
catch_block->statements()->Add(catch_info.inner_block, zone());
impl()->ValidateCatchBlock(catch_info, CHECK_OK);
scope()->set_end_position(scanner()->location().end_pos);
catch_block->set_scope(scope()->FinalizeBlockScope());
}
}
}
catch_info.scope->set_end_position(scanner()->location().end_pos);
catch_info.scope->set_end_position(scanner()->location().end_pos);
} else {
catch_block = ParseBlock(nullptr, CHECK_OK);
}
}
}
......
......@@ -506,6 +506,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_dynamic_import(FLAG_harmony_dynamic_import);
set_allow_harmony_async_iteration(FLAG_harmony_async_iteration);
set_allow_harmony_template_escapes(FLAG_harmony_template_escapes);
set_allow_harmony_optional_catch_binding(FLAG_harmony_optional_catch_binding);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -1661,7 +1662,6 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
if (catch_block != nullptr && finally_block != nullptr) {
// If we have both, create an inner try/catch.
DCHECK_NOT_NULL(catch_info.scope);
TryCatchStatement* statement;
statement = factory()->NewTryCatchStatement(try_block, catch_info.scope,
catch_block, kNoSourcePosition);
......@@ -1674,7 +1674,6 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
if (catch_block != nullptr) {
DCHECK_NULL(finally_block);
DCHECK_NOT_NULL(catch_info.scope);
TryCatchStatement* stmt = factory()->NewTryCatchStatement(
try_block, catch_info.scope, catch_block, pos);
RecordTryCatchStatementSourceRange(stmt, catch_range);
......@@ -4294,9 +4293,8 @@ void Parser::BuildIteratorCloseForCompletion(ZoneList<Statement*>* statements,
zone());
Block* catch_block = factory()->NewBlock(0, false);
Scope* catch_scope = NewHiddenCatchScope();
try_call_return = factory()->NewTryCatchStatement(try_block, catch_scope,
catch_block, nopos);
try_call_return =
factory()->NewTryCatchStatement(try_block, nullptr, catch_block, nopos);
}
// let output = %_Call(iteratorReturn, iterator);
......
......@@ -298,6 +298,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
SET_ALLOW(harmony_async_iteration);
SET_ALLOW(harmony_template_escapes);
SET_ALLOW(harmony_restrictive_generators);
SET_ALLOW(harmony_optional_catch_binding);
#undef SET_ALLOW
}
return reusable_preparser_;
......
......@@ -292,7 +292,7 @@ snippet: "
"
frame size: 22
parameter count: 1
bytecode array length: 571
bytecode array length: 557
bytecodes: [
B(Ldar), R(2),
B(JumpIfUndefined), U8(18),
......@@ -326,7 +326,7 @@ bytecodes: [
B(LdaZero),
B(Star), R(11),
B(Mov), R(15), R(12),
B(JumpConstant), U8(21),
B(JumpConstant), U8(20),
B(LdaZero),
B(Star), R(6),
B(Mov), R(context), R(17),
......@@ -415,15 +415,15 @@ bytecodes: [
B(Star), R(17),
B(LdaZero),
B(TestEqualStrict), R(6), U8(14),
B(JumpIfTrue), U8(104),
B(JumpIfTrue), U8(90),
B(LdaNamedProperty), R(4), U8(15), U8(15),
B(Star), R(8),
B(TestUndetectable),
B(JumpIfFalse), U8(4),
B(Jump), U8(93),
B(Jump), U8(79),
B(LdaSmi), I8(1),
B(TestEqualStrict), R(6), U8(18),
B(JumpIfFalse), U8(61),
B(JumpIfFalse), U8(47),
B(Ldar), R(8),
B(TestTypeOf), U8(5),
B(JumpIfFalse), U8(4),
......@@ -438,16 +438,10 @@ bytecodes: [
B(Mov), R(8), R(19),
B(Mov), R(4), R(20),
B(InvokeIntrinsic), U8(Runtime::k_Call), R(19), U8(2),
B(Jump), U8(20),
B(Star), R(19),
B(Ldar), R(closure),
B(CreateCatchContext), R(19), U8(13), U8(17),
B(Star), R(18),
B(Jump), U8(6),
B(LdaTheHole),
B(SetPendingMessage),
B(Ldar), R(18),
B(PushContext), R(19),
B(PopContext), R(19),
B(Jump), U8(27),
B(Mov), R(8), R(18),
B(Mov), R(4), R(19),
......@@ -460,7 +454,7 @@ bytecodes: [
B(Ldar), R(17),
B(SetPendingMessage),
B(Ldar), R(15),
B(SwitchOnSmiNoFeedback), U8(18), U8(2), I8(0),
B(SwitchOnSmiNoFeedback), U8(17), U8(2), I8(0),
B(Jump), U8(13),
B(LdaZero),
B(Star), R(11),
......@@ -493,7 +487,7 @@ bytecodes: [
B(Jump), U8(39),
B(Star), R(15),
B(Ldar), R(closure),
B(CreateCatchContext), R(15), U8(13), U8(20),
B(CreateCatchContext), R(15), U8(13), U8(19),
B(Star), R(14),
B(LdaTheHole),
B(SetPendingMessage),
......@@ -522,7 +516,7 @@ bytecodes: [
B(Ldar), R(13),
B(SetPendingMessage),
B(Ldar), R(11),
B(SwitchOnSmiNoFeedback), U8(22), U8(3), I8(0),
B(SwitchOnSmiNoFeedback), U8(21), U8(3), I8(0),
B(Jump), U8(22),
B(LdaTrue),
B(Star), R(16),
......@@ -540,7 +534,7 @@ bytecodes: [
constant pool: [
Smi [37],
Smi [104],
Smi [427],
Smi [413],
Smi [15],
Smi [7],
TUPLE2_TYPE,
......@@ -555,18 +549,17 @@ constant pool: [
FIXED_ARRAY_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
FIXED_ARRAY_TYPE,
Smi [6],
Smi [14],
FIXED_ARRAY_TYPE,
Smi [448],
Smi [434],
Smi [6],
Smi [20],
Smi [23],
]
handlers: [
[40, 516, 524],
[43, 477, 479],
[40, 502, 510],
[43, 463, 465],
[90, 277, 285],
[93, 237, 239],
[346, 356, 358],
......
......@@ -11,7 +11,7 @@ snippet: "
"
frame size: 14
parameter count: 1
bytecode array length: 262
bytecode array length: 248
bytecodes: [
/* 30 E> */ B(StackCheck),
B(LdaZero),
......@@ -73,15 +73,15 @@ bytecodes: [
B(Star), R(10),
B(LdaZero),
B(TestEqualStrict), R(4), U8(14),
B(JumpIfTrue), U8(104),
B(JumpIfTrue), U8(90),
B(LdaNamedProperty), R(2), U8(7), U8(15),
B(Star), R(6),
B(TestUndetectable),
B(JumpIfFalse), U8(4),
B(Jump), U8(93),
B(Jump), U8(79),
B(LdaSmi), I8(1),
B(TestEqualStrict), R(4), U8(18),
B(JumpIfFalse), U8(61),
B(JumpIfFalse), U8(47),
B(Ldar), R(6),
B(TestTypeOf), U8(5),
B(JumpIfFalse), U8(4),
......@@ -96,16 +96,10 @@ bytecodes: [
B(Mov), R(6), R(12),
B(Mov), R(2), R(13),
B(InvokeIntrinsic), U8(Runtime::k_Call), R(12), U8(2),
B(Jump), U8(20),
B(Star), R(12),
B(Ldar), R(closure),
B(CreateCatchContext), R(12), U8(5), U8(9),
B(Star), R(11),
B(Jump), U8(6),
B(LdaTheHole),
B(SetPendingMessage),
B(Ldar), R(11),
B(PushContext), R(12),
B(PopContext), R(12),
B(Jump), U8(27),
B(Mov), R(6), R(11),
B(Mov), R(2), R(12),
......@@ -135,7 +129,6 @@ constant pool: [
FIXED_ARRAY_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
FIXED_ARRAY_TYPE,
]
handlers: [
[7, 124, 132],
......@@ -150,7 +143,7 @@ snippet: "
"
frame size: 15
parameter count: 1
bytecode array length: 272
bytecode array length: 258
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaConstant), U8(0),
......@@ -214,15 +207,15 @@ bytecodes: [
B(Star), R(11),
B(LdaZero),
B(TestEqualStrict), R(5), U8(13),
B(JumpIfTrue), U8(104),
B(JumpIfTrue), U8(90),
B(LdaNamedProperty), R(3), U8(7), U8(14),
B(Star), R(7),
B(TestUndetectable),
B(JumpIfFalse), U8(4),
B(Jump), U8(93),
B(Jump), U8(79),
B(LdaSmi), I8(1),
B(TestEqualStrict), R(5), U8(17),
B(JumpIfFalse), U8(61),
B(JumpIfFalse), U8(47),
B(Ldar), R(7),
B(TestTypeOf), U8(5),
B(JumpIfFalse), U8(4),
......@@ -237,16 +230,10 @@ bytecodes: [
B(Mov), R(7), R(13),
B(Mov), R(3), R(14),
B(InvokeIntrinsic), U8(Runtime::k_Call), R(13), U8(2),
B(Jump), U8(20),
B(Star), R(13),
B(Ldar), R(closure),
B(CreateCatchContext), R(13), U8(5), U8(9),
B(Star), R(12),
B(Jump), U8(6),
B(LdaTheHole),
B(SetPendingMessage),
B(Ldar), R(12),
B(PushContext), R(13),
B(PopContext), R(13),
B(Jump), U8(27),
B(Mov), R(7), R(12),
B(Mov), R(3), R(13),
......@@ -259,7 +246,7 @@ bytecodes: [
B(Ldar), R(11),
B(SetPendingMessage),
B(Ldar), R(9),
B(SwitchOnSmiNoFeedback), U8(10), U8(2), I8(0),
B(SwitchOnSmiNoFeedback), U8(9), U8(2), I8(0),
B(Jump), U8(8),
B(Ldar), R(10),
/* 85 S> */ B(Return),
......@@ -278,7 +265,6 @@ constant pool: [
FIXED_ARRAY_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
FIXED_ARRAY_TYPE,
Smi [6],
Smi [9],
]
......@@ -297,7 +283,7 @@ snippet: "
"
frame size: 14
parameter count: 1
bytecode array length: 280
bytecode array length: 266
bytecodes: [
/* 30 E> */ B(StackCheck),
B(LdaZero),
......@@ -367,15 +353,15 @@ bytecodes: [
B(Star), R(10),
B(LdaZero),
B(TestEqualStrict), R(4), U8(16),
B(JumpIfTrue), U8(104),
B(JumpIfTrue), U8(90),
B(LdaNamedProperty), R(2), U8(7), U8(17),
B(Star), R(6),
B(TestUndetectable),
B(JumpIfFalse), U8(4),
B(Jump), U8(93),
B(Jump), U8(79),
B(LdaSmi), I8(1),
B(TestEqualStrict), R(4), U8(20),
B(JumpIfFalse), U8(61),
B(JumpIfFalse), U8(47),
B(Ldar), R(6),
B(TestTypeOf), U8(5),
B(JumpIfFalse), U8(4),
......@@ -390,16 +376,10 @@ bytecodes: [
B(Mov), R(6), R(12),
B(Mov), R(2), R(13),
B(InvokeIntrinsic), U8(Runtime::k_Call), R(12), U8(2),
B(Jump), U8(20),
B(Star), R(12),
B(Ldar), R(closure),
B(CreateCatchContext), R(12), U8(5), U8(9),
B(Star), R(11),
B(Jump), U8(6),
B(LdaTheHole),
B(SetPendingMessage),
B(Ldar), R(11),
B(PushContext), R(12),
B(PopContext), R(12),
B(Jump), U8(27),
B(Mov), R(6), R(11),
B(Mov), R(2), R(12),
......@@ -429,7 +409,6 @@ constant pool: [
FIXED_ARRAY_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
FIXED_ARRAY_TYPE,
]
handlers: [
[7, 142, 150],
......@@ -444,7 +423,7 @@ snippet: "
"
frame size: 13
parameter count: 1
bytecode array length: 282
bytecode array length: 268
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), R(7),
......@@ -510,15 +489,15 @@ bytecodes: [
B(Star), R(9),
B(LdaZero),
B(TestEqualStrict), R(3), U8(19),
B(JumpIfTrue), U8(104),
B(JumpIfTrue), U8(90),
B(LdaNamedProperty), R(1), U8(9), U8(20),
B(Star), R(5),
B(TestUndetectable),
B(JumpIfFalse), U8(4),
B(Jump), U8(93),
B(Jump), U8(79),
B(LdaSmi), I8(1),
B(TestEqualStrict), R(3), U8(23),
B(JumpIfFalse), U8(61),
B(JumpIfFalse), U8(47),
B(Ldar), R(5),
B(TestTypeOf), U8(5),
B(JumpIfFalse), U8(4),
......@@ -533,16 +512,10 @@ bytecodes: [
B(Mov), R(5), R(11),
B(Mov), R(1), R(12),
B(InvokeIntrinsic), U8(Runtime::k_Call), R(11), U8(2),
B(Jump), U8(20),
B(Star), R(11),
B(Ldar), R(closure),
B(CreateCatchContext), R(11), U8(7), U8(11),
B(Star), R(10),
B(Jump), U8(6),
B(LdaTheHole),
B(SetPendingMessage),
B(Ldar), R(10),
B(PushContext), R(11),
B(PopContext), R(11),
B(Jump), U8(27),
B(Mov), R(5), R(10),
B(Mov), R(1), R(11),
......@@ -555,7 +528,7 @@ bytecodes: [
B(Ldar), R(9),
B(SetPendingMessage),
B(Ldar), R(7),
B(SwitchOnSmiNoFeedback), U8(12), U8(2), I8(0),
B(SwitchOnSmiNoFeedback), U8(11), U8(2), I8(0),
B(Jump), U8(8),
B(Ldar), R(8),
/* 105 S> */ B(Return),
......@@ -576,7 +549,6 @@ constant pool: [
FIXED_ARRAY_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
FIXED_ARRAY_TYPE,
Smi [6],
Smi [9],
]
......
......@@ -130,7 +130,7 @@ snippet: "
"
frame size: 17
parameter count: 1
bytecode array length: 402
bytecode array length: 388
bytecodes: [
B(Ldar), R(2),
B(JumpIfUndefined), U8(18),
......@@ -244,15 +244,15 @@ bytecodes: [
B(Star), R(13),
B(LdaZero),
B(TestEqualStrict), R(6), U8(14),
B(JumpIfTrue), U8(104),
B(JumpIfTrue), U8(90),
B(LdaNamedProperty), R(4), U8(14), U8(15),
B(Star), R(8),
B(TestUndetectable),
B(JumpIfFalse), U8(4),
B(Jump), U8(93),
B(Jump), U8(79),
B(LdaSmi), I8(1),
B(TestEqualStrict), R(6), U8(18),
B(JumpIfFalse), U8(61),
B(JumpIfFalse), U8(47),
B(Ldar), R(8),
B(TestTypeOf), U8(5),
B(JumpIfFalse), U8(4),
......@@ -267,16 +267,10 @@ bytecodes: [
B(Mov), R(8), R(15),
B(Mov), R(4), R(16),
B(InvokeIntrinsic), U8(Runtime::k_Call), R(15), U8(2),
B(Jump), U8(20),
B(Star), R(15),
B(Ldar), R(closure),
B(CreateCatchContext), R(15), U8(12), U8(16),
B(Star), R(14),
B(Jump), U8(6),
B(LdaTheHole),
B(SetPendingMessage),
B(Ldar), R(14),
B(PushContext), R(15),
B(PopContext), R(15),
B(Jump), U8(27),
B(Mov), R(8), R(14),
B(Mov), R(4), R(15),
......@@ -289,7 +283,7 @@ bytecodes: [
B(Ldar), R(13),
B(SetPendingMessage),
B(Ldar), R(11),
B(SwitchOnSmiNoFeedback), U8(17), U8(2), I8(0),
B(SwitchOnSmiNoFeedback), U8(16), U8(2), I8(0),
B(Jump), U8(8),
B(Ldar), R(12),
/* 44 S> */ B(Return),
......@@ -315,7 +309,6 @@ constant pool: [
FIXED_ARRAY_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["return"],
ONE_BYTE_INTERNALIZED_STRING_TYPE [""],
FIXED_ARRAY_TYPE,
Smi [6],
Smi [9],
]
......
......@@ -1291,6 +1291,8 @@ enum ParserFlag {
kAllowHarmonyDynamicImport,
kAllowHarmonyAsyncIteration,
kAllowHarmonyTemplateEscapes,
kAllowHarmonyDoExpressions,
kAllowHarmonyOptionalCatchBinding,
};
enum ParserSyncTestResult {
......@@ -1311,6 +1313,9 @@ void SetGlobalFlags(i::EnumSet<ParserFlag> flags) {
i::FLAG_harmony_async_iteration = flags.Contains(kAllowHarmonyAsyncIteration);
i::FLAG_harmony_template_escapes =
flags.Contains(kAllowHarmonyTemplateEscapes);
i::FLAG_harmony_do_expressions = flags.Contains(kAllowHarmonyDoExpressions);
i::FLAG_harmony_optional_catch_binding =
flags.Contains(kAllowHarmonyOptionalCatchBinding);
}
void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
......@@ -1329,6 +1334,10 @@ void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
flags.Contains(kAllowHarmonyAsyncIteration));
parser->set_allow_harmony_template_escapes(
flags.Contains(kAllowHarmonyTemplateEscapes));
parser->set_allow_harmony_do_expressions(
flags.Contains(kAllowHarmonyDoExpressions));
parser->set_allow_harmony_optional_catch_binding(
flags.Contains(kAllowHarmonyOptionalCatchBinding));
}
void TestParserSyncWithFlags(i::Handle<i::String> source,
......@@ -2574,6 +2583,66 @@ TEST(NoErrorsTryCatchFinally) {
RunParserSyncTest(context_data, statement_data, kSuccess);
}
TEST(OptionalCatchBinding) {
// clang-format off
const char* context_data[][2] = {
{"", ""},
{"'use strict';", ""},
{"try {", "} catch (e) { }"},
{"try {} catch (e) {", "}"},
{"try {", "} catch ({e}) { }"},
{"try {} catch ({e}) {", "}"},
{"function f() {", "}"},
{ NULL, NULL }
};
const char* statement_data[] = {
"try { } catch { }",
"try { } catch { } finally { }",
"try { let e; } catch { let e; }",
"try { let e; } catch { let e; } finally { let e; }",
NULL
};
// clang-format on
// No error with flag
static const ParserFlag flags[] = {kAllowHarmonyOptionalCatchBinding};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0, flags,
arraysize(flags));
// Still an error without flag
RunParserSyncTest(context_data, statement_data, kError);
}
TEST(OptionalCatchBindingInDoExpression) {
// This is an edge case no otherwise hit: a catch scope in a parameter
// expression which needs its own scope.
// clang-format off
const char* context_data[][2] = {
{"((x = (eval(''), do {", "}))=>{})()"},
{ NULL, NULL }
};
const char* statement_data[] = {
"try { } catch { }",
"try { } catch { } finally { }",
"try { let e; } catch { let e; }",
"try { let e; } catch { let e; } finally { let e; }",
NULL
};
// clang-format on
// No error with flag
static const ParserFlag do_and_catch_flags[] = {
kAllowHarmonyDoExpressions, kAllowHarmonyOptionalCatchBinding};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
do_and_catch_flags, arraysize(do_and_catch_flags));
// Still an error without flag
static const ParserFlag do_flag[] = {kAllowHarmonyDoExpressions};
RunParserSyncTest(context_data, statement_data, kError, NULL, 0, do_flag,
arraysize(do_flag));
}
TEST(ErrorsRegexpLiteral) {
const char* context_data[][2] = {
......@@ -3636,81 +3705,81 @@ TEST(MaybeAssignedInsideLoop) {
{1, "for (j of x) { [foo] = [j] }", top},
{1, "for (j of x) { var foo = j }", top},
{1, "for (j of x) { var [foo] = [j] }", top},
{0, "for (j of x) { let foo = j }", {2}},
{0, "for (j of x) { let [foo] = [j] }", {2}},
{0, "for (j of x) { const foo = j }", {2}},
{0, "for (j of x) { const [foo] = [j] }", {2}},
{0, "for (j of x) { function foo() {return j} }", {2}},
{0, "for (j of x) { let foo = j }", {1}},
{0, "for (j of x) { let [foo] = [j] }", {1}},
{0, "for (j of x) { const foo = j }", {1}},
{0, "for (j of x) { const [foo] = [j] }", {1}},
{0, "for (j of x) { function foo() {return j} }", {1}},
{1, "for ({j} of x) { foo = j }", top},
{1, "for ({j} of x) { [foo] = [j] }", top},
{1, "for ({j} of x) { var foo = j }", top},
{1, "for ({j} of x) { var [foo] = [j] }", top},
{0, "for ({j} of x) { let foo = j }", {2}},
{0, "for ({j} of x) { let [foo] = [j] }", {2}},
{0, "for ({j} of x) { const foo = j }", {2}},
{0, "for ({j} of x) { const [foo] = [j] }", {2}},
{0, "for ({j} of x) { function foo() {return j} }", {2}},
{0, "for ({j} of x) { let foo = j }", {1}},
{0, "for ({j} of x) { let [foo] = [j] }", {1}},
{0, "for ({j} of x) { const foo = j }", {1}},
{0, "for ({j} of x) { const [foo] = [j] }", {1}},
{0, "for ({j} of x) { function foo() {return j} }", {1}},
{1, "for (var j of x) { foo = j }", top},
{1, "for (var j of x) { [foo] = [j] }", top},
{1, "for (var j of x) { var foo = j }", top},
{1, "for (var j of x) { var [foo] = [j] }", top},
{0, "for (var j of x) { let foo = j }", {2}},
{0, "for (var j of x) { let [foo] = [j] }", {2}},
{0, "for (var j of x) { const foo = j }", {2}},
{0, "for (var j of x) { const [foo] = [j] }", {2}},
{0, "for (var j of x) { function foo() {return j} }", {2}},
{0, "for (var j of x) { let foo = j }", {1}},
{0, "for (var j of x) { let [foo] = [j] }", {1}},
{0, "for (var j of x) { const foo = j }", {1}},
{0, "for (var j of x) { const [foo] = [j] }", {1}},
{0, "for (var j of x) { function foo() {return j} }", {1}},
{1, "for (var {j} of x) { foo = j }", top},
{1, "for (var {j} of x) { [foo] = [j] }", top},
{1, "for (var {j} of x) { var foo = j }", top},
{1, "for (var {j} of x) { var [foo] = [j] }", top},
{0, "for (var {j} of x) { let foo = j }", {2}},
{0, "for (var {j} of x) { let [foo] = [j] }", {2}},
{0, "for (var {j} of x) { const foo = j }", {2}},
{0, "for (var {j} of x) { const [foo] = [j] }", {2}},
{0, "for (var {j} of x) { function foo() {return j} }", {2}},
{0, "for (var {j} of x) { let foo = j }", {1}},
{0, "for (var {j} of x) { let [foo] = [j] }", {1}},
{0, "for (var {j} of x) { const foo = j }", {1}},
{0, "for (var {j} of x) { const [foo] = [j] }", {1}},
{0, "for (var {j} of x) { function foo() {return j} }", {1}},
{1, "for (let j of x) { foo = j }", top},
{1, "for (let j of x) { [foo] = [j] }", top},
{1, "for (let j of x) { var foo = j }", top},
{1, "for (let j of x) { var [foo] = [j] }", top},
{0, "for (let j of x) { let foo = j }", {0, 2, 0}},
{0, "for (let j of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (let j of x) { const foo = j }", {0, 2, 0}},
{0, "for (let j of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (let j of x) { function foo() {return j} }", {0, 2, 0}},
{0, "for (let j of x) { let foo = j }", {0, 1, 0}},
{0, "for (let j of x) { let [foo] = [j] }", {0, 1, 0}},
{0, "for (let j of x) { const foo = j }", {0, 1, 0}},
{0, "for (let j of x) { const [foo] = [j] }", {0, 1, 0}},
{0, "for (let j of x) { function foo() {return j} }", {0, 1, 0}},
{1, "for (let {j} of x) { foo = j }", top},
{1, "for (let {j} of x) { [foo] = [j] }", top},
{1, "for (let {j} of x) { var foo = j }", top},
{1, "for (let {j} of x) { var [foo] = [j] }", top},
{0, "for (let {j} of x) { let foo = j }", {0, 2, 0}},
{0, "for (let {j} of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (let {j} of x) { const foo = j }", {0, 2, 0}},
{0, "for (let {j} of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (let {j} of x) { function foo() {return j} }", {0, 2, 0}},
{0, "for (let {j} of x) { let foo = j }", {0, 1, 0}},
{0, "for (let {j} of x) { let [foo] = [j] }", {0, 1, 0}},
{0, "for (let {j} of x) { const foo = j }", {0, 1, 0}},
{0, "for (let {j} of x) { const [foo] = [j] }", {0, 1, 0}},
{0, "for (let {j} of x) { function foo() {return j} }", {0, 1, 0}},
{1, "for (const j of x) { foo = j }", top},
{1, "for (const j of x) { [foo] = [j] }", top},
{1, "for (const j of x) { var foo = j }", top},
{1, "for (const j of x) { var [foo] = [j] }", top},
{0, "for (const j of x) { let foo = j }", {0, 2, 0}},
{0, "for (const j of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (const j of x) { const foo = j }", {0, 2, 0}},
{0, "for (const j of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (const j of x) { function foo() {return j} }", {0, 2, 0}},
{0, "for (const j of x) { let foo = j }", {0, 1, 0}},
{0, "for (const j of x) { let [foo] = [j] }", {0, 1, 0}},
{0, "for (const j of x) { const foo = j }", {0, 1, 0}},
{0, "for (const j of x) { const [foo] = [j] }", {0, 1, 0}},
{0, "for (const j of x) { function foo() {return j} }", {0, 1, 0}},
{1, "for (const {j} of x) { foo = j }", top},
{1, "for (const {j} of x) { [foo] = [j] }", top},
{1, "for (const {j} of x) { var foo = j }", top},
{1, "for (const {j} of x) { var [foo] = [j] }", top},
{0, "for (const {j} of x) { let foo = j }", {0, 2, 0}},
{0, "for (const {j} of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (const {j} of x) { const foo = j }", {0, 2, 0}},
{0, "for (const {j} of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (const {j} of x) { function foo() {return j} }", {0, 2, 0}},
{0, "for (const {j} of x) { let foo = j }", {0, 1, 0}},
{0, "for (const {j} of x) { let [foo] = [j] }", {0, 1, 0}},
{0, "for (const {j} of x) { const foo = j }", {0, 1, 0}},
{0, "for (const {j} of x) { const [foo] = [j] }", {0, 1, 0}},
{0, "for (const {j} of x) { function foo() {return j} }", {0, 1, 0}},
{1, "for (j in x) { foo = j }", top},
{1, "for (j in x) { [foo] = [j] }", top},
......
......@@ -1352,10 +1352,11 @@ TEST(CodeSerializerLargeCodeObject) {
// code. Don't even bother generating optimized code to avoid timeouts.
FLAG_always_opt = false;
Vector<const uint8_t> source =
ConstructSource(STATIC_CHAR_VECTOR("var j=1; if (j == 0) {"),
STATIC_CHAR_VECTOR("for (let i of Object.prototype);"),
STATIC_CHAR_VECTOR("} j=7; j"), 1100);
Vector<const uint8_t> source = ConstructSource(
STATIC_CHAR_VECTOR("var j=1; if (j == 0) {"),
STATIC_CHAR_VECTOR(
"for (let i of Object.prototype) for (let k = 0; k < 0; ++k);"),
STATIC_CHAR_VECTOR("} j=7; j"), 1100);
Handle<String> source_str =
isolate->factory()->NewStringFromOneByte(source).ToHandleChecked();
......
// Copyright 2017 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-optional-catch-binding
let state = 'initial';
x: try {
throw new Error('caught');
state = 'unreachable';
} catch {
assertEquals(state, 'initial');
state = 'caught';
break x;
state = 'unreachable';
}
assertEquals(state, 'caught');
state = 'initial';
x: try {
throw new Error('caught');
state = 'unreachable';
} catch {
assertEquals(state, 'initial');
state = 'caught';
break x;
state = 'unreachable';
} finally {
assertEquals(state, 'caught');
state = 'finally';
}
assertEquals(state, 'finally');
state = 'initial';
x: {
y: try {
throw new Error('caught');
state = 'unreachable';
} catch {
assertEquals(state, 'initial');
state = 'caught';
break x;
state = 'unreachable';
} finally {
assertEquals(state, 'caught');
state = 'finally';
break y;
state = 'unreachable';
}
assertEquals(state, 'finally');
state = 'after block';
}
assertEquals(state, 'after block');
do {
try {
throw new Error();
} catch {
break;
}
assertUnreachable();
} while(false);
// Copyright 2017 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-optional-catch-binding
let state = 'initial';
try {
throw new Error('caught');
state = 'unreachable';
} catch { // Note the lack of a binding
assertEquals(state, 'initial');
state = 'caught';
}
assertEquals(state, 'caught');
let sigil1 = {};
try {
throw sigil1;
} catch (e) {
assertEquals(e, sigil1);
}
let sigil2 = {};
let reached = false;
try {
try {
throw sigil1;
} catch {
reached = true;
} finally {
throw sigil2;
}
} catch (e) {
assertEquals(e, sigil2);
}
assertTrue(reached);
......@@ -48,9 +48,10 @@ FEATURE_FLAGS = {
'regexp-unicode-property-escapes': '--harmony-regexp-property',
'regexp-lookbehind': '--harmony-regexp-lookbehind',
'Promise.prototype.finally': '--harmony-promise-finally',
'optional-catch-binding': '--harmony-optional-catch-binding',
}
SKIPPED_FEATURES = set(['BigInt', 'class-fields', 'optional-catch-binding'])
SKIPPED_FEATURES = set(['BigInt', 'class-fields'])
DATA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
ARCHIVE = DATA + ".tar"
......
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