text: /********************************************************************
  ** 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;
          }
          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_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_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) {
          bvalue vk = be_map_key2value(entry);
          var_setval(dst, &vk);
          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);
  }
  
  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);
  }
  
  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);
  }
  