mirror of https://github.com/arendst/Tasmota.git
1158 lines
27 KiB
C
1158 lines
27 KiB
C
/********************************************************************
|
|
** 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 <string.h>
|
|
|
|
#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);
|
|
}
|