This commit is contained in:
blueloveTH 2024-07-25 13:58:25 +08:00
parent 8de566f875
commit f2363a8657
12 changed files with 181 additions and 51 deletions

View File

@ -44,7 +44,7 @@ typedef struct pk_VM {
void (*_stderr)(const char*, ...); void (*_stderr)(const char*, ...);
py_TValue last_retval; py_TValue last_retval;
py_TValue last_exception; py_TValue curr_exception;
bool is_stopiteration; bool is_stopiteration;
py_TValue reg[8]; // users' registers py_TValue reg[8]; // users' registers

View File

@ -2,38 +2,15 @@
#include "pocketpy/common/str.h" #include "pocketpy/common/str.h"
#include "pocketpy/common/strname.h" #include "pocketpy/common/strname.h"
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/sourcedata.h" #include "pocketpy/objects/sourcedata.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/pocketpy.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// typedef struct pkpy_ExceptionFrame {
// pk_SourceData_ src;
// int lineno;
// const char* cursor;
// c11_string* name;
// } pkpy_ExceptionFrame;
// typedef struct pkpy_Exception {
// py_Name type;
// c11_string* msg;
// bool is_re;
// int _ip_on_error;
// void* _code_on_error;
// PyObject* self; // weak reference
// c11_vector/*T=pkpy_ExceptionFrame*/ stacktrace;
// } pkpy_Exception;
// void pkpy_Exception__ctor(pkpy_Exception* self, py_Name type);
// void pkpy_Exception__dtor(pkpy_Exception* self);
// void pkpy_Exception__stpush(pkpy_Exception* self, pk_SourceData_ src, int lineno, const char* cursor, const char* name);
// py_Str pkpy_Exception__summary(pkpy_Exception* self);
struct Error{ struct Error{
const char* type; const char* type;
pk_SourceData_ src; pk_SourceData_ src;
@ -43,6 +20,9 @@ struct Error{
int64_t userdata; int64_t userdata;
}; };
void py_BaseException__record(py_Ref, const Bytecode* ip, const CodeObject* code);
void py_BaseException__stpush(py_Ref, pk_SourceData_ src, int lineno, const char* func_name);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -241,12 +241,15 @@ py_TmpRef py_getmodule(const char* name);
bool py_import(const char* name); bool py_import(const char* name);
/************* Errors *************/ /************* Errors *************/
/// Raise an exception by name and message. Always returns false.
bool py_exception(const char* name, const char* fmt, ...); bool py_exception(const char* name, const char* fmt, ...);
/// Raise an expection object. Always returns false.
bool py_raise(py_Ref);
/// Print the last error to the console. /// Print the last error to the console.
void py_printexc(); void py_printexc();
/// Format the last error to a string. /// Format the last error to a string.
void py_formatexc(char* out); char* py_formatexc();
/// Check if an error is set. /// Check if an exception is raised.
bool py_checkexc(); bool py_checkexc();
#define NameError(n) py_exception("NameError", "name '%n' is not defined", (n)) #define NameError(n) py_exception("NameError", "name '%n' is not defined", (n))

View File

@ -105,6 +105,7 @@ OPCODE(EXCEPTION_MATCH)
OPCODE(RAISE) OPCODE(RAISE)
OPCODE(RAISE_ASSERT) OPCODE(RAISE_ASSERT)
OPCODE(RE_RAISE) OPCODE(RE_RAISE)
OPCODE(PUSH_EXCEPTION)
OPCODE(POP_EXCEPTION) OPCODE(POP_EXCEPTION)
/**************************/ /**************************/
OPCODE(FSTRING_EVAL) OPCODE(FSTRING_EVAL)

View File

@ -8,6 +8,7 @@
#include "pocketpy/common/config.h" #include "pocketpy/common/config.h"
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
#include <ctype.h> #include <ctype.h>
#include <stdbool.h>
/* expr.h */ /* expr.h */
typedef struct Expr Expr; typedef struct Expr Expr;
@ -2492,7 +2493,53 @@ __EAT_DOTS_END:
} }
static Error* compile_try_except(Compiler* self) { static Error* compile_try_except(Compiler* self) {
assert(false); Error* err;
Ctx__enter_block(ctx(), CodeBlockType_TRY_EXCEPT);
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
check(compile_block_body(self, compile_stmt));
int patches[8];
int patches_length = 0;
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
Ctx__exit_block(ctx());
if(curr()->type == TK_FINALLY) { return SyntaxError("finally clause is not supported yet"); }
do {
if(patches_length == 8) { return SyntaxError("maximum number of except clauses reached"); }
py_Name as_name = 0;
consume(TK_EXCEPT);
if(is_expression(self, false)) {
check(EXPR(self)); // push assumed type on to the stack
Ctx__s_emit_top(ctx());
Ctx__emit_(ctx(), OP_EXCEPTION_MATCH, BC_NOARG, prev()->line);
if(match(TK_AS)) {
consume(TK_ID);
as_name = py_namev(Token__sv(prev()));
}
} else {
Ctx__emit_(ctx(), OP_LOAD_TRUE, BC_NOARG, BC_KEEPLINE);
}
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
// on match
if(as_name) {
Ctx__emit_(ctx(), OP_PUSH_EXCEPTION, BC_NOARG, BC_KEEPLINE);
Ctx__emit_store_name(ctx(), name_scope(self), as_name, BC_KEEPLINE);
}
// pop the exception
Ctx__emit_(ctx(), OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE);
check(compile_block_body(self, compile_stmt));
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
Ctx__patch_jump(ctx(), patch);
} while(curr()->type == TK_EXCEPT);
// no match, re-raise
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
// no exception or no match, jump to the end
for(int i = 0; i < patches_length; i++)
Ctx__patch_jump(ctx(), patches[i]);
return NULL; return NULL;
} }

