Implement exception reraising from the guard handler

This commit is contained in:
Konstantin Nazarov 2024-10-13 19:13:33 +01:00
parent 999cd062f0
commit b9fa84ba0e
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
5 changed files with 107 additions and 28 deletions

View file

@ -702,6 +702,50 @@ Result<StackFrame> StackFrame::attach(const StackFrame& frame) const {
return StackFrame(TRY(MkGcRoot(pod)));
}
Result<Value> StackFrame::unwind(const Value& val, uint64_t start_from) const {
StackFrame cur = TRY(copy());
uint64_t depth = 0;
while (true) {
if (cur.guard() && depth >= start_from) {
auto state = TRY(cur.get(1));
if (!state.is<Int64>()) return ERROR(TypeMismatch);
auto state_int = state.to<Int64>()->value();
if (state_int <= 1) {
auto detached_stack = TRY(detach(depth - 1));
auto new_cont = TRY(Continuation::create(val, detached_stack));
cur = TRY(cur.set(1, Value(TRY(Int64::create((int64_t)1)))));
cur = TRY(cur.set(2, Value(std::move(new_cont))));
return Value(std::move(cur));
}
}
auto parent = TRY(cur.parent());
if (parent.is<Nil>()) break;
cur = TRY(parent.to<StackFrame>()->copy());
++depth;
}
return Value(TRY(Nil::create()));
}
Result<uint64_t> StackFrame::depth() const {
StackFrame cur = TRY(copy());
uint64_t depth = 1;
while (true) {
auto parent = TRY(cur.parent());
if (parent.is<Nil>()) break;
cur = TRY(parent.to<StackFrame>()->copy());
++depth;
}
return depth;
}
Result<StackFrame> StackFrame::set_guard(bool guard) const {
uint64_t size = _value->size;
auto pod = TRY(arena_alloc<PodStackFrame>(sizeof(OffPtr<PodObject>) * size));

View file

@ -1168,6 +1168,9 @@ class StackFrame : public Object {
Result<StackFrame> detach(uint64_t depth) const;
Result<StackFrame> attach(const StackFrame& frame) const;
Result<Value> unwind(const Value& val, uint64_t start_from = 0) const;
Result<uint64_t> depth() const;
Result<StackFrame> set_guard(bool guard = true) const;
virtual Result<Value> copy_value() const final;

View file

@ -406,6 +406,61 @@ Result<StackFrame> stdlib_raise(const StackFrame& stack) {
return ERROR_OBJ(Raise, cont);
}
Result<StackFrame> stdlib_reraise(const StackFrame& stack) {
auto stack_size = TRY(stack.size());
auto params = TRY(stack.get(0));
auto size = TRY(params.size());
if (size != 1) return ERROR(ArgumentCountMismatch);
auto cont_val = TRY(params.get(0));
if (!cont_val.is<Continuation>()) return ERROR(TypeMismatch);
Continuation& cont = *cont_val.to<Continuation>();
StackFrame cont_stack = TRY(cont.frame());
StackFrame cur = TRY(stack.copy());
uint64_t depth = 0;
while (true) {
if (cur.guard()) {
auto state = TRY(cur.get(1));
if (!state.is<Int64>()) return ERROR(TypeMismatch);
auto state_int = state.to<Int64>()->value();
if (state_int > 1) {
auto val = TRY(cont.value());
auto remaining_stack = TRY(stack.detach(depth - 1));
Value fun = TRY(params.get(0));
cur = TRY(cur.set(1, Value(TRY(Int64::create((int64_t)0)))));
cur = TRY(cur.set(2, Value(std::move(fun))));
cur = TRY(cur.settop(3));
cur = TRY(remaining_stack.attach(cur));
cur = TRY(cont_stack.attach(cur));
uint64_t start_from = depth + TRY(cont_stack.depth()) + 1;
auto res = TRY(cur.unwind(val, start_from));
if (res.is<StackFrame>())
return res.to<StackFrame>()->copy();
else
break;
}
}
auto parent = TRY(cur.parent());
if (parent.is<Nil>()) break;
cur = TRY(parent.to<StackFrame>()->copy());
++depth;
}
return ERROR_OBJ(Raise, cont_val);
}
Result<StackFrame> stdlib_task(const StackFrame& stack) {
auto params = TRY(stack.get(0));
Array& params_array = *params.to<Array>();
@ -588,6 +643,7 @@ static StdlibFunctionEntry function_entries[] = {
STDLIB_FUNCTION(map, Map),
STDLIB_FUNCTION(error, Error),
STDLIB_FUNCTION(raise, Raise),
STDLIB_FUNCTION(reraise, Reraise),
STDLIB_FUNCTION(task, Task),
STDLIB_FUNCTION(guard, Guard),
STDLIB_FUNCTION(serialize, Serialize),

View file

@ -22,6 +22,7 @@ enum class StdlibFunctionId : uint64_t {
Map,
Error,
Raise,
Reraise,
Task,
Guard,
Serialize,

View file

@ -303,35 +303,10 @@ Result<void> VM::step_bytecode() {
}
Result<Value> VM::unwind(const Continuation& cont) {
StackFrame cur = TRY(cont.frame());
auto val = TRY(cont.value());
auto cont_stack = TRY(cont.frame());
uint64_t depth = 0;
while (true) {
if (cur.guard()) {
auto state = TRY(cur.get(1));
if (!state.is<Int64>()) return ERROR(TypeMismatch);
auto state_int = state.to<Int64>()->value();
if (state_int <= 1) {
auto val = TRY(cont.value());
auto cont_stack = TRY(cont.frame());
cont_stack = TRY(cont_stack.detach(depth - 1));
auto new_cont = TRY(Continuation::create(val, cont_stack));
cur = TRY(cur.set(1, Value(TRY(Int64::create((int64_t)1)))));
cur = TRY(cur.set(2, Value(std::move(new_cont))));
return Value(std::move(cur));
}
}
auto parent = TRY(cur.parent());
if (parent.is<Nil>()) break;
cur = TRY(parent.to<StackFrame>()->copy());
++depth;
}
return Value(TRY(Nil::create()));
return cont_stack.unwind(val);
}
Result<StackFrame> VM::handle_raise(const Continuation& cont) {