/******************************************************************** ** Copyright (c) 2018-2020 Guan Wenliang ** This file is part of the Berry default interpreter. ** skiars@qq.com, https://github.com/Skiars/berry ** See Copyright Notice in the LICENSE file or at ** https://github.com/Skiars/berry/blob/master/LICENSE ********************************************************************/ #include "be_vm.h" #include "be_func.h" #include "be_class.h" #include "be_string.h" #include "be_vector.h" #include "be_var.h" #include "be_list.h" #include "be_map.h" #include "be_parser.h" #include "be_debug.h" #include "be_exec.h" #include "be_strlib.h" #include "be_module.h" #include "be_gc.h" #include #define retreg(vm) ((vm)->cf->func) static void class_init(bvm *vm, bclass *c, const bnfuncinfo *lib) { if (lib) { while (lib->name) { bstring *s = be_newstr(vm, lib->name); if (lib->function) { /* method */ be_prim_method_bind(vm, c, s, lib->function); } else { be_member_bind(vm, c, s); /* member */ } ++lib; } if (lib->function == (bntvfunc) BE_CLOSURE) { /* next section is closures */ ++lib; while (lib->name) { if (lib->function) { /* method */ bstring *s = be_newstr(vm, lib->name); be_closure_method_bind(vm, c, s, (bclosure*) lib->function); } ++lib; } } be_map_release(vm, c->members); /* clear space */ } } static bclass* class_auto_make(bvm *vm, bstring *name, const bnfuncinfo *lib) { bvalue key, *res; var_setobj(&key, BE_COMPTR, (void*)lib); if (vm->ntvclass == NULL) { vm->ntvclass = be_map_new(vm); } res = be_map_find(vm, vm->ntvclass, &key); if (res == NULL || !var_isclass(res)) { bclass *c; /* insert class to native class table */ res = be_map_insert(vm, vm->ntvclass, &key, NULL); var_setnil(res); /* must be initialized to ensure correct GC */ c = be_newclass(vm, name, NULL); var_setclass(res, c); class_init(vm, c, lib); /* bind members */ return c; } return var_toobj(res); } BERRY_API void be_regfunc(bvm *vm, const char *name, bntvfunc f) { bvalue *var; bstring *s = be_newstr(vm, name); #if !BE_USE_PRECOMPILED_OBJECT int idx = be_builtin_find(vm, s); be_assert(idx == -1); if (idx == -1) { /* new function */ idx = be_builtin_new(vm, s); #else int idx = be_global_find(vm, s); be_assert(idx < be_builtin_count(vm)); if (idx < be_builtin_count(vm)) { /* new function */ idx = be_global_new(vm, s); #endif var = be_global_var(vm, idx); var_setntvfunc(var, f); } /* error case, do nothing */ } BERRY_API void be_regclass(bvm *vm, const char *name, const bnfuncinfo *lib) { bvalue *var; bstring *s = be_newstr(vm, name); #if !BE_USE_PRECOMPILED_OBJECT int idx = be_builtin_find(vm, s); be_assert(idx == -1); if (idx == -1) { /* new function */ idx = be_builtin_new(vm, s); #else int idx = be_global_find(vm, s); be_assert(idx < be_builtin_count(vm)); if (idx < be_builtin_count(vm)) { /* new function */ idx = be_global_new(vm, s); #endif var = be_global_var(vm, idx); var_setclass(var, class_auto_make(vm, s, lib)); } /* error case, do nothing */ } BERRY_API int be_top(bvm *vm) { return cast_int(vm->top - vm->reg); } BERRY_API void be_pop(bvm *vm, int n) { be_assert(n <= vm->top - vm->reg); be_stackpop(vm, n); } BERRY_API int be_absindex(bvm *vm, int index) { if (index > 0) { return index; } be_assert(vm->reg <= vm->top + index); return cast_int(vm->top + index - vm->reg + 1); } BERRY_API bbool be_isnil(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isnil(v); } BERRY_API bbool be_isbool(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isbool(v); } BERRY_API bbool be_isint(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isint(v); } BERRY_API bbool be_isreal(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isreal(v); } BERRY_API bbool be_isnumber(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isnumber(v); } BERRY_API bbool be_isstring(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isstr(v); } BERRY_API bbool be_isclosure(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isclosure(v); } BERRY_API bbool be_isntvclos(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isntvclos(v); } BERRY_API bbool be_isfunction(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isfunction(v); } BERRY_API bbool be_isproto(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isproto(v); } BERRY_API bbool be_isclass(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isclass(v); } BERRY_API bbool be_isinstance(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_isinstance(v); } BERRY_API bbool be_islist(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_islist(v); } BERRY_API bbool be_ismap(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_ismap(v); } BERRY_API bbool be_iscomptr(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_istype(v, BE_COMPTR); } BERRY_API bbool be_iscomobj(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_istype(v, BE_COMOBJ); } BERRY_API bint be_toint(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_toint(v); } BERRY_API breal be_toreal(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_isreal(v)) { return var_toreal(v); } if (var_isint(v)) { return cast(breal, var_toint(v)); } return cast(breal, 0.0); } BERRY_API int be_toindex(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return var_toidx(v); } BERRY_API bbool be_tobool(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return be_value2bool(vm, v); } BERRY_API const char* be_tostring(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (!var_isstr(v)) { be_val2str(vm, index); v = be_indexof(vm, index); } return str(var_tostr(v)); } BERRY_API void* be_tocomptr(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_istype(v, BE_COMPTR)) { return var_toobj(v); } if (var_istype(v, BE_COMOBJ)) { bcommomobj *obj = var_toobj(v); return obj->data; } return NULL; } BERRY_API void be_moveto(bvm *vm, int from, int to) { bvalue *src = be_indexof(vm, from); bvalue *dst = be_indexof(vm, to); var_setval(dst, src); } BERRY_API void be_pushnil(bvm *vm) { bvalue *reg = be_incrtop(vm); var_setnil(reg); } BERRY_API void be_pushbool(bvm *vm, int b) { bvalue *reg = be_incrtop(vm); var_setbool(reg, b != bfalse); } BERRY_API void be_pushint(bvm *vm, bint i) { bvalue *reg = be_incrtop(vm); var_setint(reg, i); } BERRY_API void be_pushreal(bvm *vm, breal r) { bvalue *reg = be_incrtop(vm); var_setreal(reg, r); } BERRY_API void be_pushstring(bvm *vm, const char *str) { /* to create a string and then push the top registor, * otherwise the GC may crash due to uninitialized values. **/ bstring *s = be_newstr(vm, str); bvalue *reg = be_incrtop(vm); be_assert(reg < vm->stacktop); var_setstr(reg, s); } BERRY_API void be_pushnstring(bvm *vm, const char *str, size_t n) { /* to create a string and then push the top registor, * otherwise the GC may crash due to uninitialized values. **/ bstring *s = be_newstrn(vm, str, n); bvalue *reg = be_incrtop(vm); var_setstr(reg, s); } BERRY_API const char* be_pushfstring(bvm *vm, const char *format, ...) { const char* s; va_list arg_ptr; va_start(arg_ptr, format); s = be_pushvfstr(vm, format, arg_ptr); va_end(arg_ptr); return s; } BERRY_API void* be_pushbuffer(bvm *vm, size_t size) { bstring *s = be_newlongstr(vm, NULL, size); bvalue *reg = be_incrtop(vm); var_setstr(reg, s); return (void*)str(s); } BERRY_API void be_pushvalue(bvm *vm, int index) { bvalue *reg = vm->top; var_setval(reg, be_indexof(vm, index)); be_incrtop(vm); } BERRY_API void be_pushclosure(bvm *vm, void *cl) { bvalue *reg = be_incrtop(vm); bclosure * closure = (bclosure*) cl; var_setclosure(reg, closure); } BERRY_API void be_pushntvclosure(bvm *vm, bntvfunc f, int nupvals) { /* to create a native closure and then push the top registor, * otherwise the GC may crash due to uninitialized values. **/ bntvclos *cl = be_newntvclosure(vm, f, nupvals); bvalue *top = be_incrtop(vm); var_setntvclos(top, cl); } BERRY_API void be_pushntvfunction(bvm *vm, bntvfunc f) { bvalue *top = be_incrtop(vm); var_setntvfunc(top, f); } BERRY_API void be_pushclass(bvm *vm, const char *name, const bnfuncinfo *lib) { bclass *c; bstring *s = be_newstr(vm, name); bvalue *dst = be_incrtop(vm); var_setstr(dst, s); c = class_auto_make(vm, s, lib); var_setclass(vm->top - 1, c); } BERRY_API void be_pushntvclass(bvm *vm, const struct bclass * c) { bvalue *top = be_incrtop(vm); var_setclass(top, (bclass *) c); } BERRY_API void be_pushcomptr(bvm *vm, void *ptr) { bvalue *top = be_incrtop(vm); var_setobj(top, BE_COMPTR, ptr); } BERRY_API void be_remove(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); bvalue *top = --vm->top; while (v < top) { *v = v[1]; ++v; } } BERRY_API void be_strconcat(bvm *vm, int index) { bstring *s; bvalue *dst = be_indexof(vm, index); bvalue *src = be_indexof(vm, -1); be_assert(var_isstr(src) && var_isstr(dst)); s = be_strcat(vm, var_tostr(dst), var_tostr(src)); var_setstr(dst, s); } BERRY_API bbool be_setsuper(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); bvalue *top = be_indexof(vm, -1); if (var_isclass(v) && var_isclass(top)) { bclass *c = var_toobj(v); if (!gc_isconst(c)) { bclass *super = var_toobj(top); be_class_setsuper(c, super); return btrue; } } return bfalse; } BERRY_API void be_getsuper(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); bvalue *top = be_incrtop(vm); if (var_isclass(v)) { bclass *c = var_toobj(v); c = be_class_super(c); if (c) { var_setclass(top, c); return; } } else if (var_isinstance(v)) { binstance *o = var_toobj(v); o = be_instance_super(o); if (o) { var_setinstance(top, o); return; } } var_setnil(top); } static bclass* _getclass(bvalue *v) { if (var_isinstance(v)) { binstance *ins = var_toobj(v); return be_instance_class(ins); } return var_isclass(v) ? var_toobj(v) : NULL; } BERRY_API bbool be_isderived(bvm *vm, int index) { bclass *sup = _getclass(be_indexof(vm, -1)); if (sup) { bclass *c = _getclass(be_indexof(vm, index)); while (c && c != sup) c = be_class_super(c); return c != NULL; } return bfalse; } BERRY_API const char *be_typename(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); return be_vtype2str(v); } BERRY_API const char *be_classname(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_isclass(v)) { bclass *c = var_toobj(v); return str(be_class_name(c)); } if (var_isinstance(v)) { binstance *i = var_toobj(v); return str(be_instance_name(i)); } return NULL; } BERRY_API bbool be_classof(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_isinstance(v)) { bvalue *top = be_incrtop(vm); binstance *ins = var_toobj(v); var_setclass(top, be_instance_class(ins)); return btrue; } return bfalse; } BERRY_API int be_strlen(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_isstr(v)) { return str_len(var_tostr(v)); } return 0; } BERRY_API void be_newlist(bvm *vm) { blist *list = be_list_new(vm); bvalue *top = be_incrtop(vm); var_setlist(top, list); } BERRY_API void be_newmap(bvm *vm) { bmap *map = be_map_new(vm); bvalue *top = be_incrtop(vm); var_setobj(top, BE_MAP, map); } BERRY_API void be_newmodule(bvm *vm) { bmodule *mod = be_module_new(vm); bvalue *top = be_incrtop(vm); var_setobj(top, BE_MODULE, mod); } BERRY_API void be_newobject(bvm *vm, const char *name) { be_getbuiltin(vm, name); be_call(vm, 0); be_getmember(vm, -1, ".p"); } BERRY_API bbool be_setname(bvm *vm, int index, const char *name) { bvalue *v = be_indexof(vm, index); if (var_ismodule(v)) { bmodule *module = var_toobj(v); return be_module_setname(module, be_newstr(vm, name)); } return bfalse; } BERRY_API bbool be_getglobal(bvm *vm, const char *name) { int idx = be_global_find(vm, be_newstr(vm, name)); bvalue *top = be_incrtop(vm); if (idx > -1) { *top = *be_global_var(vm, idx); return btrue; } var_setnil(top); return bfalse; } BERRY_API void be_setglobal(bvm *vm, const char *name) { int idx; bstring *s = be_newstr(vm, name); bvalue *v = be_incrtop(vm); var_setstr(v, s); idx = be_global_new(vm, s); v = be_global_var(vm, idx); *v = *be_indexof(vm, -2); be_stackpop(vm, 1); } BERRY_API bbool be_getbuiltin(bvm *vm, const char *name) { int idx = be_builtin_find(vm, be_newstr(vm, name)); bvalue *top = be_incrtop(vm); if (idx > -1) { *top = *be_global_var(vm, idx); return btrue; } var_setnil(top); return bfalse; } BERRY_API bbool be_setmember(bvm *vm, int index, const char *k) { int res = BE_NIL; bvalue *o = be_indexof(vm, index); if (var_isinstance(o)) { bstring *key = be_newstr(vm, k); bvalue *v = be_indexof(vm, -1); binstance *obj = var_toobj(o); res = be_instance_setmember(vm, obj, key, v); } else if (var_ismodule(o)) { bstring *key = be_newstr(vm, k); bmodule *mod = var_toobj(o); bvalue *v = be_module_bind(vm, mod, key); if (v) { *v = *be_indexof(vm, -1); return btrue; } } return res != BE_NIL; } BERRY_API bbool be_copy(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); bvalue *top = be_incrtop(vm); if (var_type(v) == BE_LIST) { blist *list = be_list_copy(vm, var_toobj(v)); var_setlist(top, list) return btrue; } var_setnil(top); return bfalse; } static int ins_member(bvm *vm, int index, const char *k) { int type = BE_NIL; bvalue *o = be_indexof(vm, index); bvalue *top = be_incrtop(vm); var_setnil(top); if (var_isinstance(o)) { binstance *obj = var_toobj(o); type = be_instance_member(vm, obj, be_newstr(vm, k), top); } return type; } BERRY_API bbool be_getmember(bvm *vm, int index, const char *k) { return ins_member(vm, index, k) != BE_NIL; } BERRY_API bbool be_getmethod(bvm *vm, int index, const char *k) { return basetype(ins_member(vm, index, k)) == BE_FUNCTION; } BERRY_API bbool be_getindex(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); bvalue *k = be_indexof(vm, -1); bvalue *dst = be_incrtop(vm); switch (var_type(o)) { case BE_LIST: if (var_isint(k)) { blist *list = cast(blist*, var_toobj(o)); int idx = var_toidx(k); bvalue *src = be_list_index(list, idx); if (src) { var_setval(dst, src); return btrue; } } break; case BE_MAP: if (!var_isnil(k)) { bmap *map = cast(bmap*, var_toobj(o)); bvalue *src = be_map_find(vm, map, k); if (src) { var_setval(dst, src); return btrue; } } break; default: break; } var_setnil(dst); return bfalse; } static bvalue* list_setindex(blist *list, bvalue *key) { int idx = var_toidx(key); if (idx < be_list_count(list)) { return be_list_at(list, idx); } return NULL; } BERRY_API bbool be_setindex(bvm *vm, int index) { bvalue *dst = NULL; bvalue *o = be_indexof(vm, index); bvalue *k = be_indexof(vm, -2); bvalue *v = be_indexof(vm, -1); switch (var_type(o)) { case BE_LIST: if (var_isint(k)) { blist *list = var_toobj(o); dst = list_setindex(list, k); } break; case BE_MAP: if (!var_isnil(k)) { bmap *map = var_toobj(o); dst = be_map_insert(vm, map, k, NULL); } break; default: break; } if (dst) { var_setval(dst, v); return btrue; } return bfalse; } BERRY_API void be_getupval(bvm *vm, int index, int pos) { bvalue *f = index ? be_indexof(vm, index) : vm->cf->func; bvalue *uv, *top = be_incrtop(vm); be_assert(var_istype(f, BE_NTVCLOS)); if (var_istype(f, BE_NTVCLOS)) { bntvclos *nf = var_toobj(f); be_assert(pos >= 0 && pos < nf->nupvals); uv = be_ntvclos_upval(nf, pos)->value; var_setval(top, uv); } else { var_setnil(top); } } BERRY_API bbool be_setupval(bvm *vm, int index, int pos) { bvalue *f = index ? be_indexof(vm, index) : vm->cf->func; bvalue *uv, *v = be_indexof(vm, -1); be_assert(var_istype(f, BE_NTVCLOS)); if (var_istype(f, BE_NTVCLOS)) { bntvclos *nf = var_toobj(f); be_assert(pos >= 0 && pos < nf->nupvals); uv = be_ntvclos_upval(nf, pos)->value; var_setval(uv, v); return btrue; } return bfalse; } BERRY_API int be_data_size(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_islist(v)) { blist *list = var_toobj(v); return be_list_count(list); } else if (var_ismap(v)) { bmap *map = cast(bmap*, var_toobj(v)); return be_map_count(map); } return -1; } BERRY_API void be_data_push(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); bvalue *v = be_indexof(vm, -1); if (var_islist(o)) { blist *list = var_toobj(o); be_list_push(vm, list, v); } } BERRY_API bbool be_data_insert(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); bvalue *k = be_indexof(vm, -2); bvalue *v = be_indexof(vm, -1); switch (var_type(o)) { case BE_MAP: if (!var_isnil(k)) { bmap *map = cast(bmap*, var_toobj(o)); bvalue *dst = be_map_find(vm, map, k); if (dst == NULL) { return be_map_insert(vm, map, k, v) != NULL; } } break; case BE_LIST: if (var_isint(k)) { blist *list = cast(blist*, var_toobj(o)); return be_list_insert(vm, list, var_toidx(k), v) != NULL; } break; default: break; } return bfalse; } BERRY_API bbool be_data_remove(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); bvalue *k = be_indexof(vm, -1); switch (var_type(o)) { case BE_MAP: if (!var_isnil(k)) { bmap *map = cast(bmap*, var_toobj(o)); return be_map_remove(vm, map, k); } break; case BE_LIST: if (var_isint(k)) { blist *list = cast(blist*, var_toobj(o)); return be_list_remove(vm, list, var_toidx(k)); } break; default: break; } return bfalse; } BERRY_API bbool be_data_merge(bvm *vm, int index) { bvalue *a = be_indexof(vm, index); bvalue *b = be_indexof(vm, -1); if (var_islist(a) && var_islist(b)) { blist *dst = var_toobj(a), *src = var_toobj(b); be_list_merge(vm, dst, src); return btrue; } return bfalse; } BERRY_API void be_data_resize(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); bvalue *v = be_indexof(vm, -1); if (var_islist(o)) { blist *list = var_toobj(o); if (var_isint(v)) { be_list_resize(vm, list, var_toidx(v)); } } } BERRY_API void be_data_reverse(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_type(v) == BE_LIST) { be_list_reverse(var_toobj(v)); } } BERRY_API bbool be_pushiter(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); if (var_ismap(v)) { bvalue *iter = be_incrtop(vm); var_setobj(iter, BE_COMPTR, NULL); return btrue; } else if (var_islist(v)) { blist *list = var_toobj(v); bvalue *iter = be_incrtop(vm); var_setobj(iter, BE_COMPTR, be_list_data(list) - 1); return btrue; } return bfalse; } static int list_next(bvm *vm) { bvalue *iter = be_indexof(vm, -1); bvalue *next, *dst = be_incrtop(vm); next = cast(bvalue*, var_toobj(iter)) + 1; var_setobj(iter, BE_COMPTR, next); var_setval(dst, next); return 1; } static bbool list_hasnext(bvm *vm, bvalue *v) { bvalue *next; bvalue *iter = be_indexof(vm, -1); blist *obj = var_toobj(v); next = cast(bvalue*, var_toobj(iter)) + 1; return next >= be_list_data(obj) && next < be_list_end(obj); } static int map_next(bvm *vm, bvalue *v) { bmapiter iter; bmapnode *entry; bvalue *dst = vm->top; bvalue *itvar = be_indexof(vm, -1); iter = var_toobj(itvar); entry = be_map_next(var_toobj(v), &iter); var_setobj(itvar, BE_COMPTR, iter); if (entry) { be_map_key2value(dst, entry); var_setval(dst + 1, &entry->value); vm->top += 2; return 2; } return 0; } static bbool map_hasnext(bvm *vm, bvalue *v) { bvalue *node = be_indexof(vm, -1); bmapiter iter = var_toobj(node); return be_map_next(var_toobj(v), &iter) != NULL; } BERRY_API int be_iter_next(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); if (var_islist(o)) { return list_next(vm); } else if (var_ismap(o)) { return map_next(vm, o); } return 0; } BERRY_API bbool be_iter_hasnext(bvm *vm, int index) { bvalue *o = be_indexof(vm, index); if (var_islist(o)) { return list_hasnext(vm, o); } else if (var_ismap(o)) { return map_hasnext(vm, o); } return bfalse; } BERRY_API bbool be_refcontains(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); binstance **ref = be_stack_base(&vm->refstack); binstance **top = be_stack_top(&vm->refstack); binstance *ins = var_toobj(v); be_assert(var_isinstance(v)); if (ref) { while (ref <= top && *ref != ins) { ++ref; } return ref <= top; } return bfalse; } BERRY_API void be_refpush(bvm *vm, int index) { bvalue *v = be_indexof(vm, index); binstance *ins = var_toobj(v); be_assert(var_isinstance(v)); be_stack_push(vm, &vm->refstack, &ins); } BERRY_API void be_refpop(bvm *vm) { be_stack_pop(&vm->refstack); if (be_stack_isempty(&vm->refstack)) { be_vector_release(vm, &vm->refstack); } } BERRY_API int be_returnvalue(bvm *vm) { bvalue *src = vm->top - 1; bvalue *ret = retreg(vm); var_setval(ret, src); return 0; } BERRY_API int be_returnnilvalue(bvm *vm) { bvalue *ret = retreg(vm); var_setnil(ret); return 0; } BERRY_API void be_call(bvm *vm, int argc) { bvalue *fval = vm->top - argc - 1; be_dofunc(vm, fval, argc); } BERRY_API int be_pcall(bvm *vm, int argc) { bvalue *f = vm->top - argc - 1; return be_protectedcall(vm, f, argc); } __attribute__((noreturn)) BERRY_API void be_raise(bvm *vm, const char *except, const char *msg) { be_pushstring(vm, except); if (msg) { be_pushstring(vm, msg); } else { be_pushnil(vm); } be_pop(vm, 2); be_save_stacktrace(vm); be_throw(vm, BE_EXCEPTION); #ifdef __GNUC__ __builtin_unreachable(); #endif } BERRY_API void be_stop_iteration(bvm *vm) { be_raise(vm, "stop_iteration", NULL); } BERRY_API int be_getexcept(bvm *vm, int code) { if (code == BE_EXCEPTION) { if (be_isstring(vm, -2)) { const char *except = be_tostring(vm, -2); if (!strcmp(except, "syntax_error")) { return BE_SYNTAX_ERROR; } if (!strcmp(except, "io_error")) { return BE_IO_ERROR; } } return BE_EXEC_ERROR; } return code; } static int _dvfunc(bvm *vm, bbool esc) { const char* s = esc ? be_toescape(vm, 1, 'x') : be_tostring(vm, 1); be_writestring(s); be_return_nil(vm); } static int _dumpesc(bvm *vm) { return _dvfunc(vm, btrue); } static int _dumpdir(bvm *vm) { return _dvfunc(vm, bfalse); } static int dump_value(bvm *vm, int index, bbool esc) { int res, top = be_top(vm) + 1; index = be_absindex(vm, index); be_pushntvfunction(vm, esc ? _dumpesc : _dumpdir); be_pushvalue(vm, index); res = be_pcall(vm, 1); /* using index to store result */ be_remove(vm, top); /* remove '_dumpvalue' function */ be_remove(vm, top); /* remove the value */ if (res == BE_EXCEPTION) { be_dumpexcept(vm); } return res; } BERRY_API void be_dumpvalue(bvm *vm, int index) { if (dump_value(vm, index, btrue) == BE_OK) { be_writenewline(); } } BERRY_API void be_dumpexcept(bvm *vm) { do { /* print exception value */ if (dump_value(vm, -2, bfalse)) break; be_writestring(": "); /* print exception argument */ if (dump_value(vm, -1, bfalse)) break; be_writenewline(); /* print stack traceback */ be_tracestack(vm); } while (0); be_pop(vm, 2); /* pop the exception value & argument */ } BERRY_API bbool be_iseq(bvm *vm) { be_assert(vm->reg + 2 <= vm->top); return be_vm_iseq(vm, vm->top - 2, vm->top - 1); } BERRY_API bbool be_isneq(bvm *vm) { be_assert(vm->reg + 2 <= vm->top); return be_vm_isneq(vm, vm->top - 2, vm->top - 1); } BERRY_API bbool be_islt(bvm *vm) { be_assert(vm->reg + 2 <= vm->top); return be_vm_islt(vm, vm->top - 2, vm->top - 1); } BERRY_API bbool be_isle(bvm *vm) { be_assert(vm->reg + 2 <= vm->top); return be_vm_isle(vm, vm->top - 2, vm->top - 1); } BERRY_API bbool be_isgt(bvm *vm) { be_assert(vm->reg + 2 <= vm->top); return be_vm_isgt(vm, vm->top - 2, vm->top - 1); } BERRY_API bbool be_isge(bvm *vm) { be_assert(vm->reg + 2 <= vm->top); return be_vm_isge(vm, vm->top - 2, vm->top - 1); } BERRY_API int be_register(bvm *vm, int index) { bvalue *v; if (!vm->registry) { vm->registry = be_list_new(vm); be_list_pool_init(vm, vm->registry); } be_assert(vm->registry != NULL); v = be_indexof(vm, index); return be_list_pool_alloc(vm, vm->registry, v); } BERRY_API void be_unregister(bvm *vm, int id) { be_assert(vm->registry != NULL); be_list_pool_free(vm->registry, id); } BERRY_API void be_getregister(bvm *vm, int id) { blist *reg = vm->registry; be_assert(reg && id > 0 && id < be_list_count(reg)); var_setval(vm->top, be_list_at(reg, id)); be_incrtop(vm); }