Commit 0f15ed05 authored by Daniel Clifford's avatar Daniel Clifford Committed by Commit Bot

[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: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57169}
parent 6627bdb1
......@@ -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) {
......
......@@ -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
......
......@@ -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());
......
......@@ -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;
};
......
......@@ -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_) {
......
......@@ -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
......
......@@ -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);
}
}
......
......@@ -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());
}
......
......@@ -93,11 +93,14 @@ void ImplementationVisitor::BeginModuleFile(Module* module) {
source << "#include \"builtins-" + DashifyString(module->name()) +
"-from-dsl-gen.h\"\n\n";
source << "namespace v8 {\n"
<< "namespace internal {\n"
<< "\n"
<< "using Node = compiler::Node;\n"
<< "\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());
transform(upper_name.begin(), upper_name.end(), upper_name.begin(),
......@@ -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
......@@ -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
......
......@@ -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));
......
......@@ -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 {
......
......@@ -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(";")},
......
......@@ -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
......@@ -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;
}
}
}
......@@ -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]+>/
......
......@@ -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",
......
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