View File

@ -1,10 +1,12 @@
#include "pocketpy/common/config.h" #include "pocketpy/common/config.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/frame.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/codeobject.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include <stdbool.h> #include "pocketpy/objects/error.h"
static bool stack_unpack_sequence(pk_VM* self, uint16_t arg); static bool stack_unpack_sequence(pk_VM* self, uint16_t arg);
static bool format_object(py_Ref obj, c11_sv spec); static bool format_object(py_Ref obj, c11_sv spec);
@ -850,6 +852,30 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
DISPATCH(); DISPATCH();
} }
/////////// ///////////
case OP_TRY_ENTER: {
Frame__set_unwind_target(frame, SP());
DISPATCH();
}
case OP_EXCEPTION_MATCH: {
if(!py_checktype(TOP(), tp_type)) goto __ERROR;
bool ok = py_isinstance(TOP(), py_totype(&self->curr_exception));
POP();
py_newbool(TOP(), ok);
DISPATCH();
}
case OP_RAISE: {
// [exception]
if(py_istype(TOP(), tp_type)) {
if(!py_tpcall(py_totype(TOP()), 0, NULL)) goto __ERROR;
py_assign(TOP(), py_retval());
}
if(!py_isinstance(TOP(), tp_BaseException)) {
TypeError("exceptions must derive from BaseException");
goto __ERROR;
}
py_raise(TOP());
goto __ERROR;
}
case OP_RAISE_ASSERT: { case OP_RAISE_ASSERT: {
if(byte.arg) { if(byte.arg) {
if(!py_str(TOP())) goto __ERROR; if(!py_str(TOP())) goto __ERROR;
@ -860,6 +886,20 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
} }
goto __ERROR; goto __ERROR;
} }
case OP_RE_RAISE: {
py_raise(&self->curr_exception);
goto __ERROR;
}
case OP_PUSH_EXCEPTION: {
assert(self->curr_exception.type);
PUSH(&self->curr_exception);
DISPATCH();
}
case OP_POP_EXCEPTION: {
assert(self->curr_exception.type);
self->curr_exception = *py_NIL;
DISPATCH();
}
////////////////// //////////////////
case OP_FSTRING_EVAL: { case OP_FSTRING_EVAL: {
py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg); py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg);
@ -879,13 +919,28 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
default: c11__unreachedable(); default: c11__unreachedable();
} }
assert(false); // should never reach here c11__unreachedable();
__ERROR: __ERROR:
// 1. Exception can be handled inside the current frame
// 2. Exception need to be propagated to the upper frame
printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame)); printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame));
return RES_ERROR; py_BaseException__record(&self->curr_exception, frame->ip, frame->co);
// TODO: lineno bug on re-raise
int lineno = Frame__lineno(frame);
py_BaseException__stpush(
&self->curr_exception,
frame->co->src,
lineno,
frame->function ? frame->co->name->data : NULL
);
int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
if(target >= 0) {
// 1. Exception can be handled inside the current frame
DISPATCH_JUMP_ABSOLUTE(target);
} else {
// 2. Exception need to be propagated to the upper frame
return RES_ERROR;
}
} }
return RES_RETURN; return RES_RETURN;

