summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2017-03-21 16:16:48 +0100
committerMichaël Zasso <targos@protonmail.com>2017-03-25 09:44:11 +0100
commit07088e6fc1342b5581d9e6f7ce084308573e3139 (patch)
tree05df5e41276c73c3f3392e1aa0bce02084866d2b /deps
parent8394b05e20d3b661bb36cf778cd1f8c323334d7d (diff)
downloadandroid-node-v8-07088e6fc1342b5581d9e6f7ce084308573e3139.tar.gz
android-node-v8-07088e6fc1342b5581d9e6f7ce084308573e3139.tar.bz2
android-node-v8-07088e6fc1342b5581d9e6f7ce084308573e3139.zip
deps: backport 39642fa from upstream V8
This is an almost clean cherry-pick of the original commit. The only conflict was related to a rename of an internal class. Original commit message: [async-await] (simpler) fix for Return in try/finally in async functions Alternative approach to https://codereview.chromium.org/2667983004/, which does not depend on implicit control flow changes from https://codereview.chromium.org/2664083002 - Remove handling for `async function` from Parser::RewriteReturn(). This functionality is moved to BytecodeGenerator::BuildAsyncReturn(). This ensures that promise resolution is deferred until all finally blocks are evaluated fully. - Add a new deferred command (CMD_ASYNC_RETURN), which instructs ControlScope to generate return code using BuildAsyncReturn rather than BuildReturn. - Parser has a new `NewReturnStatement()` helper which determines what type of return statement to generate based on the type of function. BUG=v8:5896, v8:4483 R=littledan@chromium.org, neis@chromium.org, rmcilroy@chromium.org, adamk@chromium.org, gsathya@chromium.org Review-Url: https://codereview.chromium.org/2685683002 Cr-Commit-Position: refs/heads/master@{#43104} Fixes: https://github.com/nodejs/node/issues/11960 PR-URL: https://github.com/nodejs/node/pull/11752 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Diffstat (limited to 'deps')
-rw-r--r--deps/v8/src/ast/ast-numbering.cc3
-rw-r--r--deps/v8/src/ast/ast.h20
-rw-r--r--deps/v8/src/ast/scopes.cc27
-rw-r--r--deps/v8/src/ast/scopes.h68
-rw-r--r--deps/v8/src/interpreter/bytecode-generator.cc45
-rw-r--r--deps/v8/src/interpreter/bytecode-generator.h1
-rw-r--r--deps/v8/src/parsing/parser-base.h31
-rw-r--r--deps/v8/src/parsing/parser.cc11
-rw-r--r--deps/v8/src/parsing/preparser.h4
-rw-r--r--deps/v8/test/mjsunit/es8/async-function-try-finally.js201
10 files changed, 373 insertions, 38 deletions
diff --git a/deps/v8/src/ast/ast-numbering.cc b/deps/v8/src/ast/ast-numbering.cc
index 25aa9d7a5a..49040f7841 100644
--- a/deps/v8/src/ast/ast-numbering.cc
+++ b/deps/v8/src/ast/ast-numbering.cc
@@ -211,6 +211,9 @@ void AstNumberingVisitor::VisitExpressionStatement(ExpressionStatement* node) {
void AstNumberingVisitor::VisitReturnStatement(ReturnStatement* node) {
IncrementNodeCount();
Visit(node->expression());
+
+ DCHECK(!node->is_async_return() ||
+ properties_.flags() & AstProperties::kMustUseIgnitionTurbo);
}
diff --git a/deps/v8/src/ast/ast.h b/deps/v8/src/ast/ast.h
index af561e0a3f..0cf20b0e4c 100644
--- a/deps/v8/src/ast/ast.h
+++ b/deps/v8/src/ast/ast.h
@@ -895,17 +895,25 @@ class BreakStatement final : public JumpStatement {
class ReturnStatement final : public JumpStatement {
public:
+ enum Type { kNormal, kAsyncReturn };
Expression* expression() const { return expression_; }
void set_expression(Expression* e) { expression_ = e; }
+ Type type() const { return TypeField::decode(bit_field_); }
+ bool is_async_return() const { return type() == kAsyncReturn; }
private:
friend class AstNodeFactory;
- ReturnStatement(Expression* expression, int pos)
- : JumpStatement(pos, kReturnStatement), expression_(expression) {}
+ ReturnStatement(Expression* expression, Type type, int pos)
+ : JumpStatement(pos, kReturnStatement), expression_(expression) {
+ bit_field_ |= TypeField::encode(type);
+ }
Expression* expression_;
+
+ class TypeField
+ : public BitField<Type, JumpStatement::kNextBitFieldIndex, 1> {};
};
@@ -3217,7 +3225,13 @@ class AstNodeFactory final BASE_EMBEDDED {
}
ReturnStatement* NewReturnStatement(Expression* expression, int pos) {
- return new (zone_) ReturnStatement(expression, pos);
+ return new (zone_)
+ ReturnStatement(expression, ReturnStatement::kNormal, pos);
+ }
+
+ ReturnStatement* NewAsyncReturnStatement(Expression* expression, int pos) {
+ return new (zone_)
+ ReturnStatement(expression, ReturnStatement::kAsyncReturn, pos);
}
WithStatement* NewWithStatement(Scope* scope,
diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc
index b61bcdab55..27654c20f6 100644
--- a/deps/v8/src/ast/scopes.cc
+++ b/deps/v8/src/ast/scopes.cc
@@ -294,7 +294,7 @@ void DeclarationScope::SetDefaults() {
new_target_ = nullptr;
function_ = nullptr;
arguments_ = nullptr;
- this_function_ = nullptr;
+ rare_data_ = nullptr;
should_eager_compile_ = false;
was_lazily_parsed_ = false;
#ifdef DEBUG
@@ -672,7 +672,7 @@ void DeclarationScope::DeclareDefaultFunctionVariables(
if (IsConciseMethod(function_kind_) || IsClassConstructor(function_kind_) ||
IsAccessorFunction(function_kind_)) {
- this_function_ =
+ EnsureRareData()->this_function =
Declare(zone(), ast_value_factory->this_function_string(), CONST);
}
}
@@ -693,6 +693,24 @@ Variable* DeclarationScope::DeclareFunctionVar(const AstRawString* name) {
return function_;
}
+Variable* DeclarationScope::DeclareGeneratorObjectVar(
+ const AstRawString* name) {
+ DCHECK(is_function_scope() || is_module_scope());
+ DCHECK_NULL(generator_object_var());
+
+ Variable* result = EnsureRareData()->generator_object = NewTemporary(name);
+ result->set_is_used();
+ return result;
+}
+
+Variable* DeclarationScope::DeclarePromiseVar(const AstRawString* name) {
+ DCHECK(is_function_scope());
+ DCHECK_NULL(promise_var());
+ Variable* result = EnsureRareData()->promise = NewTemporary(name);
+ result->set_is_used();
+ return result;
+}
+
bool Scope::HasBeenRemoved() const {
if (sibling() == this) {
DCHECK_NULL(inner_scope_);
@@ -2053,9 +2071,8 @@ void DeclarationScope::AllocateLocals() {
new_target_ = nullptr;
}
- if (this_function_ != nullptr && !MustAllocate(this_function_)) {
- this_function_ = nullptr;
- }
+ NullifyRareVariableIf(RareVariable::kThisFunction,
+ [=](Variable* var) { return !MustAllocate(var); });
}
void ModuleScope::AllocateModuleVariables() {
diff --git a/deps/v8/src/ast/scopes.h b/deps/v8/src/ast/scopes.h
index 49cfdffba7..9e80e1c8e6 100644
--- a/deps/v8/src/ast/scopes.h
+++ b/deps/v8/src/ast/scopes.h
@@ -676,6 +676,11 @@ class DeclarationScope : public Scope {
// calls sloppy eval.
Variable* DeclareFunctionVar(const AstRawString* name);
+ // Declare some special internal variables which must be accessible to
+ // Ignition without ScopeInfo.
+ Variable* DeclareGeneratorObjectVar(const AstRawString* name);
+ Variable* DeclarePromiseVar(const AstRawString* name);
+
// Declare a parameter in this scope. When there are duplicated
// parameters the rightmost one 'wins'. However, the implementation
// expects all parameters to be declared and from left to right.
@@ -714,6 +719,17 @@ class DeclarationScope : public Scope {
return function_;
}
+ Variable* generator_object_var() const {
+ DCHECK(is_function_scope() || is_module_scope());
+ return GetRareVariable(RareVariable::kGeneratorObject);
+ }
+
+ Variable* promise_var() const {
+ DCHECK(is_function_scope());
+ DCHECK(IsAsyncFunction(function_kind_));
+ return GetRareVariable(RareVariable::kPromise);
+ }
+
// Parameters. The left-most parameter has index 0.
// Only valid for function and module scopes.
Variable* parameter(int index) const {
@@ -754,12 +770,14 @@ class DeclarationScope : public Scope {
}
Variable* this_function_var() const {
+ Variable* this_function = GetRareVariable(RareVariable::kThisFunction);
+
// This is only used in derived constructors atm.
- DCHECK(this_function_ == nullptr ||
+ DCHECK(this_function == nullptr ||
(is_function_scope() && (IsClassConstructor(function_kind()) ||
IsConciseMethod(function_kind()) ||
IsAccessorFunction(function_kind()))));
- return this_function_;
+ return this_function;
}
// Adds a local variable in this scope's locals list. This is for adjusting
@@ -867,8 +885,50 @@ class DeclarationScope : public Scope {
Variable* new_target_;
// Convenience variable; function scopes only.
Variable* arguments_;
- // Convenience variable; Subclass constructor only
- Variable* this_function_;
+
+ struct RareData : public ZoneObject {
+ void* operator new(size_t size, Zone* zone) { return zone->New(size); }
+
+ // Convenience variable; Subclass constructor only
+ Variable* this_function = nullptr;
+
+ // Generator object, if any; generator function scopes and module scopes
+ // only.
+ Variable* generator_object = nullptr;
+ // Promise, if any; async function scopes only.
+ Variable* promise = nullptr;
+ };
+
+ enum class RareVariable {
+ kThisFunction = offsetof(RareData, this_function),
+ kGeneratorObject = offsetof(RareData, generator_object),
+ kPromise = offsetof(RareData, promise)
+ };
+
+ V8_INLINE RareData* EnsureRareData() {
+ if (rare_data_ == nullptr) {
+ rare_data_ = new (zone_) RareData;
+ }
+ return rare_data_;
+ }
+
+ V8_INLINE Variable* GetRareVariable(RareVariable id) const {
+ if (rare_data_ == nullptr) return nullptr;
+ return *reinterpret_cast<Variable**>(
+ reinterpret_cast<uint8_t*>(rare_data_) + static_cast<ptrdiff_t>(id));
+ }
+
+ // Set `var` to null if it's non-null and Predicate (Variable*) -> bool
+ // returns true.
+ template <typename Predicate>
+ V8_INLINE void NullifyRareVariableIf(RareVariable id, Predicate predicate) {
+ if (V8_LIKELY(rare_data_ == nullptr)) return;
+ Variable** var = reinterpret_cast<Variable**>(
+ reinterpret_cast<uint8_t*>(rare_data_) + static_cast<ptrdiff_t>(id));
+ if (*var && predicate(*var)) *var = nullptr;
+ }
+
+ RareData* rare_data_ = nullptr;
};
class ModuleScope final : public DeclarationScope {
diff --git a/deps/v8/src/interpreter/bytecode-generator.cc b/deps/v8/src/interpreter/bytecode-generator.cc
index 02f6c3bb2c..72c5b925c8 100644
--- a/deps/v8/src/interpreter/bytecode-generator.cc
+++ b/deps/v8/src/interpreter/bytecode-generator.cc
@@ -106,12 +106,19 @@ class BytecodeGenerator::ControlScope BASE_EMBEDDED {
void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); }
void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); }
void ReturnAccumulator() { PerformCommand(CMD_RETURN, nullptr); }
+ void AsyncReturnAccumulator() { PerformCommand(CMD_ASYNC_RETURN, nullptr); }
void ReThrowAccumulator() { PerformCommand(CMD_RETHROW, nullptr); }
class DeferredCommands;
protected:
- enum Command { CMD_BREAK, CMD_CONTINUE, CMD_RETURN, CMD_RETHROW };
+ enum Command {
+ CMD_BREAK,
+ CMD_CONTINUE,
+ CMD_RETURN,
+ CMD_ASYNC_RETURN,
+ CMD_RETHROW
+ };
void PerformCommand(Command command, Statement* statement);
virtual bool Execute(Command command, Statement* statement) = 0;
@@ -221,6 +228,9 @@ class BytecodeGenerator::ControlScopeForTopLevel final
case CMD_RETURN:
generator()->BuildReturn();
return true;
+ case CMD_ASYNC_RETURN:
+ generator()->BuildAsyncReturn();
+ return true;
case CMD_RETHROW:
generator()->BuildReThrow();
return true;
@@ -249,6 +259,7 @@ class BytecodeGenerator::ControlScopeForBreakable final
return true;
case CMD_CONTINUE:
case CMD_RETURN:
+ case CMD_ASYNC_RETURN:
case CMD_RETHROW:
break;
}
@@ -286,6 +297,7 @@ class BytecodeGenerator::ControlScopeForIteration final
loop_builder_->Continue();
return true;
case CMD_RETURN:
+ case CMD_ASYNC_RETURN:
case CMD_RETHROW:
break;
}
@@ -311,6 +323,7 @@ class BytecodeGenerator::ControlScopeForTryCatch final
case CMD_BREAK:
case CMD_CONTINUE:
case CMD_RETURN:
+ case CMD_ASYNC_RETURN:
break;
case CMD_RETHROW:
generator()->BuildReThrow();
@@ -337,6 +350,7 @@ class BytecodeGenerator::ControlScopeForTryFinally final
case CMD_BREAK:
case CMD_CONTINUE:
case CMD_RETURN:
+ case CMD_ASYNC_RETURN:
case CMD_RETHROW:
commands_->RecordCommand(command, statement);
try_finally_builder_->LeaveTry();
@@ -1045,7 +1059,12 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
builder()->SetStatementPosition(stmt);
VisitForAccumulatorValue(stmt->expression());
- execution_control()->ReturnAccumulator();
+
+ if (stmt->is_async_return()) {
+ execution_control()->AsyncReturnAccumulator();
+ } else {
+ execution_control()->ReturnAccumulator();
+ }
}
void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
@@ -1982,6 +2001,28 @@ void BytecodeGenerator::BuildReturn() {
builder()->Return();
}
+void BytecodeGenerator::BuildAsyncReturn() {
+ DCHECK(IsAsyncFunction(info()->literal()->kind()));
+ RegisterAllocationScope register_scope(this);
+ RegisterList args = register_allocator()->NewRegisterList(3);
+ Register receiver = args[0];
+ Register promise = args[1];
+ Register return_value = args[2];
+ builder()->StoreAccumulatorInRegister(return_value);
+
+ Variable* var_promise = scope()->promise_var();
+ DCHECK_NOT_NULL(var_promise);
+ BuildVariableLoad(var_promise, FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+ builder()
+ ->StoreAccumulatorInRegister(promise)
+ .LoadUndefined()
+ .StoreAccumulatorInRegister(receiver)
+ .CallJSRuntime(Context::PROMISE_RESOLVE_INDEX, args)
+ .LoadAccumulatorWithRegister(promise);
+ BuildReturn();
+}
+
void BytecodeGenerator::BuildReThrow() { builder()->ReThrow(); }
void BytecodeGenerator::BuildAbort(BailoutReason bailout_reason) {
diff --git a/deps/v8/src/interpreter/bytecode-generator.h b/deps/v8/src/interpreter/bytecode-generator.h
index f15829dea8..dcdba99e2f 100644
--- a/deps/v8/src/interpreter/bytecode-generator.h
+++ b/deps/v8/src/interpreter/bytecode-generator.h
@@ -106,6 +106,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
HoleCheckMode hole_check_mode);
void BuildReturn();
+ void BuildAsyncReturn();
void BuildReThrow();
void BuildAbort(BailoutReason bailout_reason);
void BuildThrowIfHole(Handle<String> name);
diff --git a/deps/v8/src/parsing/parser-base.h b/deps/v8/src/parsing/parser-base.h
index f7fdb26d22..dba263390e 100644
--- a/deps/v8/src/parsing/parser-base.h
+++ b/deps/v8/src/parsing/parser-base.h
@@ -419,23 +419,12 @@ class ParserBase {
FunctionKind kind() const { return scope()->function_kind(); }
FunctionState* outer() const { return outer_function_state_; }
- void set_generator_object_variable(typename Types::Variable* variable) {
- DCHECK_NOT_NULL(variable);
- DCHECK(IsResumableFunction(kind()));
- DCHECK(scope()->has_forced_context_allocation());
- generator_object_variable_ = variable;
- }
typename Types::Variable* generator_object_variable() const {
- return generator_object_variable_;
+ return scope()->generator_object_var();
}
- void set_promise_variable(typename Types::Variable* variable) {
- DCHECK(variable != NULL);
- DCHECK(IsAsyncFunction(kind()));
- promise_variable_ = variable;
- }
typename Types::Variable* promise_variable() const {
- return promise_variable_;
+ return scope()->promise_var();
}
const ZoneList<DestructuringAssignment>&
@@ -1336,6 +1325,15 @@ class ParserBase {
return Call::NOT_EVAL;
}
+ // Convenience method which determines the type of return statement to emit
+ // depending on the current function type.
+ inline StatementT BuildReturnStatement(ExpressionT expr, int pos) {
+ if (V8_UNLIKELY(is_async_function())) {
+ return factory()->NewAsyncReturnStatement(expr, pos);
+ }
+ return factory()->NewReturnStatement(expr, pos);
+ }
+
// Validation per ES6 object literals.
class ObjectLiteralChecker {
public:
@@ -4074,9 +4072,8 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
} else {
ExpressionT expression = ParseAssignmentExpression(accept_IN, CHECK_OK);
impl()->RewriteNonPattern(CHECK_OK);
- body->Add(
- factory()->NewReturnStatement(expression, expression->position()),
- zone());
+ body->Add(BuildReturnStatement(expression, expression->position()),
+ zone());
if (allow_tailcalls() && !is_sloppy(language_mode())) {
// ES6 14.6.1 Static Semantics: IsInTailPosition
impl()->MarkTailPosition(expression);
@@ -4981,7 +4978,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement(
}
ExpectSemicolon(CHECK_OK);
return_value = impl()->RewriteReturn(return_value, loc.beg_pos);
- return factory()->NewReturnStatement(return_value, loc.beg_pos);
+ return BuildReturnStatement(return_value, loc.beg_pos);
}
template <typename Impl>
diff --git a/deps/v8/src/parsing/parser.cc b/deps/v8/src/parsing/parser.cc
index bcafd78853..685fe1d6ea 100644
--- a/deps/v8/src/parsing/parser.cc
+++ b/deps/v8/src/parsing/parser.cc
@@ -1606,8 +1606,6 @@ Expression* Parser::RewriteReturn(Expression* return_value, int pos) {
}
if (is_generator()) {
return_value = BuildIteratorResult(return_value, true);
- } else if (is_async_function()) {
- return_value = BuildResolvePromise(return_value, return_value->position());
}
return return_value;
}
@@ -2426,9 +2424,8 @@ void Parser::PrepareGeneratorVariables() {
// Calling a generator returns a generator object. That object is stored
// in a temporary variable, a definition that is used by "yield"
// expressions.
- Variable* temp =
- NewTemporary(ast_value_factory()->dot_generator_object_string());
- function_state_->set_generator_object_variable(temp);
+ function_state_->scope()->DeclareGeneratorObjectVar(
+ ast_value_factory()->dot_generator_object_string());
}
FunctionLiteral* Parser::ParseFunctionLiteral(
@@ -3042,8 +3039,8 @@ Variable* Parser::PromiseVariable() {
// comes first should create it and stash it in the FunctionState.
Variable* promise = function_state_->promise_variable();
if (function_state_->promise_variable() == nullptr) {
- promise = scope()->NewTemporary(ast_value_factory()->empty_string());
- function_state_->set_promise_variable(promise);
+ promise = function_state_->scope()->DeclarePromiseVar(
+ ast_value_factory()->empty_string());
}
return promise;
}
diff --git a/deps/v8/src/parsing/preparser.h b/deps/v8/src/parsing/preparser.h
index 77fe061f42..65c482c530 100644
--- a/deps/v8/src/parsing/preparser.h
+++ b/deps/v8/src/parsing/preparser.h
@@ -670,6 +670,10 @@ class PreParserFactory {
int pos) {
return PreParserStatement::Jump();
}
+ PreParserStatement NewAsyncReturnStatement(PreParserExpression expression,
+ int pos) {
+ return PreParserStatement::Jump();
+ }
PreParserExpression NewFunctionLiteral(
PreParserIdentifier name, Scope* scope, PreParserStatementList body,
int materialized_literal_count, int expected_property_count,
diff --git a/deps/v8/test/mjsunit/es8/async-function-try-finally.js b/deps/v8/test/mjsunit/es8/async-function-try-finally.js
new file mode 100644
index 0000000000..9ba07eb427
--- /dev/null
+++ b/deps/v8/test/mjsunit/es8/async-function-try-finally.js
@@ -0,0 +1,201 @@
+// 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: --allow-natives-syntax
+
+function assertThrowsAsync(run, errorType, message) {
+ var actual;
+ var hadValue = false;
+ var hadError = false;
+ var promise = run();
+
+ if (typeof promise !== "object" || typeof promise.then !== "function") {
+ throw new MjsUnitAssertionError(
+ "Expected " + run.toString() +
+ " to return a Promise, but it returned " + PrettyPrint(promise));
+ }
+
+ promise.then(function(value) { hadValue = true; actual = value; },
+ function(error) { hadError = true; actual = error; });
+
+ assertFalse(hadValue || hadError);
+
+ %RunMicrotasks();
+
+ if (!hadError) {
+ throw new MjsUnitAssertionError(
+ "Expected " + run + "() to throw " + errorType.name +
+ ", but did not throw.");
+ }
+ if (!(actual instanceof errorType))
+ throw new MjsUnitAssertionError(
+ "Expected " + run + "() to throw " + errorType.name +
+ ", but threw '" + actual + "'");
+ if (message !== void 0 && actual.message !== message)
+ throw new MjsUnitAssertionError(
+ "Expected " + run + "() to throw '" + message + "', but threw '" +
+ actual.message + "'");
+};
+
+function assertEqualsAsync(expected, run, msg) {
+ var actual;
+ var hadValue = false;
+ var hadError = false;
+ var promise = run();
+
+ if (typeof promise !== "object" || typeof promise.then !== "function") {
+ throw new MjsUnitAssertionError(
+ "Expected " + run.toString() +
+ " to return a Promise, but it returned " + PrettyPrint(promise));
+ }
+
+ promise.then(function(value) { hadValue = true; actual = value; },
+ function(error) { hadError = true; actual = error; });
+
+ assertFalse(hadValue || hadError);
+
+ %RunMicrotasks();
+
+ if (hadError) throw actual;
+
+ assertTrue(
+ hadValue, "Expected '" + run.toString() + "' to produce a value");
+
+ assertEquals(expected, actual, msg);
+};
+
+function resolveLater(value) {
+ return new Promise(function(resolve) {
+ resolve(value);
+ });
+}
+
+function rejectLater(reason) {
+ return new Promise(function(resolve, reject) {
+ reject(reason);
+ });
+}
+
+class MyError extends Error {};
+var AsyncFunction = async function() {}.constructor;
+
+assertEqualsAsync("finally-return (func-expr)", async function() {
+ try {
+ return "early-return (func-expr)";
+ } finally {
+ return "finally-return (func-expr)";
+ }
+});
+
+assertEqualsAsync("finally-return (arrow)", async() => {
+ try {
+ return "early-return (arrow)";
+ } finally {
+ return "finally-return (arrow)";
+ }
+});
+
+assertEqualsAsync("finally-return (eval)", AsyncFunction(`
+ try {
+ return "early-return (eval)";
+ } finally {
+ return "finally-return (eval)";
+ }
+`));
+
+assertEqualsAsync("promise-finally-return (func-expr)", async function() {
+ try {
+ return new Promise(function() {});
+ } finally {
+ return "promise-finally-return (func-expr)";
+ }
+});
+
+assertEqualsAsync("promise-finally-return (arrow)", async() => {
+ try {
+ return new Promise(function() {});
+ } finally {
+ return "promise-finally-return (arrow)";
+ }
+});
+
+assertEqualsAsync("promise-finally-return (eval)", AsyncFunction(`
+ try {
+ return new Promise(function() {});
+ } finally {
+ return "promise-finally-return (eval)";
+ }
+`));
+
+assertEqualsAsync("await-finally-return (func-expr)", async function() {
+ try {
+ return "early-return (func-expr)";
+ } finally {
+ return await resolveLater("await-finally-return (func-expr)");
+ }
+});
+
+assertEqualsAsync("await-finally-return (arrow)", async() => {
+ try {
+ return "early-return (arrow)";
+ } finally {
+ return await resolveLater("await-finally-return (arrow)");
+ }
+});
+
+assertEqualsAsync("await-finally-return (eval)", AsyncFunction(`
+ try {
+ return "early-return (eval)";
+ } finally {
+ return await resolveLater("await-finally-return (eval)");
+ }
+`));
+
+assertThrowsAsync(async function() {
+ try {
+ return "early-return (func-expr)";
+ } finally {
+ throw new MyError("finally-throw (func-expr)");
+ }
+}, MyError, "finally-throw (func-expr)");
+
+assertThrowsAsync(async() => {
+ try {
+ return "early-return (arrow)";
+ } finally {
+ throw new MyError("finally-throw (arrow)");
+ }
+}, MyError, "finally-throw (arrow)");
+
+assertThrowsAsync(AsyncFunction(`
+ try {
+ return "early-return (eval)";
+ } finally {
+ throw new MyError("finally-throw (eval)");
+ }
+`), MyError, "finally-throw (eval)");
+
+assertThrowsAsync(async function() {
+ try {
+ return "early-return (func-expr)";
+ } finally {
+ await rejectLater(new MyError("await-finally-throw (func-expr)"));
+ }
+}, MyError, "await-finally-throw (func-expr)");
+
+assertThrowsAsync(async() => {
+ try {
+ return "early-return (arrow)";
+ } finally {
+ await rejectLater(new MyError("await-finally-throw (arrow)"));
+ }
+}, MyError, "await-finally-throw (arrow)");
+
+assertThrowsAsync(AsyncFunction(`
+ try {
+ return "early-return (eval)";
+ } finally {
+ await rejectLater(new MyError("await-finally-throw (eval)"));
+ }
+`), MyError, "await-finally-throw (eval)");