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:
parent
54cc05553b
commit
27dc9fa5ca
@ -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>(¶meterListNoVararg), &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",
|
||||
|
Loading…
Reference in New Issue
Block a user