Reland "[torque]: Implement catch handlers for try blocks"

This is a reland of 0f15ed05b9

Original change's description:
> [torque]: Implement catch handlers for try blocks
> 
> In addition (and in combination), try statements now support "catch"
> clauses at the end that catch JavaScript exceptions throw by any builtin
> or runtime function contained in the try block:
> 
>   try {
>     ThrowTypeError(context, ...);
>   }
>   catch (e) {
>     // e has type Object
>   }
> 
> Bug: v8:7793
> Change-Id: Ie285ff888c49c112276240f7360f70c8b540ed19
> Reviewed-on: https://chromium-review.googlesource.com/c/1302055
> Commit-Queue: Daniel Clifford <danno@chromium.org>
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#57169}

Bug: v8:7793
Change-Id: I3c4182303acfdfa625654976bec372cf531d954f
Reviewed-on: https://chromium-review.googlesource.com/c/1310295
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Daniel Clifford <danno@chromium.org>
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57184}
This commit is contained in:
Daniel Clifford 2018-10-31 14:00:51 +01:00 committed by Commit Bot
parent 54cc05553b
commit 27dc9fa5ca
17 changed files with 441 additions and 41 deletions

View File

@ -1083,6 +1083,8 @@ void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
return;
}
// No catch handlers should be active if we're using catch labels
DCHECK_EQ(state()->exception_handler_labels_.size(), 0);
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
Label success(this), exception(this, Label::kDeferred);
@ -1102,6 +1104,29 @@ void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
Bind(&success);
}
void CodeAssembler::HandleException(Node* node) {
if (state_->exception_handler_labels_.size() == 0) return;
CodeAssemblerExceptionHandlerLabel* label =
state_->exception_handler_labels_.back();
if (node->op()->HasProperty(Operator::kNoThrow)) {
return;
}
Label success(this), exception(this, Label::kDeferred);
success.MergeVariables();
exception.MergeVariables();
raw_assembler()->Continuations(node, success.label_, exception.label_);
Bind(&exception);
const Operator* op = raw_assembler()->common()->IfException();
Node* exception_value = raw_assembler()->AddNode(op, node, node);
label->AddInputs({UncheckedCast<Object>(exception_value)});
Goto(label->plain_label());
Bind(&success);
}
namespace {
template <size_t kMaxSize>
class NodeArray {
@ -1152,6 +1177,7 @@ TNode<Object> CodeAssembler::CallRuntimeWithCEntryImpl(
CallPrologue();
Node* return_value =
raw_assembler()->CallN(call_descriptor, inputs.size(), inputs.data());
HandleException(return_value);
CallEpilogue();
return UncheckedCast<Object>(return_value);
}
@ -1207,6 +1233,7 @@ Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor,
CallPrologue();
Node* return_value =
raw_assembler()->CallN(call_descriptor, input_count, inputs);
HandleException(return_value);
CallEpilogue();
return return_value;
}
@ -1503,6 +1530,10 @@ Factory* CodeAssembler::factory() const { return isolate()->factory(); }
Zone* CodeAssembler::zone() const { return raw_assembler()->zone(); }
bool CodeAssembler::IsExceptionHandlerActive() const {
return state_->exception_handler_labels_.size() != 0;
}
RawMachineAssembler* CodeAssembler::raw_assembler() const {
return state_->raw_assembler_.get();
}
@ -1810,6 +1841,15 @@ const std::vector<Node*>& CodeAssemblerParameterizedLabelBase::CreatePhis(
return phi_nodes_;
}
void CodeAssemblerState::PushExceptionHandler(
CodeAssemblerExceptionHandlerLabel* label) {
exception_handler_labels_.push_back(label);
}
void CodeAssemblerState::PopExceptionHandler() {
exception_handler_labels_.pop_back();
}
} // namespace compiler
Smi* CheckObjectType(Object* value, Smi* type, String* location) {

View File

@ -451,6 +451,9 @@ class SloppyTNode : public TNode<T> {
: TNode<T>(other) {}
};
template <class... Types>
class CodeAssemblerParameterizedLabel;
// This macro alias allows to use PairT<T1, T2> as a macro argument.
#define PAIR_TYPE(T1, T2) PairT<T1, T2>
@ -807,6 +810,15 @@ class V8_EXPORT_PRIVATE CodeAssembler {
void Branch(SloppyTNode<IntegralT> condition, Label* true_label,
Label* false_label);
template <class... Ts>
using PLabel = compiler::CodeAssemblerParameterizedLabel<Ts...>;
template <class... T, class... Args>
void Goto(PLabel<T...>* label, Args... args) {
label->AddInputs(args...);
Goto(label->plain_label());
}
void Branch(TNode<BoolT> condition, const std::function<void()>& true_body,
const std::function<void()>& false_body);
void Branch(TNode<BoolT> condition, Label* true_label,
@ -1269,6 +1281,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
bool UnalignedLoadSupported(MachineRepresentation rep) const;
bool UnalignedStoreSupported(MachineRepresentation rep) const;
bool IsExceptionHandlerActive() const;
protected:
void RegisterCallGenerationCallbacks(
const CodeAssemblerCallback& call_prologue,
@ -1281,6 +1295,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
bool IsJSFunctionCall() const;
private:
void HandleException(Node* result);
TNode<Object> CallRuntimeImpl(Runtime::FunctionId function,
TNode<Object> context,
std::initializer_list<TNode<Object>> args);
@ -1482,6 +1498,7 @@ class CodeAssemblerParameterizedLabel
private:
friend class internal::TorqueAssembler;
friend class CodeAssembler;
void AddInputs(TNode<Types>... inputs) {
CodeAssemblerParameterizedLabelBase::AddInputs(
@ -1501,6 +1518,9 @@ class CodeAssemblerParameterizedLabel
}
};
typedef CodeAssemblerParameterizedLabel<Object>
CodeAssemblerExceptionHandlerLabel;
class V8_EXPORT_PRIVATE CodeAssemblerState {
public:
// Create with CallStub linkage.
@ -1535,12 +1555,16 @@ class V8_EXPORT_PRIVATE CodeAssemblerState {
friend class CodeAssemblerVariable;
friend class CodeAssemblerTester;
friend class CodeAssemblerParameterizedLabelBase;
friend class CodeAssemblerScopedExceptionHandler;
CodeAssemblerState(Isolate* isolate, Zone* zone,
CallDescriptor* call_descriptor, Code::Kind kind,
const char* name, PoisoningMitigationLevel poisoning_level,
uint32_t stub_key, int32_t builtin_index);
void PushExceptionHandler(CodeAssemblerExceptionHandlerLabel* label);
void PopExceptionHandler();
std::unique_ptr<RawMachineAssembler> raw_assembler_;
Code::Kind kind_;
const char* name_;
@ -1550,10 +1574,27 @@ class V8_EXPORT_PRIVATE CodeAssemblerState {
ZoneSet<CodeAssemblerVariable::Impl*> variables_;
CodeAssemblerCallback call_prologue_;
CodeAssemblerCallback call_epilogue_;
std::vector<CodeAssemblerExceptionHandlerLabel*> exception_handler_labels_;
DISALLOW_COPY_AND_ASSIGN(CodeAssemblerState);
};
class CodeAssemblerScopedExceptionHandler {
public:
CodeAssemblerScopedExceptionHandler(CodeAssembler* assembler,
CodeAssemblerExceptionHandlerLabel* label)
: assembler_(assembler) {
assembler_->state()->PushExceptionHandler(label);
}
~CodeAssemblerScopedExceptionHandler() {
assembler_->state()->PopExceptionHandler();
}
private:
CodeAssembler* assembler_;
};
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -28,12 +28,6 @@ class TorqueAssembler : public CodeStubAssembler {
return {};
}
template <class... T, class... Args>
void Goto(PLabel<T...>* label, Args... args) {
label->AddInputs(args...);
CodeStubAssembler::Goto(label->plain_label());
}
using CodeStubAssembler::Goto;
template <class... T>
void Bind(PLabel<T...>* label, TNode<T>*... phis) {
Bind(label->plain_label());

View File

@ -552,7 +552,7 @@ struct ForOfLoopStatement : Statement {
struct LabelBlock : AstNode {
DEFINE_AST_NODE_LEAF_BOILERPLATE(LabelBlock)
LabelBlock(SourcePosition pos, const std::string& label,
LabelBlock(SourcePosition pos, std::string label,
const ParameterList& parameters, Statement* body)
: AstNode(kKind, pos),
label(std::move(label)),
@ -572,11 +572,13 @@ struct StatementExpression : Expression {
struct TryLabelExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(TryLabelExpression)
TryLabelExpression(SourcePosition pos, Expression* try_expression,
LabelBlock* label_block)
TryLabelExpression(SourcePosition pos, bool catch_exceptions,
Expression* try_expression, LabelBlock* label_block)
: Expression(kKind, pos),
catch_exceptions(catch_exceptions),
try_expression(try_expression),
label_block(label_block) {}
bool catch_exceptions;
Expression* try_expression;
LabelBlock* label_block;
};

View File

@ -18,6 +18,7 @@ void Block::SetInputTypes(const Stack<const Type*>& input_types) {
return;
}
DCHECK_EQ(input_types.Size(), input_types_->Size());
Stack<const Type*> merged_types;
auto c2_iterator = input_types.begin();
for (const Type* c1 : *input_types_) {

View File

@ -139,11 +139,35 @@ class CfgAssembler {
void PrintCurrentStack(std::ostream& s) { s << "stack: " << current_stack_; }
private:
friend class CfgAssemblerScopedTemporaryBlock;
Stack<const Type*> current_stack_;
ControlFlowGraph cfg_;
Block* current_block_ = cfg_.start();
};
class CfgAssemblerScopedTemporaryBlock {
public:
CfgAssemblerScopedTemporaryBlock(CfgAssembler* assembler, Block* block)
: assembler_(assembler), saved_block_(block) {
saved_stack_ = block->InputTypes();
DCHECK(!assembler->CurrentBlockIsComplete());
std::swap(saved_block_, assembler->current_block_);
std::swap(saved_stack_, assembler->current_stack_);
assembler->cfg_.PlaceBlock(block);
}
~CfgAssemblerScopedTemporaryBlock() {
DCHECK(assembler_->CurrentBlockIsComplete());
std::swap(saved_block_, assembler_->current_block_);
std::swap(saved_stack_, assembler_->current_stack_);
}
private:
CfgAssembler* assembler_;
Stack<const Type*> saved_stack_;
Block* saved_block_;
};
} // namespace torque
} // namespace internal
} // namespace v8

View File

@ -150,6 +150,7 @@ void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
}
std::reverse(args.begin(), args.end());
Stack<std::string> pre_call_stack = *stack;
const Type* return_type = instruction.macro->signature().return_type;
std::vector<std::string> results;
for (const Type* type : LowerType(return_type)) {
@ -159,6 +160,8 @@ void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
<< stack->Top() << ";\n";
out_ << " USE(" << stack->Top() << ");\n";
}
std::string catch_name =
PreCallableExceptionPreparation(instruction.catch_block);
out_ << " ";
if (return_type->IsStructType()) {
out_ << "std::tie(";
@ -178,6 +181,8 @@ void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
if (results.size() == 1) out_ << ")";
out_ << ");\n";
}
PostCallableExceptionPreparation(catch_name, return_type,
instruction.catch_block, &pre_call_stack);
}
void CSAGenerator::EmitInstruction(
@ -205,6 +210,7 @@ void CSAGenerator::EmitInstruction(
}
std::reverse(args.begin(), args.end());
Stack<std::string> pre_call_stack = *stack;
std::vector<std::string> results;
const Type* return_type = instruction.macro->signature().return_type;
if (return_type != TypeOracle::GetNeverType()) {
@ -235,6 +241,8 @@ void CSAGenerator::EmitInstruction(
out_ << " Label " << label_names[i] << "(this);\n";
}
std::string catch_name =
PreCallableExceptionPreparation(instruction.catch_block);
out_ << " ";
if (results.size() == 1) {
out_ << results[0] << " = ";
@ -259,6 +267,10 @@ void CSAGenerator::EmitInstruction(
} else {
out_ << ");\n";
}
PostCallableExceptionPreparation(catch_name, return_type,
instruction.catch_block, &pre_call_stack);
if (instruction.return_continuation) {
out_ << " Goto(&" << BlockName(*instruction.return_continuation);
for (const std::string& value : *stack) {
@ -296,16 +308,24 @@ void CSAGenerator::EmitInstruction(const CallBuiltinInstruction& instruction,
PrintCommaSeparatedList(out_, arguments);
out_ << ");\n";
} else {
std::string result_name = FreshNodeName();
if (result_types.size() == 1) {
out_ << " TNode<" << result_types[0]->GetGeneratedTNodeTypeName()
<< "> " << result_name << ";\n";
}
std::string catch_name =
PreCallableExceptionPreparation(instruction.catch_block);
Stack<std::string> pre_call_stack = *stack;
if (result_types.size() == 1) {
std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
stack->Push(FreshNodeName());
out_ << " TNode<" << generated_type << "> " << stack->Top() << " = ";
stack->Push(result_name);
out_ << " " << result_name << " = ";
if (generated_type != "Object") out_ << "CAST(";
out_ << "CallBuiltin(Builtins::k" << instruction.builtin->name() << ", ";
PrintCommaSeparatedList(out_, arguments);
if (generated_type != "Object") out_ << ")";
out_ << ");\n";
out_ << " USE(" << stack->Top() << ");\n";
out_ << " USE(" << result_name << ");\n";
} else {
DCHECK_EQ(0, result_types.size());
// TODO(tebbi): Actually, builtins have to return a value, so we should
@ -315,6 +335,10 @@ void CSAGenerator::EmitInstruction(const CallBuiltinInstruction& instruction,
PrintCommaSeparatedList(out_, arguments);
out_ << ");\n";
}
PostCallableExceptionPreparation(
catch_name,
result_types.size() == 0 ? TypeOracle::GetVoidType() : result_types[0],
instruction.catch_block, &pre_call_stack);
}
}
@ -348,6 +372,44 @@ void CSAGenerator::EmitInstruction(
}
}
std::string CSAGenerator::PreCallableExceptionPreparation(
base::Optional<Block*> catch_block) {
std::string catch_name;
if (catch_block) {
catch_name = FreshCatchName();
out_ << " CatchLabel " << catch_name
<< "_label(this, compiler::CodeAssemblerLabel::kDeferred);\n";
out_ << " { ScopedCatch s(this, &" << catch_name << "_label);\n";
}
return catch_name;
}
void CSAGenerator::PostCallableExceptionPreparation(
const std::string& catch_name, const Type* return_type,
base::Optional<Block*> catch_block, Stack<std::string>* stack) {
if (catch_block) {
std::string block_name = BlockName(*catch_block);
out_ << " }\n";
out_ << " if (" << catch_name << "_label.is_used()) {\n";
out_ << " Label " << catch_name << "_skip(this);\n";
if (!return_type->IsNever()) {
out_ << " Goto(&" << catch_name << "_skip);\n";
}
out_ << " TNode<Object> " << catch_name << "_exception_object;\n";
out_ << " Bind(&" << catch_name << "_label, &" << catch_name
<< "_exception_object);\n";
out_ << " Goto(&" << block_name;
for (size_t i = 0; i < stack->Size(); ++i) {
out_ << ", " << stack->begin()[i];
}
out_ << ", " << catch_name << "_exception_object);\n";
if (!return_type->IsNever()) {
out_ << " Bind(&" << catch_name << "_skip);\n";
}
out_ << " }\n";
}
}
void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
Stack<std::string>* stack) {
std::vector<std::string> arguments = stack->PopMany(instruction.argc);
@ -366,14 +428,21 @@ void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
PrintCommaSeparatedList(out_, arguments);
out_ << ");\n";
} else {
std::string result_name = FreshNodeName();
if (result_types.size() == 1) {
stack->Push(FreshNodeName());
out_ << " TNode<" << result_types[0]->GetGeneratedTNodeTypeName()
<< "> " << stack->Top() << " = CAST(CallRuntime(Runtime::k"
<< "> " << result_name << ";\n";
}
std::string catch_name =
PreCallableExceptionPreparation(instruction.catch_block);
Stack<std::string> pre_call_stack = *stack;
if (result_types.size() == 1) {
stack->Push(result_name);
out_ << " " << result_name << " = CAST(CallRuntime(Runtime::k"
<< instruction.runtime_function->name() << ", ";
PrintCommaSeparatedList(out_, arguments);
out_ << "));\n";
out_ << " USE(" << stack->Top() << ");\n";
out_ << " USE(" << result_name << ");\n";
} else {
DCHECK_EQ(0, result_types.size());
out_ << " CallRuntime(Runtime::k"
@ -386,6 +455,8 @@ void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
DCHECK(return_type == TypeOracle::GetVoidType());
}
}
PostCallableExceptionPreparation(catch_name, return_type,
instruction.catch_block, &pre_call_stack);
}
}

View File

@ -32,7 +32,15 @@ class CSAGenerator {
size_t fresh_id_ = 0;
base::Optional<Builtin::Kind> linkage_;
std::string PreCallableExceptionPreparation(
base::Optional<Block*> catch_block);
void PostCallableExceptionPreparation(const std::string& catch_name,
const Type* return_type,
base::Optional<Block*> catch_block,
Stack<std::string>* stack);
std::string FreshNodeName() { return "tmp" + std::to_string(fresh_id_++); }
std::string FreshCatchName() { return "catch" + std::to_string(fresh_id_++); }
std::string BlockName(const Block* block) {
return "block" + std::to_string(block->id());
}

View File

@ -93,10 +93,13 @@ void ImplementationVisitor::BeginModuleFile(Module* module) {
source << "#include \"builtins-" + DashifyString(module->name()) +
"-from-dsl-gen.h\"\n\n";
source << "namespace v8 {\n"
source
<< "namespace v8 {\n"
<< "namespace internal {\n"
<< "\n"
<< "using Node = compiler::Node;\n"
<< "using CatchLabel = compiler::CodeAssemblerExceptionHandlerLabel;\n"
<< "using ScopedCatch = compiler::CodeAssemblerScopedExceptionHandler;\n"
<< "\n";
std::string upper_name(module->name());
@ -1774,8 +1777,10 @@ VisitResult ImplementationVisitor::GenerateCall(
}
if (auto* builtin = Builtin::DynamicCast(callable)) {
assembler().Emit(
CallBuiltinInstruction{is_tailcall, builtin, argument_range.Size()});
base::Optional<Block*> catch_block = GetCatchBlock();
assembler().Emit(CallBuiltinInstruction{
is_tailcall, builtin, argument_range.Size(), catch_block});
GenerateCatchBlock(catch_block);
if (is_tailcall) {
return VisitResult::NeverResult();
} else {
@ -1806,7 +1811,10 @@ VisitResult ImplementationVisitor::GenerateCall(
return VisitResult(return_type, result.str());
} else if (arguments.labels.empty() &&
return_type != TypeOracle::GetNeverType()) {
assembler().Emit(CallCsaMacroInstruction{macro, constexpr_arguments});
base::Optional<Block*> catch_block = GetCatchBlock();
assembler().Emit(
CallCsaMacroInstruction{macro, constexpr_arguments, catch_block});
GenerateCatchBlock(catch_block);
size_t return_slot_count = LoweredSlotCount(return_type);
return VisitResult(return_type, assembler().TopRange(return_slot_count));
} else {
@ -1820,9 +1828,11 @@ VisitResult ImplementationVisitor::GenerateCall(
for (size_t i = 0; i < label_count; ++i) {
label_blocks.push_back(assembler().NewBlock());
}
base::Optional<Block*> catch_block = GetCatchBlock();
assembler().Emit(CallCsaMacroAndBranchInstruction{
macro, constexpr_arguments, return_continuation, label_blocks});
macro, constexpr_arguments, return_continuation, label_blocks,
catch_block});
GenerateCatchBlock(catch_block);
for (size_t i = 0; i < label_count; ++i) {
Binding<LocalLabel>* label = arguments.labels[i];
@ -1862,8 +1872,10 @@ VisitResult ImplementationVisitor::GenerateCall(
}
}
} else if (auto* runtime_function = RuntimeFunction::DynamicCast(callable)) {
assembler().Emit(CallRuntimeInstruction{is_tailcall, runtime_function,
argument_range.Size()});
base::Optional<Block*> catch_block = GetCatchBlock();
assembler().Emit(CallRuntimeInstruction{
is_tailcall, runtime_function, argument_range.Size(), catch_block});
GenerateCatchBlock(catch_block);
if (is_tailcall || return_type == TypeOracle::GetNeverType()) {
return VisitResult::NeverResult();
} else {
@ -2061,6 +2073,30 @@ bool IsCompatibleSignature(const Signature& sig, const TypeVector& types,
return true;
}
base::Optional<Block*> ImplementationVisitor::GetCatchBlock() {
base::Optional<Block*> catch_block;
if (base::Optional<Binding<LocalLabel>*> catch_handler =
TryLookupLabel("_catch")) {
catch_block = assembler().NewBlock(base::nullopt, true);
}
return catch_block;
}
void ImplementationVisitor::GenerateCatchBlock(
base::Optional<Block*> catch_block) {
if (catch_block) {
base::Optional<Binding<LocalLabel>*> catch_handler =
TryLookupLabel("_catch");
if (assembler().CurrentBlockIsComplete()) {
assembler().Bind(*catch_block);
assembler().Goto((*catch_handler)->block, 1);
} else {
CfgAssemblerScopedTemporaryBlock temp(&assembler(), *catch_block);
assembler().Goto((*catch_handler)->block, 1);
}
}
}
} // namespace torque
} // namespace internal
} // namespace v8

View File

@ -312,6 +312,9 @@ class ImplementationVisitor : public FileVisitor {
std::string GetDSLAssemblerName(Module* module);
base::Optional<Block*> GetCatchBlock();
void GenerateCatchBlock(base::Optional<Block*> catch_block);
// {StackScope} records the stack height at creation time and reconstructs it
// when being destructed by emitting a {DeleteRangeInstruction}, except for
// the slots protected by {StackScope::Yield}. Calling {Yield(v)} deletes all

View File

@ -104,6 +104,12 @@ void CallCsaMacroInstruction::TypeInstruction(Stack<const Type*>* stack,
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetObjectType());
(*catch_block)->SetInputTypes(catch_stack);
}
stack->PushMany(LowerType(macro->signature().return_type));
}
@ -136,6 +142,12 @@ void CallCsaMacroAndBranchInstruction::TypeInstruction(
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetObjectType());
(*catch_block)->SetInputTypes(catch_stack);
}
if (macro->signature().return_type != TypeOracle::GetNeverType()) {
Stack<const Type*> return_stack = *stack;
return_stack.PushMany(LowerType(macro->signature().return_type));
@ -160,6 +172,13 @@ void CallBuiltinInstruction::TypeInstruction(Stack<const Type*>* stack,
if (builtin->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetObjectType());
(*catch_block)->SetInputTypes(catch_stack);
}
stack->PushMany(LowerType(builtin->signature().return_type));
}
@ -188,6 +207,13 @@ void CallRuntimeInstruction::TypeInstruction(Stack<const Type*>* stack,
if (runtime_function->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetObjectType());
(*catch_block)->SetInputTypes(catch_stack);
}
const Type* return_type = runtime_function->signature().return_type;
if (return_type != TypeOracle::GetNeverType()) {
stack->PushMany(LowerType(return_type));

View File

@ -190,11 +190,18 @@ struct ModuleConstantInstruction : InstructionBase {
struct CallCsaMacroInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
CallCsaMacroInstruction(Macro* macro,
std::vector<std::string> constexpr_arguments)
: macro(macro), constexpr_arguments(constexpr_arguments) {}
std::vector<std::string> constexpr_arguments,
base::Optional<Block*> catch_block)
: macro(macro),
constexpr_arguments(constexpr_arguments),
catch_block(catch_block) {}
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
if (catch_block) block_list->push_back(*catch_block);
}
Macro* macro;
std::vector<std::string> constexpr_arguments;
base::Optional<Block*> catch_block;
};
struct CallCsaMacroAndBranchInstruction : InstructionBase {
@ -202,13 +209,16 @@ struct CallCsaMacroAndBranchInstruction : InstructionBase {
CallCsaMacroAndBranchInstruction(Macro* macro,
std::vector<std::string> constexpr_arguments,
base::Optional<Block*> return_continuation,
std::vector<Block*> label_blocks)
std::vector<Block*> label_blocks,
base::Optional<Block*> catch_block)
: macro(macro),
constexpr_arguments(constexpr_arguments),
return_continuation(return_continuation),
label_blocks(label_blocks) {}
label_blocks(label_blocks),
catch_block(catch_block) {}
bool IsBlockTerminator() const override { return true; }
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
if (catch_block) block_list->push_back(*catch_block);
if (return_continuation) block_list->push_back(*return_continuation);
for (Block* block : label_blocks) block_list->push_back(block);
}
@ -217,17 +227,26 @@ struct CallCsaMacroAndBranchInstruction : InstructionBase {
std::vector<std::string> constexpr_arguments;
base::Optional<Block*> return_continuation;
std::vector<Block*> label_blocks;
base::Optional<Block*> catch_block;
};
struct CallBuiltinInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
bool IsBlockTerminator() const override { return is_tailcall; }
CallBuiltinInstruction(bool is_tailcall, Builtin* builtin, size_t argc)
: is_tailcall(is_tailcall), builtin(builtin), argc(argc) {}
CallBuiltinInstruction(bool is_tailcall, Builtin* builtin, size_t argc,
base::Optional<Block*> catch_block)
: is_tailcall(is_tailcall),
builtin(builtin),
argc(argc),
catch_block(catch_block) {}
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
if (catch_block) block_list->push_back(*catch_block);
}
bool is_tailcall;
Builtin* builtin;
size_t argc;
base::Optional<Block*> catch_block;
};
struct CallBuiltinPointerInstruction : InstructionBase {
@ -249,14 +268,19 @@ struct CallRuntimeInstruction : InstructionBase {
bool IsBlockTerminator() const override;
CallRuntimeInstruction(bool is_tailcall, RuntimeFunction* runtime_function,
size_t argc)
size_t argc, base::Optional<Block*> catch_block)
: is_tailcall(is_tailcall),
runtime_function(runtime_function),
argc(argc) {}
argc(argc),
catch_block(catch_block) {}
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
if (catch_block) block_list->push_back(*catch_block);
}
bool is_tailcall;
RuntimeFunction* runtime_function;
size_t argc;
base::Optional<Block*> catch_block;
};
struct BranchInstruction : InstructionBase {

View File

@ -39,6 +39,7 @@ enum class ParseResultHolderBase::TypeId {
kDeclarationPtr,
kTypeExpressionPtr,
kLabelBlockPtr,
kOptionalLabelBlockPtr,
kNameAndTypeExpression,
kStdVectorOfNameAndTypeExpression,
kIncrementDecrementOperator,
@ -82,6 +83,10 @@ template <>
V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<LabelBlock*>::id =
ParseResultTypeId::kLabelBlockPtr;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<base::Optional<LabelBlock*>>::id =
ParseResultTypeId::kOptionalLabelBlockPtr;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<Expression*>::id =
ParseResultTypeId::kExpressionPtr;
template <>
@ -235,7 +240,7 @@ Expression* MakeCall(const std::string& callee, bool is_operator,
Expression* result = MakeNode<CallExpression>(
callee, false, generic_arguments, arguments, labels);
for (auto* label : temp_labels) {
result = MakeNode<TryLabelExpression>(result, label);
result = MakeNode<TryLabelExpression>(false, result, label);
}
return result;
}
@ -671,7 +676,7 @@ base::Optional<ParseResult> MakeTypeswitchStatement(
BlockStatement* next_block = MakeNode<BlockStatement>();
current_block->statements.push_back(
MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
MakeNode<StatementExpression>(case_block),
false, MakeNode<StatementExpression>(case_block),
MakeNode<LabelBlock>("_NextCase", ParameterList::Empty(),
next_block))));
current_block = next_block;
@ -772,9 +777,14 @@ base::Optional<ParseResult> MakeTryLabelExpression(
CheckNotDeferredStatement(try_block);
Statement* result = try_block;
auto label_blocks = child_results->NextAs<std::vector<LabelBlock*>>();
auto catch_block = child_results->NextAs<base::Optional<LabelBlock*>>();
for (auto block : label_blocks) {
result = MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
MakeNode<StatementExpression>(result), block));
false, MakeNode<StatementExpression>(result), block));
}
if (catch_block) {
result = MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
true, MakeNode<StatementExpression>(result), *catch_block));
}
return ParseResult{result};
}
@ -818,6 +828,21 @@ base::Optional<ParseResult> MakeLabelBlock(ParseResultIterator* child_results) {
return ParseResult{result};
}
base::Optional<ParseResult> MakeCatchBlock(ParseResultIterator* child_results) {
auto variable = child_results->NextAs<std::string>();
auto body = child_results->NextAs<Statement*>();
if (!IsLowerCamelCase(variable)) {
NamingConventionError("Exception", variable, "lowerCamelCase");
}
ParameterList parameters;
parameters.names.push_back(variable);
parameters.types.push_back(MakeNode<BasicTypeExpression>(false, "Object"));
parameters.has_varargs = false;
LabelBlock* result =
MakeNode<LabelBlock>("_catch", std::move(parameters), body);
return ParseResult{result};
}
base::Optional<ParseResult> MakeRangeExpression(
ParseResultIterator* child_results) {
auto begin = child_results->NextAs<base::Optional<Expression*>>();
@ -1279,6 +1304,10 @@ struct TorqueGrammar : Grammar {
TryOrDefault<ParameterList>(&parameterListNoVararg), &block},
MakeLabelBlock)};
Symbol catchBlock = {
Rule({Token("catch"), Token("("), &identifier, Token(")"), &block},
MakeCatchBlock)};
// Result: ExpressionWithSource
Symbol expressionWithSource = {Rule({expression}, MakeExpressionWithSource)};
@ -1329,7 +1358,8 @@ struct TorqueGrammar : Grammar {
Token("}"),
},
MakeTypeswitchStatement),
Rule({Token("try"), &block, NonemptyList<LabelBlock*>(&labelBlock)},
Rule({Token("try"), &block, List<LabelBlock*>(&labelBlock),
Optional<LabelBlock*>(&catchBlock)},
MakeTryLabelExpression),
Rule({OneOf({"assert", "check"}), Token("("), &expressionWithSource,
Token(")"), Token(";")},

View File

@ -286,6 +286,63 @@ TEST(TestOtherwiseAndLabels) {
ft.Call();
}
TEST(TestCatch1) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
Handle<Context> context =
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
CodeAssemblerTester asm_tester(isolate, 0);
TestBuiltinsFromDSLAssembler m(asm_tester.state());
{
TNode<Smi> result =
m.TestCatch1(m.UncheckedCast<Context>(m.HeapConstant(context)));
USE(result);
CSA_ASSERT(&m, m.WordEqual(result, m.SmiConstant(1)));
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
TEST(TestCatch2) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
Handle<Context> context =
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
CodeAssemblerTester asm_tester(isolate, 0);
TestBuiltinsFromDSLAssembler m(asm_tester.state());
{
TNode<Smi> result =
m.TestCatch2(m.UncheckedCast<Context>(m.HeapConstant(context)));
USE(result);
CSA_ASSERT(&m, m.WordEqual(result, m.SmiConstant(2)));
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
TEST(TestCatch3) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
Handle<Context> context =
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
CodeAssemblerTester asm_tester(isolate, 0);
TestBuiltinsFromDSLAssembler m(asm_tester.state());
{
TNode<Smi> result =
m.TestCatch3(m.UncheckedCast<Context>(m.HeapConstant(context)));
USE(result);
CSA_ASSERT(&m, m.WordEqual(result, m.SmiConstant(2)));
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -598,4 +598,47 @@ module test {
assert(b == 5);
}
}
macro TestCatch1(context: Context): Smi {
let r: Smi = 0;
try {
ThrowTypeError(context, kInvalidArrayLength);
} catch (e) {
r = 1;
return r;
}
}
macro TestCatch2Wrapper(context: Context): never {
ThrowTypeError(context, kInvalidArrayLength);
}
macro TestCatch2(context: Context): Smi {
let r: Smi = 0;
try {
TestCatch2Wrapper(context);
} catch (e) {
r = 2;
return r;
}
}
macro TestCatch3WrapperWithLabel(context: Context): never
labels Abort {
ThrowTypeError(context, kInvalidArrayLength);
}
macro TestCatch3(context: Context): Smi {
let r: Smi = 0;
try {
TestCatch3WrapperWithLabel(context) otherwise Abort;
}
label Abort {
return -1;
}
catch (e) {
r = 2;
return r;
}
}
}

View File

@ -29,7 +29,7 @@ syn match torqueConstant /\v<k[A-Z][A-Za-z0-9]*>/
syn keyword torqueFunction macro builtin runtime
syn keyword torqueKeyword cast convert from_constexpr min max unsafe_cast
syn keyword torqueLabel case
syn keyword torqueMatching try label
syn keyword torqueMatching try label catch
syn keyword torqueModifier extern javascript constexpr transitioning transient
syn match torqueNumber /\v<[0-9]+(\.[0-9]*)?>/
syn match torqueNumber /\v<0x[0-9a-fA-F]+>/

View File

@ -61,7 +61,7 @@
"keywords": {
"patterns": [{
"name": "keyword.control.torque",
"match": "\\b(if|else|while|for|return|continue|break|goto|otherwise|try|catch)\\b"
"match": "\\b(if|else|while|for|return|continue|break|goto|otherwise|try|label|catch)\\b"
},
{
"name": "keyword.other.torque",