View File

@ -68,7 +68,7 @@ void pk_VM__ctor(pk_VM* self) {
self->_stderr = pk_default_stderr; self->_stderr = pk_default_stderr;
self->last_retval = *py_NIL; self->last_retval = *py_NIL;
self->last_exception = *py_NIL; self->curr_exception = *py_NIL;
self->is_stopiteration = false; self->is_stopiteration = false;
self->__curr_class = NULL; self->__curr_class = NULL;
@ -540,14 +540,14 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
} }
// mark vm's registers // mark vm's registers
mark_value(&vm->last_retval); mark_value(&vm->last_retval);
mark_value(&vm->last_exception); mark_value(&vm->curr_exception);
for(int i = 0; i < c11__count_array(vm->reg); i++) { for(int i = 0; i < c11__count_array(vm->reg); i++) {
mark_value(&vm->reg[i]); mark_value(&vm->reg[i]);
} }
} }
void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) { void pk_print_stack(pk_VM* self, Frame* frame, Bytecode byte) {
return; // return;
py_TValue* sp = self->stack.sp; py_TValue* sp = self->stack.sp;
c11_sbuf buf; c11_sbuf buf;

View File

@ -46,4 +46,17 @@ bool py_checktype(py_Ref self, py_Type type) {
return TypeError("expected %t, got %t", type, self->type); return TypeError("expected %t, got %t", type, self->type);
} }
bool py_isinstance(py_Ref obj, py_Type type){
return py_issubclass(obj->type, type);
}
bool py_issubclass(py_Type derived, py_Type base){
pk_TypeInfo* types = pk_current_vm->types.data;
do {
if(derived == base) return true;
derived = types[derived].base;
} while(derived);
return false;
}
py_Type py_typeof(py_Ref self) { return self->type; } py_Type py_typeof(py_Ref self) { return self->type; }

View File

@ -1,3 +1,4 @@
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
@ -6,29 +7,31 @@
bool py_checkexc() { bool py_checkexc() {
pk_VM* vm = pk_current_vm; pk_VM* vm = pk_current_vm;
return !py_isnil(&vm->last_exception); return !py_isnil(&vm->curr_exception);
} }
void py_printexc() { void py_printexc() {
pk_VM* vm = pk_current_vm; pk_VM* vm = pk_current_vm;
if(py_isnil(&vm->last_exception)) { if(py_isnil(&vm->curr_exception)) {
vm->_stdout("NoneType: None\n"); vm->_stdout("NoneType: None\n");
} else { } else {
const char* name = py_tpname(vm->last_exception.type); const char* name = py_tpname(vm->curr_exception.type);
bool ok = py_str(&vm->last_exception); bool ok = py_str(&vm->curr_exception);
if(!ok) abort(); if(!ok) abort();
const char* message = py_tostr(py_retval()); const char* message = py_tostr(py_retval());
vm->_stdout("%s: %s\n", name, message); vm->_stdout("%s: %s\n", name, message);
} }
} }
void py_formatexc(char* out) {} char* py_formatexc() {
pk_VM* vm = pk_current_vm;
if(py_isnil(&vm->curr_exception)) {
return NULL;
}
assert(false);
}
bool py_exception(const char* name, const char* fmt, ...) { bool py_exception(const char* name, const char* fmt, ...) {
pk_VM* vm = pk_current_vm;
// an error is already set
assert(py_isnil(&vm->last_exception));
c11_sbuf buf; c11_sbuf buf;
c11_sbuf__ctor(&buf); c11_sbuf__ctor(&buf);
va_list args; va_list args;
@ -41,11 +44,16 @@ bool py_exception(const char* name, const char* fmt, ...) {
py_newstrn(message, res->data, res->size); py_newstrn(message, res->data, res->size);
c11_string__delete(res); c11_string__delete(res);
bool ok = py_tpcall(tp_Exception, 1, message); bool ok = py_tpcall(tp_Exception, 1, message);
if(!ok) abort();
py_pop(); py_pop();
if(!ok) abort(); return py_raise(py_retval());
vm->last_exception = *py_retval(); }
bool py_raise(py_Ref exc) {
assert(py_isinstance(exc, tp_BaseException));
pk_VM* vm = pk_current_vm;
vm->curr_exception = *exc;
return false; return false;
} }

View File

@ -1,3 +1,5 @@
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/error.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
@ -12,20 +14,41 @@ typedef struct BaseExceptionFrame {
} BaseExceptionFrame; } BaseExceptionFrame;
typedef struct BaseException { typedef struct BaseException {
int ip_backup; const Bytecode* ip_backup;
CodeObject* code_backup; const CodeObject* code_backup;
c11_vector /*T=BaseExceptionFrame*/ stacktrace; c11_vector /*T=BaseExceptionFrame*/ stacktrace;
} BaseException; } BaseException;
void py_BaseException__record(py_Ref self, const Bytecode* ip, const CodeObject* code) {
BaseException* ud = py_touserdata(self);
ud->ip_backup = ip;
ud->code_backup = code;
}
void py_BaseException__stpush(py_Ref self, pk_SourceData_ src, int lineno, const char *func_name){
BaseException* ud = py_touserdata(self);
if(ud->stacktrace.count >= 7) return;
BaseExceptionFrame* frame = c11_vector__emplace(&ud->stacktrace);
PK_INCREF(src);
frame->src = src;
frame->lineno = lineno;
frame->name = func_name ? c11_string__new(func_name) : NULL;
}
static void BaseException__dtor(void* ud) { static void BaseException__dtor(void* ud) {
BaseException* self = (BaseException*)ud; BaseException* self = (BaseException*)ud;
c11__foreach(BaseExceptionFrame, &self->stacktrace, it) {
PK_DECREF(it->src);
if(it->name) c11_string__delete(it->name);
}
c11_vector__dtor(&self->stacktrace); c11_vector__dtor(&self->stacktrace);
} }
static bool _py_BaseException__new__(int argc, py_Ref argv) { static bool _py_BaseException__new__(int argc, py_Ref argv) {
py_Type cls = py_totype(argv); py_Type cls = py_totype(argv);
BaseException* ud = py_newobject(py_retval(), cls, 1, sizeof(BaseException)); BaseException* ud = py_newobject(py_retval(), cls, 1, sizeof(BaseException));
ud->ip_backup = -1; c11_vector__ctor(&ud->stacktrace, sizeof(BaseExceptionFrame));
ud->ip_backup = NULL;
ud->code_backup = NULL; ud->code_backup = NULL;
return true; return true;
} }