mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-21 03:50:16 +00:00
impl LOAD_FAST
This commit is contained in:
parent
205fedd053
commit
c403252aec
9
.github/workflows/main.yml
vendored
9
.github/workflows/main.yml
vendored
@ -32,15 +32,6 @@ jobs:
|
||||
platform: x64
|
||||
- name: Install libc++
|
||||
run: sudo apt install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15
|
||||
# libclang-rt-15-dev ...some bugs here
|
||||
# - name: Coverage Test
|
||||
# run: |
|
||||
# python3 preprocess.py
|
||||
# bash run_tests.sh
|
||||
# - uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: coverage
|
||||
# path: .coverage
|
||||
- name: Compile
|
||||
run: |
|
||||
python3 build.py linux
|
||||
|
44
src/ceval.h
44
src/ceval.h
@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "namedict.h"
|
||||
#include "vm.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
inline PyObject* VM::_run_top_frame(){
|
||||
inline PyObject* VM::_run_top_frame(bool force_no_pop){
|
||||
FrameId frame = top_frame();
|
||||
const int base_id = frame.index;
|
||||
bool need_raise = false;
|
||||
@ -28,9 +29,9 @@ inline PyObject* VM::_run_top_frame(){
|
||||
Bytecode byte = frame->next_bytecode();
|
||||
// cache
|
||||
const CodeObject* co = frame->co;
|
||||
const std::vector<StrName>& co_names = co->names;
|
||||
const List& co_consts = co->consts;
|
||||
const std::vector<CodeBlock>& co_blocks = co->blocks;
|
||||
const auto& co_names = co->names;
|
||||
const auto& co_consts = co->consts;
|
||||
const auto& co_blocks = co->blocks;
|
||||
|
||||
#if PK_ENABLE_COMPUTED_GOTO
|
||||
static void* OP_LABELS[] = {
|
||||
@ -76,16 +77,27 @@ __NEXT_STEP:;
|
||||
TARGET(LOAD_BUILTIN_EVAL) frame->push(builtins->attr(m_eval)); DISPATCH();
|
||||
TARGET(LOAD_FUNCTION) {
|
||||
FuncDecl_ decl = co->func_decls[byte.arg];
|
||||
PyObject* obj = VAR(Function({decl, frame->_module, frame->_locals}));
|
||||
PyObject* obj;
|
||||
if(decl->nested){
|
||||
obj = VAR(Function({decl, frame->_module, frame->locals_to_namedict()}));
|
||||
}else{
|
||||
obj = VAR(Function({decl, frame->_module, nullptr}));
|
||||
}
|
||||
frame->push(obj);
|
||||
} DISPATCH();
|
||||
TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH();
|
||||
/*****************************************/
|
||||
TARGET(LOAD_FAST) {
|
||||
heap._auto_collect();
|
||||
PyObject* val = frame->_locals[byte.arg];
|
||||
if(val == nullptr) vm->NameError(co->varnames[byte.arg]);
|
||||
frame->push(val);
|
||||
} DISPATCH();
|
||||
TARGET(LOAD_NAME) {
|
||||
heap._auto_collect();
|
||||
StrName name = co_names[byte.arg];
|
||||
PyObject* val;
|
||||
val = frame->f_locals().try_get(name);
|
||||
val = frame->f_locals_try_get(name);
|
||||
if(val != nullptr) { frame->push(val); DISPATCH(); }
|
||||
val = frame->f_closure_try_get(name);
|
||||
if(val != nullptr) { frame->push(val); DISPATCH(); }
|
||||
@ -122,10 +134,9 @@ __NEXT_STEP:;
|
||||
args[0] = frame->top(); // a
|
||||
frame->top() = fast_call(__getitem__, std::move(args));
|
||||
} DISPATCH();
|
||||
TARGET(STORE_LOCAL) {
|
||||
StrName name = co_names[byte.arg];
|
||||
frame->f_locals().set(name, frame->popx());
|
||||
} DISPATCH();
|
||||
TARGET(STORE_FAST)
|
||||
frame->_locals[byte.arg] = frame->popx();
|
||||
DISPATCH();
|
||||
TARGET(STORE_GLOBAL) {
|
||||
StrName name = co_names[byte.arg];
|
||||
frame->f_globals().set(name, frame->popx());
|
||||
@ -144,13 +155,10 @@ __NEXT_STEP:;
|
||||
args[2] = frame->popx(); // val
|
||||
fast_call(__setitem__, std::move(args));
|
||||
} DISPATCH();
|
||||
TARGET(DELETE_LOCAL) {
|
||||
StrName name = co_names[byte.arg];
|
||||
if(frame->f_locals().contains(name)){
|
||||
frame->f_locals().erase(name);
|
||||
}else{
|
||||
NameError(name);
|
||||
}
|
||||
TARGET(DELETE_FAST) {
|
||||
PyObject* val = frame->_locals[byte.arg];
|
||||
if(val == nullptr) vm->NameError(co->varnames[byte.arg]);
|
||||
frame->_locals[byte.arg] = nullptr;
|
||||
} DISPATCH();
|
||||
TARGET(DELETE_GLOBAL) {
|
||||
StrName name = co_names[byte.arg];
|
||||
@ -515,7 +523,7 @@ __NEXT_STEP:;
|
||||
/**********************************************************************/
|
||||
__PY_SIMPLE_RETURN:
|
||||
if(frame.index == base_id){ // [ frameBase<- ]
|
||||
callstack.pop();
|
||||
if(!force_no_pop) callstack.pop();
|
||||
return __ret;
|
||||
}else{
|
||||
callstack.pop();
|
||||
|
@ -52,23 +52,20 @@ struct CodeObject {
|
||||
Str name;
|
||||
bool is_generator = false;
|
||||
|
||||
CodeObject(shared_ptr<SourceData> src, Str name) {
|
||||
this->src = src;
|
||||
this->name = name;
|
||||
}
|
||||
CodeObject(shared_ptr<SourceData> src, const Str& name):
|
||||
src(src), name(name) {}
|
||||
|
||||
std::vector<Bytecode> codes;
|
||||
std::vector<int> lines; // line number for each bytecode
|
||||
List consts;
|
||||
std::vector<StrName> names;
|
||||
std::vector<StrName> names; // other names
|
||||
std::vector<StrName> varnames; // local variables
|
||||
std::map<StrName, int> varnames_inv;
|
||||
std::set<Str> global_names;
|
||||
std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
|
||||
std::map<StrName, int> labels;
|
||||
std::vector<FuncDecl_> func_decls;
|
||||
|
||||
uint32_t perfect_locals_capacity = NameDict::__Capacity;
|
||||
uint32_t perfect_hash_seed = kHashSeeds[0];
|
||||
|
||||
void optimize(VM* vm);
|
||||
|
||||
void _gc_mark() const {
|
||||
|
@ -29,13 +29,12 @@
|
||||
#include <variant>
|
||||
#include <type_traits>
|
||||
|
||||
#define PK_VERSION "0.9.7"
|
||||
#define PK_VERSION "0.9.8"
|
||||
|
||||
// debug macros
|
||||
#define DEBUG_NO_BUILTIN_MODULES 0
|
||||
#define DEBUG_EXTRA_CHECK 0
|
||||
#define DEBUG_DIS_EXEC 0
|
||||
#define DEBUG_DIS_EXEC_MIN 1
|
||||
#define DEBUG_CEVAL_STEP 0
|
||||
#define DEBUG_FULL_EXCEPTION 0
|
||||
#define DEBUG_MEMORY_POOL 0
|
||||
@ -96,7 +95,6 @@ struct Type {
|
||||
#define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!");
|
||||
#endif
|
||||
|
||||
inline const float kLocalsLoadFactor = 0.67f;
|
||||
inline const float kInstAttrLoadFactor = 0.67f;
|
||||
inline const float kTypeAttrLoadFactor = 0.5f;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "codeobject.h"
|
||||
#include "common.h"
|
||||
#include "expr.h"
|
||||
#include "obj.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
@ -38,13 +39,20 @@ class Compiler {
|
||||
CompileMode mode() const{ return lexer->src->mode; }
|
||||
NameScope name_scope() const { return contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; }
|
||||
|
||||
template<typename... Args>
|
||||
CodeObject_ push_context(Args&&... args){
|
||||
CodeObject_ co = make_sp<CodeObject>(std::forward<Args>(args)...);
|
||||
CodeObject_ push_global_context(){
|
||||
CodeObject_ co = make_sp<CodeObject>(lexer->src, lexer->src->filename);
|
||||
contexts.push(CodeEmitContext(vm, co));
|
||||
return co;
|
||||
}
|
||||
|
||||
FuncDecl_ push_f_context(Str name){
|
||||
FuncDecl_ decl = make_sp<FuncDecl>();
|
||||
decl->code = make_sp<CodeObject>(lexer->src, name);
|
||||
decl->nested = name_scope() == NAME_LOCAL;
|
||||
contexts.push(CodeEmitContext(vm, decl->code));
|
||||
return decl;
|
||||
}
|
||||
|
||||
void pop_context(){
|
||||
if(!ctx()->s_expr.empty()){
|
||||
throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr());
|
||||
@ -190,12 +198,12 @@ class Compiler {
|
||||
|
||||
|
||||
void exprLambda(){
|
||||
auto e = make_expr<LambdaExpr>(name_scope());
|
||||
FuncDecl_ decl = push_f_context("<lambda>");
|
||||
auto e = make_expr<LambdaExpr>(decl);
|
||||
if(!match(TK(":"))){
|
||||
_compile_f_args(e->decl, false);
|
||||
consume(TK(":"));
|
||||
}
|
||||
e->decl->code = push_context(lexer->src, e->decl->name.sv());
|
||||
// https://github.com/blueloveTH/pocketpy/issues/37
|
||||
parse_expression(PREC_LAMBDA + 1, false);
|
||||
ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
|
||||
@ -528,9 +536,11 @@ __SUBSCR_END:
|
||||
consume(TK("@id"));
|
||||
name = prev().str();
|
||||
}
|
||||
int index = ctx()->add_name(name);
|
||||
auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL;
|
||||
ctx()->emit(op, index, prev().line);
|
||||
if(name_scope() == NAME_LOCAL){
|
||||
ctx()->emit(OP_STORE_FAST, ctx()->add_varname(name), prev().line);
|
||||
}else{
|
||||
ctx()->emit(OP_STORE_GLOBAL, ctx()->add_name(name), prev().line);
|
||||
}
|
||||
} while (match(TK(",")));
|
||||
consume_end_stmt();
|
||||
}
|
||||
@ -555,9 +565,11 @@ __SUBSCR_END:
|
||||
consume(TK("@id"));
|
||||
name = prev().str();
|
||||
}
|
||||
index = ctx()->add_name(name);
|
||||
auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL;
|
||||
ctx()->emit(op, index, prev().line);
|
||||
if(name_scope() == NAME_LOCAL){
|
||||
ctx()->emit(OP_STORE_FAST, ctx()->add_varname(name), prev().line);
|
||||
}else{
|
||||
ctx()->emit(OP_STORE_GLOBAL, ctx()->add_name(name), prev().line);
|
||||
}
|
||||
} while (match(TK(",")));
|
||||
ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
|
||||
consume_end_stmt();
|
||||
@ -849,27 +861,44 @@ __SUBSCR_END:
|
||||
else if(match(TK("**"))){
|
||||
state = 3;
|
||||
}
|
||||
|
||||
consume(TK("@id"));
|
||||
const Str& name = prev().str();
|
||||
if(decl->has_name(name)) SyntaxError("duplicate argument name");
|
||||
StrName name = prev().str();
|
||||
|
||||
// check duplicate argument name
|
||||
for(int i: decl->args){
|
||||
if(decl->code->varnames[i] == name) {
|
||||
SyntaxError("duplicate argument name");
|
||||
}
|
||||
}
|
||||
if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){
|
||||
SyntaxError("duplicate argument name");
|
||||
}
|
||||
for(auto& kv: decl->kwargs){
|
||||
if(decl->code->varnames[kv.key] == name){
|
||||
SyntaxError("duplicate argument name");
|
||||
}
|
||||
}
|
||||
|
||||
// eat type hints
|
||||
if(enable_type_hints && match(TK(":"))) consume(TK("@id"));
|
||||
|
||||
if(state == 0 && curr().type == TK("=")) state = 2;
|
||||
int index = ctx()->add_varname(name);
|
||||
switch (state)
|
||||
{
|
||||
case 0: decl->args.push_back(name); break;
|
||||
case 1: decl->starred_arg = name; state+=1; break;
|
||||
case 0:
|
||||
decl->args.push_back(index);
|
||||
break;
|
||||
case 1:
|
||||
decl->starred_arg = index;
|
||||
state+=1;
|
||||
break;
|
||||
case 2: {
|
||||
consume(TK("="));
|
||||
PyObject* value = read_literal();
|
||||
if(value == nullptr){
|
||||
SyntaxError(Str("expect a literal, not ") + TK_STR(curr().type));
|
||||
}
|
||||
decl->kwargs.set(name, value);
|
||||
decl->kwargs_order.push_back(name);
|
||||
decl->kwargs.push_back(FuncDecl::KwArg{index, value});
|
||||
} break;
|
||||
case 3: SyntaxError("**kwargs is not supported yet"); break;
|
||||
}
|
||||
@ -877,15 +906,16 @@ __SUBSCR_END:
|
||||
}
|
||||
|
||||
void compile_function(const std::vector<Expr_>& decorators={}){
|
||||
FuncDecl_ decl = make_sp<FuncDecl>();
|
||||
StrName obj_name;
|
||||
Str obj_name;
|
||||
Str decl_name;
|
||||
consume(TK("@id"));
|
||||
decl->name = prev().str();
|
||||
decl_name = prev().str();
|
||||
if(!ctx()->is_compiling_class && match(TK("::"))){
|
||||
consume(TK("@id"));
|
||||
obj_name = decl->name;
|
||||
decl->name = prev().str();
|
||||
obj_name = decl_name;
|
||||
decl_name = prev().str();
|
||||
}
|
||||
FuncDecl_ decl = push_f_context(decl_name);
|
||||
consume(TK("("));
|
||||
if (!match(TK(")"))) {
|
||||
_compile_f_args(decl, true);
|
||||
@ -894,7 +924,6 @@ __SUBSCR_END:
|
||||
if(match(TK("->"))){
|
||||
if(!match(TK("None"))) consume(TK("@id"));
|
||||
}
|
||||
decl->code = push_context(lexer->src, decl->name.sv());
|
||||
compile_block_body();
|
||||
pop_context();
|
||||
ctx()->emit(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line);
|
||||
@ -908,15 +937,15 @@ __SUBSCR_END:
|
||||
}
|
||||
if(!ctx()->is_compiling_class){
|
||||
if(obj_name.empty()){
|
||||
auto e = make_expr<NameExpr>(decl->name, name_scope());
|
||||
auto e = make_expr<NameExpr>(decl_name, name_scope());
|
||||
e->emit_store(ctx());
|
||||
} else {
|
||||
ctx()->emit(OP_LOAD_GLOBAL, ctx()->add_name(obj_name), prev().line);
|
||||
int index = ctx()->add_name(decl->name);
|
||||
int index = ctx()->add_name(decl_name);
|
||||
ctx()->emit(OP_STORE_ATTR, index, prev().line);
|
||||
}
|
||||
}else{
|
||||
int index = ctx()->add_name(decl->name);
|
||||
int index = ctx()->add_name(decl_name);
|
||||
ctx()->emit(OP_STORE_CLASS_ATTR, index, prev().line);
|
||||
}
|
||||
}
|
||||
@ -963,7 +992,7 @@ public:
|
||||
// for(auto& t: tokens) std::cout << t.info() << std::endl;
|
||||
// }
|
||||
|
||||
CodeObject_ code = push_context(lexer->src, lexer->src->filename);
|
||||
CodeObject_ code = push_global_context();
|
||||
|
||||
advance(); // skip @sof, so prev() is always valid
|
||||
match_newlines(); // skip possible leading '\n'
|
||||
|
42
src/expr.h
42
src/expr.h
@ -105,6 +105,15 @@ struct CodeEmitContext{
|
||||
return co->names.size() - 1;
|
||||
}
|
||||
|
||||
int add_varname(StrName name){
|
||||
auto it = co->varnames_inv.find(name);
|
||||
if(it != co->varnames_inv.end()) return it->second;
|
||||
co->varnames.push_back(name);
|
||||
int index = co->varnames.size() - 1;
|
||||
co->varnames_inv[name] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
int add_const(PyObject* v){
|
||||
co->consts.push_back(v);
|
||||
return co->consts.size() - 1;
|
||||
@ -124,18 +133,24 @@ struct NameExpr: Expr{
|
||||
std::string str() const override { return fmt("Name(", name.escape(), ")"); }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
int index = ctx->add_name(name);
|
||||
ctx->emit(OP_LOAD_NAME, index, line);
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_LOAD_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_LOAD_GLOBAL, ctx->add_name(name), line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
}
|
||||
|
||||
bool emit_del(CodeEmitContext* ctx) override {
|
||||
int index = ctx->add_name(name);
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_DELETE_LOCAL, index, line);
|
||||
ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_DELETE_GLOBAL, index, line);
|
||||
ctx->emit(OP_DELETE_GLOBAL, ctx->add_name(name), line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
@ -143,17 +158,17 @@ struct NameExpr: Expr{
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
int index = ctx->add_name(name);
|
||||
if(ctx->is_compiling_class){
|
||||
int index = ctx->add_name(name);
|
||||
ctx->emit(OP_STORE_CLASS_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_STORE_LOCAL, index, line);
|
||||
ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_STORE_GLOBAL, index, line);
|
||||
ctx->emit(OP_STORE_GLOBAL, ctx->add_name(name), line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
@ -484,14 +499,9 @@ struct SetCompExpr: CompExpr{
|
||||
|
||||
struct LambdaExpr: Expr{
|
||||
FuncDecl_ decl;
|
||||
NameScope scope;
|
||||
std::string str() const override { return "Lambda()"; }
|
||||
|
||||
LambdaExpr(NameScope scope){
|
||||
this->decl = make_sp<FuncDecl>();
|
||||
this->decl->name = "<lambda>";
|
||||
this->scope = scope;
|
||||
}
|
||||
LambdaExpr(FuncDecl_ decl): decl(decl) {}
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
int index = ctx->add_func_decl(decl);
|
||||
@ -624,8 +634,8 @@ struct CallExpr: Expr{
|
||||
for(auto& item: args) item->emit(ctx);
|
||||
// emit kwargs
|
||||
for(auto& item: kwargs){
|
||||
// TODO: optimize this
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(item.first)), line);
|
||||
int index = ctx->add_varname(item.first);
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(index)), line);
|
||||
item.second->emit(ctx);
|
||||
}
|
||||
int KWARGC = (int)kwargs.size();
|
||||
|
35
src/frame.h
35
src/frame.h
@ -7,6 +7,7 @@
|
||||
namespace pkpy{
|
||||
|
||||
using ValueStack = pod_vector<PyObject*>;
|
||||
using FastLocals = pod_vector<PyObject*>;
|
||||
|
||||
struct Frame {
|
||||
ValueStack _data;
|
||||
@ -15,18 +16,39 @@ struct Frame {
|
||||
|
||||
const CodeObject* co;
|
||||
PyObject* _module;
|
||||
NameDict_ _locals;
|
||||
FastLocals _locals;
|
||||
NameDict_ _closure;
|
||||
|
||||
NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); }
|
||||
PyObject* f_locals_try_get(StrName name){
|
||||
auto it = co->varnames_inv.find(name);
|
||||
if(it == co->varnames_inv.end()) return nullptr;
|
||||
return _locals[it->second];
|
||||
}
|
||||
|
||||
NameDict& f_globals() noexcept { return _module->attr(); }
|
||||
PyObject* f_closure_try_get(StrName name){
|
||||
if(_closure == nullptr) return nullptr;
|
||||
return _closure->try_get(name);
|
||||
}
|
||||
|
||||
Frame(const CodeObject_& co, PyObject* _module, const NameDict_& _locals=nullptr, const NameDict_& _closure=nullptr)
|
||||
: co(co.get()), _module(_module), _locals(_locals), _closure(_closure) {
|
||||
NameDict_ locals_to_namedict() const{
|
||||
NameDict_ dict = make_sp<NameDict>();
|
||||
for(int i=0; i<co->varnames.size(); i++){
|
||||
if(_locals[i] != nullptr) dict->set(co->varnames[i], _locals[i]);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const NameDict_& _closure)
|
||||
: co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) {
|
||||
}
|
||||
|
||||
Frame(const CodeObject* co, PyObject* _module)
|
||||
: co(co), _module(_module), _locals(), _closure(nullptr) {
|
||||
}
|
||||
|
||||
Frame(const CodeObject_& co, PyObject* _module)
|
||||
: co(co.get()), _module(_module), _locals(), _closure(nullptr) {
|
||||
}
|
||||
|
||||
Frame(const Frame& other) = delete;
|
||||
@ -155,7 +177,10 @@ struct Frame {
|
||||
if(_data._data == nullptr) return;
|
||||
for(PyObject* obj : _data) OBJ_MARK(obj);
|
||||
OBJ_MARK(_module);
|
||||
if(_locals != nullptr) _locals->_gc_mark();
|
||||
// _locals may be move for `eval/exec`
|
||||
if(_locals._data != nullptr){
|
||||
for(PyObject* obj: _locals) OBJ_MARK(obj);
|
||||
}
|
||||
if(_closure != nullptr) _closure->_gc_mark();
|
||||
co->_gc_mark();
|
||||
}
|
||||
|
2
src/gc.h
2
src/gc.h
@ -120,7 +120,7 @@ inline void NameDict::_gc_mark() const{
|
||||
|
||||
inline void FuncDecl::_gc_mark() const{
|
||||
code->_gc_mark();
|
||||
kwargs._gc_mark();
|
||||
for(int i=0; i<kwargs.size(); i++) OBJ_MARK(kwargs[i].value);
|
||||
}
|
||||
|
||||
template<> inline void _gc_mark<List>(List& t){
|
||||
|
24
src/obj.h
24
src/obj.h
@ -8,7 +8,6 @@ namespace pkpy {
|
||||
|
||||
struct CodeObject;
|
||||
struct Frame;
|
||||
struct BaseRef;
|
||||
class VM;
|
||||
|
||||
typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw;
|
||||
@ -25,20 +24,15 @@ struct NativeFunc {
|
||||
};
|
||||
|
||||
struct FuncDecl {
|
||||
StrName name;
|
||||
CodeObject_ code;
|
||||
std::vector<StrName> args;
|
||||
StrName starred_arg; // empty if no *arg
|
||||
NameDict kwargs; // empty if no k=v
|
||||
std::vector<StrName> kwargs_order;
|
||||
|
||||
bool has_name(StrName val) const {
|
||||
bool _0 = std::find(args.begin(), args.end(), val) != args.end();
|
||||
bool _1 = starred_arg == val;
|
||||
bool _2 = kwargs.contains(val);
|
||||
return _0 || _1 || _2;
|
||||
}
|
||||
|
||||
struct KwArg {
|
||||
int key; // index in co->varnames
|
||||
PyObject* value; // default value
|
||||
};
|
||||
CodeObject_ code; // code object of this function
|
||||
pod_vector<int> args; // indices in co->varnames
|
||||
int starred_arg = -1; // index in co->varnames, -1 if no *arg
|
||||
pod_vector<KwArg> kwargs; // indices in co->varnames
|
||||
bool nested = false; // whether this function is nested
|
||||
void _gc_mark() const;
|
||||
};
|
||||
|
||||
|
@ -17,18 +17,19 @@ OPCODE(LOAD_BUILTIN_EVAL)
|
||||
OPCODE(LOAD_FUNCTION)
|
||||
OPCODE(LOAD_NULL)
|
||||
/**************************/
|
||||
OPCODE(LOAD_FAST)
|
||||
OPCODE(LOAD_NAME)
|
||||
OPCODE(LOAD_GLOBAL)
|
||||
OPCODE(LOAD_ATTR)
|
||||
OPCODE(LOAD_METHOD)
|
||||
OPCODE(LOAD_SUBSCR)
|
||||
|
||||
OPCODE(STORE_LOCAL)
|
||||
OPCODE(STORE_FAST)
|
||||
OPCODE(STORE_GLOBAL)
|
||||
OPCODE(STORE_ATTR)
|
||||
OPCODE(STORE_SUBSCR)
|
||||
|
||||
OPCODE(DELETE_LOCAL)
|
||||
OPCODE(DELETE_FAST)
|
||||
OPCODE(DELETE_GLOBAL)
|
||||
OPCODE(DELETE_ATTR)
|
||||
OPCODE(DELETE_SUBSCR)
|
||||
|
@ -98,12 +98,26 @@ inline void init_builtins(VM* _vm) {
|
||||
|
||||
_vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) {
|
||||
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<eval>", EVAL_MODE);
|
||||
return vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals);
|
||||
FrameId frame = vm->top_frame();
|
||||
vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr);
|
||||
PyObject* ret = vm->_run_top_frame(true);
|
||||
frame->_locals = std::move(vm->top_frame()->_locals);
|
||||
vm->callstack.pop();
|
||||
return ret;
|
||||
});
|
||||
|
||||
_vm->bind_builtin_func<1>("exec", [](VM* vm, Args& args) {
|
||||
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<exec>", EXEC_MODE);
|
||||
vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals);
|
||||
FrameId frame = vm->top_frame();
|
||||
// TODO: implementation is not correct
|
||||
// ...
|
||||
// moving _locals is dangerous since OP_LOAD_FAST's arg is index of _locals
|
||||
// the new opcode may not generate the same index, or even not a OP_LOAD_FAST call
|
||||
// we should do some special handling here
|
||||
vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr);
|
||||
vm->_run_top_frame(true);
|
||||
frame->_locals = std::move(vm->top_frame()->_locals);
|
||||
vm->callstack.pop();
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
@ -598,7 +612,7 @@ inline void add_module_json(VM* vm){
|
||||
vm->bind_func<1>(mod, "loads", [](VM* vm, Args& args) {
|
||||
const Str& expr = CAST(Str&, args[0]);
|
||||
CodeObject_ code = vm->compile(expr, "<json>", JSON_MODE);
|
||||
return vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals);
|
||||
return vm->_exec(code, vm->top_frame()->_module);
|
||||
});
|
||||
|
||||
vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->fast_call(__json__, Args{args[0]})));
|
||||
|
95
src/vm.h
95
src/vm.h
@ -348,7 +348,7 @@ public:
|
||||
template<int ARGC>
|
||||
void bind_func(PyObject*, Str, NativeFuncRaw);
|
||||
void _error(Exception);
|
||||
PyObject* _run_top_frame();
|
||||
PyObject* _run_top_frame(bool force_no_pop=false);
|
||||
void post_init();
|
||||
};
|
||||
|
||||
@ -361,11 +361,9 @@ inline PyObject* NativeFunc::operator()(VM* vm, Args& args) const{
|
||||
}
|
||||
|
||||
inline void CodeObject::optimize(VM* vm){
|
||||
// here we simple pass all names, but only some of them are NAME_LOCAL
|
||||
// TODO: ...
|
||||
uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5);
|
||||
perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity);
|
||||
perfect_hash_seed = find_perfect_hash_seed(perfect_locals_capacity, names);
|
||||
// uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5);
|
||||
// perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity);
|
||||
// perfect_hash_seed = find_perfect_hash_seed(perfect_locals_capacity, names);
|
||||
}
|
||||
|
||||
DEF_NATIVE_2(Str, tp_str)
|
||||
@ -596,12 +594,15 @@ inline Str VM::disassemble(CodeObject_ co){
|
||||
argStr += fmt(" (", CAST(Str, asRepr(co->consts[byte.arg])), ")");
|
||||
break;
|
||||
case OP_LOAD_NAME: case OP_LOAD_GLOBAL:
|
||||
case OP_STORE_LOCAL: case OP_STORE_GLOBAL:
|
||||
case OP_STORE_GLOBAL:
|
||||
case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR:
|
||||
case OP_IMPORT_NAME: case OP_BEGIN_CLASS:
|
||||
case OP_DELETE_LOCAL: case OP_DELETE_GLOBAL:
|
||||
case OP_DELETE_GLOBAL:
|
||||
argStr += fmt(" (", co->names[byte.arg].sv(), ")");
|
||||
break;
|
||||
case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST:
|
||||
argStr += fmt(" (", co->varnames[byte.arg].sv(), ")");
|
||||
break;
|
||||
case OP_BINARY_OP:
|
||||
argStr += fmt(" (", BINARY_SPECIAL_METHODS[byte.arg], ")");
|
||||
break;
|
||||
@ -611,22 +612,21 @@ inline Str VM::disassemble(CodeObject_ co){
|
||||
if(i != co->codes.size() - 1) ss << '\n';
|
||||
}
|
||||
|
||||
#if !DEBUG_DIS_EXEC_MIN
|
||||
std::stringstream consts;
|
||||
consts << "co_consts: ";
|
||||
consts << CAST(Str&, asRepr(VAR(co->consts)));
|
||||
// std::stringstream consts;
|
||||
// consts << "co_consts: ";
|
||||
// consts << CAST(Str&, asRepr(VAR(co->consts)));
|
||||
|
||||
// std::stringstream names;
|
||||
// names << "co_names: ";
|
||||
// List list;
|
||||
// for(int i=0; i<co->names.size(); i++){
|
||||
// list.push_back(VAR(co->names[i].sv()));
|
||||
// }
|
||||
// names << CAST(Str, asRepr(VAR(list)));
|
||||
// ss << '\n' << consts.str() << '\n' << names.str();
|
||||
|
||||
std::stringstream names;
|
||||
names << "co_names: ";
|
||||
List list;
|
||||
for(int i=0; i<co->names.size(); i++){
|
||||
list.push_back(VAR(co->names[i].sv()));
|
||||
}
|
||||
names << CAST(Str, asRepr(VAR(list)));
|
||||
ss << '\n' << consts.str() << '\n' << names.str();
|
||||
#endif
|
||||
for(auto& decl: co->func_decls){
|
||||
ss << "\n\n" << "Disassembly of " << decl->name << ":\n";
|
||||
ss << "\n\n" << "Disassembly of " << decl->code->name << ":\n";
|
||||
ss << disassemble(decl->code);
|
||||
}
|
||||
return Str(ss.str());
|
||||
@ -699,33 +699,35 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo
|
||||
return f(this, args);
|
||||
} else if(is_type(callable, tp_function)){
|
||||
const Function& fn = CAST(Function&, callable);
|
||||
NameDict_ locals = make_sp<NameDict>(
|
||||
kLocalsLoadFactor,
|
||||
fn.decl->code->perfect_locals_capacity,
|
||||
fn.decl->code->perfect_hash_seed
|
||||
);
|
||||
const CodeObject* co = fn.decl->code.get();
|
||||
// create a FastLocals with the same size as co->varnames
|
||||
FastLocals locals(co->varnames.size());
|
||||
// zero init
|
||||
for(auto& v: locals) v = nullptr;
|
||||
|
||||
int i = 0;
|
||||
for(StrName name : fn.decl->args){
|
||||
for(int index: fn.decl->args){
|
||||
if(i < args.size()){
|
||||
locals->set(name, args[i++]);
|
||||
continue;
|
||||
locals[index] = args[i++];
|
||||
}else{
|
||||
StrName name = co->varnames[index];
|
||||
TypeError(fmt("missing positional argument ", name.escape()));
|
||||
}
|
||||
TypeError(fmt("missing positional argument ", name.escape()));
|
||||
}
|
||||
|
||||
// NameDict.update is of O(capacity) complexity
|
||||
// so we try not to call it if possible
|
||||
if(fn.decl->kwargs.size()!=0) locals->update(fn.decl->kwargs);
|
||||
|
||||
if(!fn.decl->starred_arg.empty()){
|
||||
// prepare kwdefaults
|
||||
for(auto& kv: fn.decl->kwargs) locals[kv.key] = kv.value;
|
||||
|
||||
// handle *args
|
||||
if(fn.decl->starred_arg != -1){
|
||||
List vargs; // handle *args
|
||||
while(i < args.size()) vargs.push_back(args[i++]);
|
||||
locals->set(fn.decl->starred_arg, VAR(Tuple(std::move(vargs))));
|
||||
locals[fn.decl->starred_arg] = VAR(Tuple(std::move(vargs)));
|
||||
}else{
|
||||
for(StrName key : fn.decl->kwargs_order){
|
||||
// kwdefaults override
|
||||
for(auto& kv: fn.decl->kwargs){
|
||||
if(i < args.size()){
|
||||
locals->set(key, args[i++]);
|
||||
locals[kv.key] = args[i++];
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
@ -734,17 +736,18 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo
|
||||
}
|
||||
|
||||
for(int i=0; i<kwargs.size(); i+=2){
|
||||
const Str& key = CAST(Str&, kwargs[i]);
|
||||
if(!fn.decl->kwargs.contains(key)){
|
||||
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", fn.decl->name, "()"));
|
||||
StrName key = CAST(int, kwargs[i]);
|
||||
auto it = co->varnames_inv.find(key);
|
||||
if(it == co->varnames_inv.end()){
|
||||
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
|
||||
}
|
||||
locals->set(key, kwargs[i+1]);
|
||||
locals[it->second] = kwargs[i+1];
|
||||
}
|
||||
PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module;
|
||||
if(fn.decl->code->is_generator){
|
||||
return PyIter(Generator(this, Frame(fn.decl->code, _module, locals, fn._closure)));
|
||||
if(co->is_generator){
|
||||
return PyIter(Generator(this, Frame(co, _module, std::move(locals), fn._closure)));
|
||||
}
|
||||
_push_new_frame(fn.decl->code, _module, locals, fn._closure);
|
||||
_push_new_frame(co, _module, std::move(locals), fn._closure);
|
||||
if(opCall) return _py_op_call;
|
||||
return _run_top_frame();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user