mirror of
https://github.com/arendst/Tasmota.git
synced 2025-04-24 23:07:17 +00:00
1291 lines
43 KiB
C
1291 lines
43 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_decoder.h"
|
||
#include "be_string.h"
|
||
#include "be_strlib.h"
|
||
#include "be_class.h"
|
||
#include "be_func.h"
|
||
#include "be_vector.h"
|
||
#include "be_list.h"
|
||
#include "be_map.h"
|
||
#include "be_module.h"
|
||
#include "be_mem.h"
|
||
#include "be_var.h"
|
||
#include "be_gc.h"
|
||
#include "be_exec.h"
|
||
#include "be_debug.h"
|
||
#include "be_libs.h"
|
||
#include <string.h>
|
||
#include <math.h>
|
||
|
||
#define NOT_METHOD BE_NONE
|
||
|
||
#define vm_error(vm, except, ...) \
|
||
be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__))
|
||
|
||
#define RA() (reg + IGET_RA(ins)) /* Get value of register A */
|
||
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins))) /* Get value of register or constant B */
|
||
#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins))) /* Get value of register or constant C */
|
||
|
||
#define var2cl(_v) cast(bclosure*, var_toobj(_v)) /* cast var to closure */
|
||
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i) /* get var as real or convert to real if integer */
|
||
#define val2bool(v) ((v) ? btrue : bfalse) /* get var as bool (trur if non zero) */
|
||
#define ibinop(op, a, b) ((a)->v.i op (b)->v.i) /* apply binary operator to both arguments as integers */
|
||
|
||
#if BE_USE_DEBUG_HOOK
|
||
#define DEBUG_HOOK() \
|
||
if (vm->hookmask & BE_HOOK_LINE) { \
|
||
do_linehook(vm); \
|
||
reg = vm->reg; \
|
||
}
|
||
#else
|
||
#define DEBUG_HOOK()
|
||
#endif
|
||
|
||
#if BE_USE_PERF_COUNTERS
|
||
#define COUNTER_HOOK() \
|
||
vm->counter_ins++;
|
||
#else
|
||
#define COUNTER_HOOK()
|
||
#endif
|
||
|
||
#if BE_USE_PERF_COUNTERS
|
||
#define VM_HEARTBEAT() \
|
||
if ((vm->counter_ins & ((1<<(BE_VM_OBSERVABILITY_SAMPLING - 1))-1) ) == 0) { /* call every 2^BE_VM_OBSERVABILITY_SAMPLING instructions */ \
|
||
if (vm->obshook != NULL) \
|
||
(*vm->obshook)(vm, BE_OBS_VM_HEARTBEAT, vm->counter_ins); \
|
||
}
|
||
#else
|
||
#define VM_HEARTBEAT()
|
||
#endif
|
||
|
||
#define vm_exec_loop() \
|
||
loop: \
|
||
DEBUG_HOOK(); \
|
||
COUNTER_HOOK(); \
|
||
VM_HEARTBEAT(); \
|
||
switch (IGET_OP(ins = *vm->ip++))
|
||
|
||
#if BE_USE_SINGLE_FLOAT
|
||
#define mathfunc(func) func##f
|
||
#else
|
||
#define mathfunc(func) func
|
||
#endif
|
||
|
||
#define opcase(opcode) case OP_##opcode
|
||
#define dispatch() goto loop
|
||
|
||
#define equal_rule(op, iseq) \
|
||
bbool res; \
|
||
be_assert(!var_isstatic(a)); \
|
||
be_assert(!var_isstatic(b)); \
|
||
if (var_isint(a) && var_isint(b)) { \
|
||
res = ibinop(op, a, b); \
|
||
} else if (var_isnumber(a) && var_isnumber(b)) { \
|
||
res = var2real(a) op var2real(b); \
|
||
} else if (var_isinstance(a) && !var_isnil(b)) { \
|
||
res = object_eqop(vm, #op, iseq, a, b); \
|
||
} else if (var_primetype(a) == var_primetype(b)) { /* same types */ \
|
||
if (var_isnil(a)) { /* nil op nil */ \
|
||
res = 1 op 1; \
|
||
} else if (var_isbool(a)) { /* bool op bool */ \
|
||
res = var_tobool(a) op var_tobool(b); \
|
||
} else if (var_isstr(a)) { /* string op string */ \
|
||
res = 1 op be_eqstr(a->v.s, b->v.s); \
|
||
} else if (var_isclass(a) || var_isfunction(a) || var_iscomptr(a)) { \
|
||
res = var_toobj(a) op var_toobj(b); \
|
||
} else { \
|
||
binop_error(vm, #op, a, b); \
|
||
res = bfalse; /* will not be executed */ \
|
||
} \
|
||
} else { /* different types */ \
|
||
res = 1 op 0; \
|
||
} \
|
||
return res
|
||
|
||
#define relop_rule(op) \
|
||
bbool res; \
|
||
if (var_isint(a) && var_isint(b)) { \
|
||
res = ibinop(op, a, b); \
|
||
} else if (var_isnumber(a) && var_isnumber(b)) { \
|
||
res = var2real(a) op var2real(b); \
|
||
} else if (var_isstr(a) && var_isstr(b)) { \
|
||
bstring *s1 = var_tostr(a), *s2 = var_tostr(b); \
|
||
res = be_strcmp(s1, s2) op 0; \
|
||
} else if (var_isinstance(a)) { \
|
||
binstance *obj = var_toobj(a); \
|
||
object_binop(vm, #op, *a, *b); \
|
||
check_bool(vm, obj, #op); \
|
||
res = var_tobool(vm->top); \
|
||
} else { \
|
||
binop_error(vm, #op, a, b); \
|
||
res = bfalse; /* will not be executed */ \
|
||
} \
|
||
return res
|
||
|
||
#define bitwise_block(op) \
|
||
bvalue *dst = RA(), *a = RKB(), *b = RKC(); \
|
||
if (var_isint(a) && var_isint(b)) { \
|
||
var_setint(dst, ibinop(op, a, b)); \
|
||
} else if (var_isinstance(a)) { \
|
||
ins_binop(vm, #op, ins); \
|
||
} else { \
|
||
binop_error(vm, #op, a, b); \
|
||
}
|
||
|
||
#define push_native(_vm, _f, _ns, _t) { \
|
||
precall(_vm, _f, _ns, _t); \
|
||
_vm->cf->status = PRIM_FUNC; \
|
||
}
|
||
|
||
static void prep_closure(bvm *vm, int pos, int argc, int mode);
|
||
|
||
static void attribute_error(bvm *vm, const char *t, bvalue *b, bvalue *c)
|
||
{
|
||
const char *attr = var_isstr(c) ? str(var_tostr(c)) : be_vtype2str(c);
|
||
vm_error(vm, "attribute_error",
|
||
"'%s' value has no %s '%s'", be_vtype2str(b), t, attr);
|
||
}
|
||
|
||
static void binop_error(bvm *vm, const char *op, bvalue *a, bvalue *b)
|
||
{
|
||
vm_error(vm, "type_error",
|
||
"unsupported operand type(s) for %s: '%s' and '%s'",
|
||
op, be_vtype2str(a), be_vtype2str(b));
|
||
}
|
||
|
||
static void unop_error(bvm *vm, const char *op, bvalue *a)
|
||
{
|
||
vm_error(vm, "type_error",
|
||
"unsupported operand type(s) for %s: '%s'",
|
||
op, be_vtype2str(a));
|
||
}
|
||
|
||
static void call_error(bvm *vm, bvalue *v)
|
||
{
|
||
vm_error(vm, "type_error",
|
||
"'%s' value is not callable", be_vtype2str(v));
|
||
}
|
||
|
||
/* Check that the return value is bool or raise an exception */
|
||
/* `obj` and `method` are only passed for error reporting */
|
||
static void check_bool(bvm *vm, binstance *obj, const char *method)
|
||
{
|
||
if (!var_isbool(vm->top)) {
|
||
const char *name = str(be_instance_name(obj));
|
||
vm_error(vm, "type_error",
|
||
"`%s::%s` return value error, the expected type is 'bool'",
|
||
strlen(name) ? name : "<anonymous>", method);
|
||
}
|
||
}
|
||
|
||
#if BE_USE_DEBUG_HOOK
|
||
static void do_linehook(bvm *vm)
|
||
{
|
||
bcallframe *cf = vm->cf;
|
||
bclosure *cl = var_toobj(cf->func);
|
||
int pc = cast_int(vm->ip - cl->proto->code);
|
||
if (!pc || pc > cf->lineinfo->endpc) {
|
||
while (pc > cf->lineinfo->endpc)
|
||
cf->lineinfo++;
|
||
be_callhook(vm, BE_HOOK_LINE);
|
||
} else {
|
||
blineinfo *linfo = cf->lineinfo;
|
||
blineinfo *base = cl->proto->lineinfo;
|
||
while (linfo > base && pc <= linfo[-1].endpc)
|
||
linfo--;
|
||
if (cf->lineinfo != linfo) {
|
||
cf->lineinfo = linfo;
|
||
be_callhook(vm, BE_HOOK_LINE);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* Prepare the stack for the function/method call */
|
||
/* `func` is a pointer to the function/method on the stack, it contains the closure before call and the result after the call */
|
||
/* `nstackˋ is the stack depth used by the function (determined by compiler), we add BE_STACK_FREE_MIN as a safety margin */
|
||
static void precall(bvm *vm, bvalue *func, int nstack, int mode)
|
||
{
|
||
bcallframe *cf;
|
||
int expan = nstack + BE_STACK_FREE_MIN; /* `expan` is the minimum required space on the stack */
|
||
if (vm->stacktop < func + expan) { /* do we have too little space left on the stack? */
|
||
size_t fpos = func - vm->stack; /* compute offset of `func` from base stack, in case stack is reallocated and base address changes */
|
||
be_stack_expansion(vm, expan); /* expand stack (vector object), warning stack address changes */
|
||
func = vm->stack + fpos; /* recompute `func` address with new stack address */
|
||
}
|
||
be_stack_push(vm, &vm->callstack, NULL); /* push a NULL value on callstack */
|
||
cf = be_stack_top(&vm->callstack); /* get address of new callframe at top of callstack */
|
||
cf->func = func - mode;
|
||
cf->top = vm->top; /* save previous stack top */
|
||
cf->reg = vm->reg; /* save previous stack base */
|
||
vm->reg = func + 1; /* new stack base is right after function */
|
||
vm->top = vm->reg + nstack; /* new stack top is above the registers used by the function, so we don´t mess with them */
|
||
vm->cf = cf; /* set new current callframe */
|
||
}
|
||
|
||
/* Prepare call of closure, setting the instruction pointer (ip) */
|
||
static void push_closure(bvm *vm, bvalue *func, int nstack, int mode)
|
||
{
|
||
bclosure *cl = var_toobj(func);
|
||
precall(vm, func, nstack, mode);
|
||
vm->cf->ip = vm->ip;
|
||
vm->cf->status = NONE_FLAG;
|
||
vm->ip = cl->proto->code;
|
||
#if BE_USE_DEBUG_HOOK
|
||
vm->cf->lineinfo = cl->proto->lineinfo;
|
||
be_callhook(vm, BE_HOOK_CALL);
|
||
#endif
|
||
}
|
||
|
||
static void ret_native(bvm *vm)
|
||
{
|
||
bcallframe *_cf = vm->cf;
|
||
vm->reg = _cf->reg;
|
||
vm->top = _cf->top;
|
||
be_stack_pop(&vm->callstack);
|
||
vm->cf = be_stack_top(&vm->callstack);
|
||
}
|
||
|
||
static bbool obj2bool(bvm *vm, bvalue *var)
|
||
{
|
||
binstance *obj = var_toobj(var);
|
||
bstring *tobool = str_literal(vm, "tobool");
|
||
/* get operator method */
|
||
// TODO what if `tobool` is static
|
||
int type = be_instance_member(vm, obj, tobool, vm->top);
|
||
if (type != BE_NONE && type != BE_NIL) {
|
||
vm->top[1] = *var; /* move self to argv[0] */
|
||
be_dofunc(vm, vm->top, 1); /* call method 'tobool' */
|
||
/* check the return value */
|
||
check_bool(vm, obj, "tobool");
|
||
return var_tobool(vm->top);
|
||
}
|
||
return btrue;
|
||
}
|
||
|
||
bbool be_value2bool(bvm *vm, bvalue *v)
|
||
{
|
||
switch (var_basetype(v)) {
|
||
case BE_NIL:
|
||
return bfalse;
|
||
case BE_BOOL:
|
||
return var_tobool(v);
|
||
case BE_INT:
|
||
return val2bool(v->v.i);
|
||
case BE_REAL:
|
||
return val2bool(v->v.r);
|
||
case BE_INSTANCE:
|
||
return obj2bool(vm, v);
|
||
default:
|
||
return btrue;
|
||
}
|
||
}
|
||
|
||
static void obj_method(bvm *vm, bvalue *o, bstring *attr, bvalue *dst)
|
||
{
|
||
binstance *obj = var_toobj(o);
|
||
int type = be_instance_member_simple(vm, obj, attr, dst);
|
||
if (basetype(type) != BE_FUNCTION) {
|
||
vm_error(vm, "attribute_error",
|
||
"the '%s' object has no method '%s'",
|
||
str(be_instance_name(obj)), str(attr));
|
||
}
|
||
}
|
||
|
||
static int obj_attribute(bvm *vm, bvalue *o, bstring *attr, bvalue *dst)
|
||
{
|
||
binstance *obj = var_toobj(o);
|
||
int type = be_instance_member(vm, obj, attr, dst);
|
||
if (type == BE_NONE) {
|
||
vm_error(vm, "attribute_error",
|
||
"the '%s' object has no attribute '%s'",
|
||
str(be_instance_name(obj)), str(attr));
|
||
}
|
||
return type;
|
||
}
|
||
|
||
static int class_attribute(bvm *vm, bvalue *o, bvalue *c, bvalue *dst)
|
||
{
|
||
bstring *attr = var_tostr(c);
|
||
bclass *obj = var_toobj(o);
|
||
int type = be_class_member(vm, obj, attr, dst);
|
||
if (type == BE_NONE || type == BE_INDEX) {
|
||
vm_error(vm, "attribute_error",
|
||
"the '%s' class has no static attribute '%s'",
|
||
str(obj->name), str(attr));
|
||
}
|
||
return type;
|
||
}
|
||
|
||
static int module_attribute(bvm *vm, bvalue *o, bvalue *c, bvalue *dst)
|
||
{
|
||
bstring *attr = var_tostr(c);
|
||
bmodule *module = var_toobj(o);
|
||
int type = be_module_attr(vm, module, attr, dst);
|
||
if (type == BE_NONE) {
|
||
vm_error(vm, "attribute_error",
|
||
"module '%s' has no member '%s'",
|
||
be_module_name(module), str(attr));
|
||
}
|
||
return type;
|
||
}
|
||
|
||
static bbool object_eqop(bvm *vm,
|
||
const char *op, bbool iseq, bvalue *a, bvalue *b)
|
||
{
|
||
binstance *o = var_toobj(a);
|
||
bvalue self = *a, other = *b;
|
||
bbool isself = var_isinstance(b) && o == var_toobj(b);
|
||
/* first, try to call the overloaded operator of the object */
|
||
int type = be_instance_member(vm, o, be_newstr(vm, op), vm->top);
|
||
// TODO check that method is not static
|
||
if (basetype(type) == BE_FUNCTION) { /* call method */
|
||
bvalue *top = vm->top;
|
||
top[1] = self; /* move self to argv[0] */
|
||
top[2] = other; /* move other to argv[1] */
|
||
be_incrtop(vm); /* prevent collection results */
|
||
be_dofunc(vm, top, 2); /* call method 'item' */
|
||
be_stackpop(vm, 1);
|
||
check_bool(vm, o, op); /* check return value */
|
||
return var_tobool(vm->top); /* copy result to dst */
|
||
}
|
||
/* the default equal operation rule */
|
||
return iseq == isself; /* check object self */
|
||
}
|
||
|
||
static void object_binop(bvm *vm, const char *op, bvalue self, bvalue other)
|
||
{
|
||
bvalue *top = vm->top;
|
||
/* get operator method (possible GC) */
|
||
obj_method(vm, &self, be_newstr(vm, op), vm->top);
|
||
top[1] = self; /* move self to argv[0] */
|
||
top[2] = other; /* move other to argv[1] */
|
||
be_incrtop(vm); /* prevent collection results */
|
||
be_dofunc(vm, top, 2); /* call method 'item' */
|
||
be_stackpop(vm, 1);
|
||
}
|
||
|
||
#define ins_binop(vm, op, ins) { \
|
||
object_binop(vm, op, *RKB(), *RKC()); \
|
||
reg = vm->reg; \
|
||
*RA() = *vm->top; /* copy result to dst */ \
|
||
}
|
||
|
||
static void ins_unop(bvm *vm, const char *op, bvalue self)
|
||
{
|
||
bvalue *top = vm->top;
|
||
/* get operator method (possible GC) */
|
||
obj_method(vm, &self, be_newstr(vm, op), vm->top);
|
||
top[1] = self; /* move self to argv[0] */
|
||
be_dofunc(vm, top, 1); /* call method 'item' */
|
||
}
|
||
|
||
bbool be_vm_iseq(bvm *vm, bvalue *a, bvalue *b)
|
||
{
|
||
equal_rule(==, btrue);
|
||
}
|
||
|
||
bbool be_vm_isneq(bvm *vm, bvalue *a, bvalue *b)
|
||
{
|
||
equal_rule(!=, bfalse);
|
||
}
|
||
|
||
bbool be_vm_islt(bvm *vm, bvalue *a, bvalue *b)
|
||
{
|
||
relop_rule(<);
|
||
}
|
||
|
||
bbool be_vm_isle(bvm *vm, bvalue *a, bvalue *b)
|
||
{
|
||
relop_rule(<=);
|
||
}
|
||
|
||
bbool be_vm_isgt(bvm *vm, bvalue *a, bvalue *b)
|
||
{
|
||
relop_rule(>);
|
||
}
|
||
|
||
bbool be_vm_isge(bvm *vm, bvalue *a, bvalue *b)
|
||
{
|
||
relop_rule(>=);
|
||
}
|
||
|
||
static void make_range(bvm *vm, bvalue lower, bvalue upper)
|
||
{
|
||
/* get method 'item' (possible GC) */
|
||
int idx = be_builtin_find(vm, str_literal(vm, "range"));
|
||
bvalue *top = vm->top;
|
||
top[0] = *be_global_var(vm, idx);
|
||
top[1] = lower; /* move lower to argv[0] */
|
||
top[2] = upper; /* move upper to argv[1] */
|
||
vm->top += 3; /* prevent collection results */
|
||
be_dofunc(vm, top, 2); /* call method 'item' */
|
||
vm->top -= 3;
|
||
}
|
||
|
||
static void connect_str(bvm *vm, bstring *a, bvalue *b)
|
||
{
|
||
bstring *s;
|
||
if (var_isstr(b)) {
|
||
s = be_strcat(vm, a, var_tostr(b));
|
||
var_setstr(vm->top, s);
|
||
} else {
|
||
*vm->top++ = *b;
|
||
be_val2str(vm, -1);
|
||
b = vm->top - 1;
|
||
s = be_strcat(vm, a, var_tostr(b));
|
||
var_setstr(b, s);
|
||
vm->top -= 1;
|
||
}
|
||
}
|
||
|
||
BERRY_API bvm* be_vm_new(void)
|
||
{
|
||
bvm *vm = be_os_malloc(sizeof(bvm));
|
||
be_assert(vm != NULL);
|
||
memset(vm, 0, sizeof(bvm)); /* clear all members */
|
||
be_gc_init(vm);
|
||
be_string_init(vm);
|
||
be_stack_init(vm, &vm->callstack, sizeof(bcallframe));
|
||
be_stack_init(vm, &vm->refstack, sizeof(binstance*));
|
||
be_stack_init(vm, &vm->exceptstack, sizeof(struct bexecptframe));
|
||
be_stack_init(vm, &vm->tracestack, sizeof(bcallsnapshot));
|
||
vm->stack = be_malloc(vm, sizeof(bvalue) * BE_STACK_START);
|
||
vm->stacktop = vm->stack + BE_STACK_START;
|
||
vm->reg = vm->stack;
|
||
vm->top = vm->reg;
|
||
be_globalvar_init(vm);
|
||
be_gc_setpause(vm, 1);
|
||
be_loadlibs(vm);
|
||
vm->compopt = 0;
|
||
vm->obshook = NULL;
|
||
vm->ctypefunc = NULL;
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_ins = 0;
|
||
vm->counter_enter = 0;
|
||
vm->counter_call = 0;
|
||
vm->counter_get = 0;
|
||
vm->counter_set = 0;
|
||
vm->counter_try = 0;
|
||
vm->counter_exc = 0;
|
||
vm->counter_gc_kept = 0;
|
||
vm->counter_gc_freed = 0;
|
||
#endif
|
||
return vm;
|
||
}
|
||
|
||
BERRY_API void be_vm_delete(bvm *vm)
|
||
{
|
||
be_gc_deleteall(vm);
|
||
be_string_deleteall(vm);
|
||
be_stack_delete(vm, &vm->callstack);
|
||
be_stack_delete(vm, &vm->refstack);
|
||
be_stack_delete(vm, &vm->exceptstack);
|
||
be_stack_delete(vm, &vm->tracestack);
|
||
be_free(vm, vm->stack, (vm->stacktop - vm->stack) * sizeof(bvalue));
|
||
be_globalvar_deinit(vm);
|
||
#if BE_USE_DEBUG_HOOK
|
||
/* free native hook */
|
||
if (var_istype(&vm->hook, BE_COMPTR))
|
||
be_free(vm, var_toobj(&vm->hook), sizeof(struct bhookblock));
|
||
#endif
|
||
/* free VM structure */
|
||
be_os_free(vm);
|
||
}
|
||
|
||
static void vm_exec(bvm *vm)
|
||
{
|
||
bclosure *clos;
|
||
bvalue *ktab, *reg;
|
||
binstruction ins;
|
||
vm->cf->status |= BASE_FRAME;
|
||
newframe: /* a new call frame */
|
||
be_assert(var_isclosure(vm->cf->func));
|
||
clos = var_toobj(vm->cf->func); /* `clos` is the current function/closure */
|
||
ktab = clos->proto->ktab; /* `ktab` is the current constant table */
|
||
reg = vm->reg; /* `reg` is the current stack base for the callframe */
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_enter++;
|
||
#endif
|
||
vm_exec_loop() {
|
||
opcase(LDNIL): {
|
||
var_setnil(RA());
|
||
dispatch();
|
||
}
|
||
opcase(LDBOOL): {
|
||
bvalue *v = RA();
|
||
var_setbool(v, IGET_RKB(ins));
|
||
if (IGET_RKC(ins)) { /* skip next instruction */
|
||
vm->ip += 1;
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(LDINT): {
|
||
bvalue *v = RA();
|
||
var_setint(v, IGET_sBx(ins));
|
||
dispatch();
|
||
}
|
||
opcase(LDCONST): {
|
||
bvalue *dst = RA();
|
||
*dst = ktab[IGET_Bx(ins)];
|
||
dispatch();
|
||
}
|
||
opcase(GETGBL): {
|
||
bvalue *v = RA();
|
||
int idx = IGET_Bx(ins);
|
||
*v = *be_global_var(vm, idx);
|
||
dispatch();
|
||
}
|
||
opcase(GETNGBL): { /* get Global by name */
|
||
bvalue *v = RA();
|
||
bvalue *b = RKB();
|
||
if (var_isstr(b)) {
|
||
bstring *name = var_tostr(b);
|
||
int idx = be_global_find(vm, name);
|
||
if (idx > -1) {
|
||
*v = *be_global_var(vm, idx);
|
||
} else {
|
||
vm_error(vm, "attribute_error", "'%s' undeclared", str(name));
|
||
}
|
||
} else {
|
||
vm_error(vm, "internal_error", "global name must be a string");
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(SETNGBL): { /* set Global by name */
|
||
bvalue *v = RA();
|
||
bvalue *b = RKB();
|
||
if (var_isstr(b)) {
|
||
bstring *name = var_tostr(b);
|
||
int idx = be_global_new(vm, name);
|
||
*be_global_var(vm, idx) = *v;
|
||
} else {
|
||
vm_error(vm, "internal_error", "global name must be a string");
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(SETGBL): {
|
||
bvalue *v = RA();
|
||
int idx = IGET_Bx(ins);
|
||
*be_global_var(vm, idx) = *v;
|
||
dispatch();
|
||
}
|
||
opcase(GETUPV): {
|
||
bvalue *v = RA();
|
||
int idx = IGET_Bx(ins);
|
||
be_assert(*clos->upvals != NULL);
|
||
*v = *clos->upvals[idx]->value;
|
||
dispatch();
|
||
}
|
||
opcase(SETUPV): {
|
||
bvalue *v = RA();
|
||
int idx = IGET_Bx(ins);
|
||
be_assert(*clos->upvals != NULL);
|
||
*clos->upvals[idx]->value = *v;
|
||
dispatch();
|
||
}
|
||
opcase(MOVE): {
|
||
bvalue *dst = RA();
|
||
*dst = *RKB();
|
||
dispatch();
|
||
}
|
||
opcase(ADD): {
|
||
bvalue *dst = RA(), *a = RKB(), *b = RKC();
|
||
if (var_isint(a) && var_isint(b)) {
|
||
var_setint(dst, ibinop(+, a, b));
|
||
} else if (var_isnumber(a) && var_isnumber(b)) {
|
||
union bvaldata x, y; // TASMOTA workaround for ESP32 rev0 bug
|
||
x.i = a->v.i;
|
||
if (var_isint(a)) { x.r = (breal) x.i; }
|
||
y.i = b->v.i;
|
||
if (var_isint(b)) { y.r = (breal) y.i; }
|
||
// breal x = var2real(a), y = var2real(b);
|
||
var_setreal(dst, x.r + y.r);
|
||
} else if (var_isstr(a) && var_isstr(b)) { /* strcat */
|
||
bstring *s = be_strcat(vm, var_tostr(a), var_tostr(b));
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setstr(dst, s);
|
||
} else if (var_isinstance(a)) {
|
||
ins_binop(vm, "+", ins);
|
||
} else {
|
||
binop_error(vm, "+", a, b);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(SUB): {
|
||
bvalue *dst = RA(), *a = RKB(), *b = RKC();
|
||
if (var_isint(a) && var_isint(b)) {
|
||
var_setint(dst, ibinop(-, a, b));
|
||
} else if (var_isnumber(a) && var_isnumber(b)) {
|
||
breal x = var2real(a), y = var2real(b);
|
||
var_setreal(dst, x - y);
|
||
} else if (var_isinstance(a)) {
|
||
ins_binop(vm, "-", ins);
|
||
} else {
|
||
binop_error(vm, "-", a, b);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(MUL): {
|
||
bvalue *dst = RA(), *a = RKB(), *b = RKC();
|
||
if (var_isint(a) && var_isint(b)) {
|
||
var_setint(dst, ibinop(*, a, b));
|
||
} else if (var_isnumber(a) && var_isnumber(b)) {
|
||
breal x = var2real(a), y = var2real(b);
|
||
var_setreal(dst, x * y);
|
||
} else if (var_isinstance(a)) {
|
||
ins_binop(vm, "*", ins);
|
||
} else {
|
||
binop_error(vm, "*", a, b);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(DIV): {
|
||
bvalue *dst = RA(), *a = RKB(), *b = RKC();
|
||
if (var_isint(a) && var_isint(b)) {
|
||
bint x = var_toint(a), y = var_toint(b);
|
||
if (y == 0) {
|
||
vm_error(vm, "divzero_error", "division by zero");
|
||
} else {
|
||
var_setint(dst, x / y);
|
||
}
|
||
} else if (var_isnumber(a) && var_isnumber(b)) {
|
||
breal x = var2real(a), y = var2real(b);
|
||
if (y == cast(breal, 0)) {
|
||
vm_error(vm, "divzero_error", "division by zero");
|
||
}
|
||
var_setreal(dst, x / y);
|
||
} else if (var_isinstance(a)) {
|
||
ins_binop(vm, "/", ins);
|
||
} else {
|
||
binop_error(vm, "/", a, b);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(MOD): {
|
||
bvalue *dst = RA(), *a = RKB(), *b = RKC();
|
||
if (var_isint(a) && var_isint(b)) {
|
||
var_setint(dst, ibinop(%, a, b));
|
||
} else if (var_isnumber(a) && var_isnumber(b)) {
|
||
var_setreal(dst, mathfunc(fmod)(var_toreal(a), var_toreal(b)));
|
||
} else if (var_isinstance(a)) {
|
||
ins_binop(vm, "%", ins);
|
||
} else {
|
||
binop_error(vm, "%", a, b);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(LT): {
|
||
bbool res = be_vm_islt(vm, RKB(), RKC());
|
||
bvalue *dst;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setbool(dst, res);
|
||
dispatch();
|
||
}
|
||
opcase(LE): {
|
||
bbool res = be_vm_isle(vm, RKB(), RKC());
|
||
bvalue *dst;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setbool(dst, res);
|
||
dispatch();
|
||
}
|
||
opcase(EQ): {
|
||
bbool res = be_vm_iseq(vm, RKB(), RKC());
|
||
bvalue *dst;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setbool(dst, res);
|
||
dispatch();
|
||
}
|
||
opcase(NE): {
|
||
bbool res = be_vm_isneq(vm, RKB(), RKC());
|
||
bvalue *dst;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setbool(dst, res);
|
||
dispatch();
|
||
}
|
||
opcase(GT): {
|
||
bbool res = be_vm_isgt(vm, RKB(), RKC());
|
||
bvalue *dst;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setbool(dst, res);
|
||
dispatch();
|
||
}
|
||
opcase(GE): {
|
||
bbool res = be_vm_isge(vm, RKB(), RKC());
|
||
bvalue *dst;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setbool(dst, res);
|
||
dispatch();
|
||
}
|
||
opcase(CONNECT): {
|
||
bvalue *a = RKB(), *b = RKC();
|
||
if (var_isint(a) && var_isint(b)) {
|
||
make_range(vm, *RKB(), *RKC());
|
||
} else if (var_isstr(a)) {
|
||
connect_str(vm, var_tostr(a), b);
|
||
} else if (var_isinstance(a)) {
|
||
object_binop(vm, "..", *RKB(), *RKC());
|
||
} else {
|
||
binop_error(vm, "..", RKB(), RKC());
|
||
}
|
||
reg = vm->reg;
|
||
*RA() = *vm->top; /* copy result to R(A) */
|
||
dispatch();
|
||
}
|
||
opcase(AND): {
|
||
bitwise_block(&);
|
||
dispatch();
|
||
}
|
||
opcase(OR): {
|
||
bitwise_block(|);
|
||
dispatch();
|
||
}
|
||
opcase(XOR): {
|
||
bitwise_block(^);
|
||
dispatch();
|
||
}
|
||
opcase(SHL): {
|
||
bitwise_block(<<);
|
||
dispatch();
|
||
}
|
||
opcase(SHR): {
|
||
bitwise_block(>>);
|
||
dispatch();
|
||
}
|
||
opcase(NEG): {
|
||
bvalue *dst = RA(), *a = RKB();
|
||
if (var_isint(a)) {
|
||
var_setint(dst, -a->v.i);
|
||
} else if (var_isreal(a)) {
|
||
var_setreal(dst, -a->v.r);
|
||
} else if (var_isinstance(a)) {
|
||
ins_unop(vm, "-*", *RKB());
|
||
reg = vm->reg;
|
||
*RA() = *vm->top; /* copy result to dst */
|
||
} else {
|
||
unop_error(vm, "-", a);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(FLIP): {
|
||
bvalue *dst = RA(), *a = RKB();
|
||
if (var_isint(a)) {
|
||
var_setint(dst, ~a->v.i);
|
||
} else if (var_isinstance(a)) {
|
||
ins_unop(vm, "~", *RKB());
|
||
reg = vm->reg;
|
||
*RA() = *vm->top; /* copy result to dst */
|
||
} else {
|
||
unop_error(vm, "~", a);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(JMP): {
|
||
vm->ip += IGET_sBx(ins);
|
||
dispatch();
|
||
}
|
||
opcase(JMPT): {
|
||
if (be_value2bool(vm, RA())) {
|
||
vm->ip += IGET_sBx(ins);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(JMPF): {
|
||
if (!be_value2bool(vm, RA())) {
|
||
vm->ip += IGET_sBx(ins);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(CLOSURE): {
|
||
bvalue *dst;
|
||
bproto *p = clos->proto->ptab[IGET_Bx(ins)];
|
||
bclosure *cl = be_newclosure(vm, p->nupvals);
|
||
cl->proto = p;
|
||
reg = vm->reg;
|
||
dst = RA();
|
||
var_setclosure(dst, cl);
|
||
be_initupvals(vm, cl);
|
||
dispatch();
|
||
}
|
||
opcase(CLASS): {
|
||
bclass *c = var_toobj(ktab + IGET_Bx(ins));
|
||
be_class_upvalue_init(vm, c);
|
||
dispatch();
|
||
}
|
||
opcase(GETMBR): {
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_get++;
|
||
#endif
|
||
bvalue result; /* copy result to a temp variable because the stack may be relocated in virtual member calls */
|
||
bvalue *b = RKB(), *c = RKC();
|
||
if (var_isinstance(b) && var_isstr(c)) {
|
||
obj_attribute(vm, b, var_tostr(c), &result);
|
||
reg = vm->reg;
|
||
} else if (var_isclass(b) && var_isstr(c)) {
|
||
class_attribute(vm, b, c, &result);
|
||
reg = vm->reg;
|
||
} else if (var_ismodule(b) && var_isstr(c)) {
|
||
module_attribute(vm, b, c, &result);
|
||
reg = vm->reg;
|
||
} else {
|
||
attribute_error(vm, "attribute", b, c);
|
||
result = *RA(); /* avoid gcc warning for uninitialized variable result, this code is never reached */
|
||
}
|
||
bvalue *a = RA();
|
||
*a = result; /* assign the resul to the specified register on the updated stack */
|
||
dispatch();
|
||
}
|
||
opcase(GETMET): {
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_get++;
|
||
#endif
|
||
bvalue result; /* copy result to a temp variable because the stack may be relocated in virtual member calls */
|
||
bvalue *b = RKB(), *c = RKC();
|
||
if (var_isinstance(b) && var_isstr(c)) {
|
||
binstance *obj = var_toobj(b);
|
||
int type = obj_attribute(vm, b, var_tostr(c), &result);
|
||
reg = vm->reg;
|
||
bvalue *a = RA();
|
||
*a = result;
|
||
if (var_basetype(a) == BE_FUNCTION) {
|
||
if ((type & BE_STATIC) || (type == BE_INDEX)) { /* if instance variable then we consider it's non-method */
|
||
/* static method, don't bother with the instance */
|
||
a[1] = result;
|
||
var_settype(a, NOT_METHOD);
|
||
} else {
|
||
/* this is a real method (i.e. non-static) */
|
||
/* check if the object is a superinstance, if so get the lowest possible subclass */
|
||
while (obj->sub) {
|
||
obj = obj->sub;
|
||
}
|
||
var_setinstance(&a[1], obj); /* replace superinstance by lowest subinstance */
|
||
}
|
||
} else {
|
||
vm_error(vm, "attribute_error",
|
||
"class '%s' has no method '%s'",
|
||
str(be_instance_name(obj)), str(var_tostr(c)));
|
||
}
|
||
} else if (var_isclass(b) && var_isstr(c)) {
|
||
class_attribute(vm, b, c, &result);
|
||
reg = vm->reg;
|
||
bvalue *a = RA();
|
||
a[1] = result;
|
||
var_settype(a, NOT_METHOD);
|
||
} else if (var_ismodule(b) && var_isstr(c)) {
|
||
module_attribute(vm, b, c, &result);
|
||
reg = vm->reg;
|
||
bvalue *a = RA();
|
||
a[1] = result;
|
||
var_settype(a, NOT_METHOD);
|
||
} else {
|
||
attribute_error(vm, "method", b, c);
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(SETMBR): {
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_set++;
|
||
#endif
|
||
bvalue *a = RA(), *b = RKB(), *c = RKC();
|
||
if (var_isinstance(a) && var_isstr(b)) {
|
||
binstance *obj = var_toobj(a);
|
||
bstring *attr = var_tostr(b);
|
||
bvalue result = *c;
|
||
if (var_isfunction(&result)) {
|
||
var_markstatic(&result);
|
||
}
|
||
if (!be_instance_setmember(vm, obj, attr, &result)) {
|
||
reg = vm->reg;
|
||
vm_error(vm, "attribute_error",
|
||
"class '%s' cannot assign to attribute '%s'",
|
||
str(be_instance_name(obj)), str(attr));
|
||
}
|
||
reg = vm->reg;
|
||
dispatch();
|
||
}
|
||
if (var_isclass(a) && var_isstr(b)) {
|
||
/* if value is a function, we mark it as a static to distinguish from methods */
|
||
bclass *obj = var_toobj(a);
|
||
bstring *attr = var_tostr(b);
|
||
bvalue result = *c;
|
||
if (var_isfunction(&result)) {
|
||
var_markstatic(&result);
|
||
}
|
||
if (!be_class_setmember(vm, obj, attr, &result)) {
|
||
reg = vm->reg;
|
||
vm_error(vm, "attribute_error",
|
||
"class '%s' cannot assign to static attribute '%s'",
|
||
str(be_class_name(obj)), str(attr));
|
||
}
|
||
reg = vm->reg;
|
||
dispatch();
|
||
}
|
||
if (var_ismodule(a) && var_isstr(b)) {
|
||
bmodule *obj = var_toobj(a);
|
||
bstring *attr = var_tostr(b);
|
||
if (be_module_setmember(vm, obj, attr, c)) {
|
||
reg = vm->reg;
|
||
dispatch();
|
||
} else {
|
||
reg = vm->reg;
|
||
// fall through exception below
|
||
}
|
||
}
|
||
attribute_error(vm, "writable attribute", a, b);
|
||
dispatch();
|
||
}
|
||
opcase(GETIDX): {
|
||
bvalue *b = RKB(), *c = RKC();
|
||
if (var_isinstance(b)) {
|
||
bvalue *top = vm->top;
|
||
/* get method 'item' */
|
||
obj_method(vm, b, str_literal(vm, "item"), vm->top);
|
||
top[1] = *b; /* move object to argv[0] */
|
||
top[2] = *c; /* move key to argv[1] */
|
||
vm->top += 3; /* prevent collection results */
|
||
be_dofunc(vm, top, 2); /* call method 'item' */
|
||
vm->top -= 3;
|
||
reg = vm->reg;
|
||
*RA() = *vm->top; /* copy result to R(A) */
|
||
} else if (var_isstr(b)) {
|
||
bstring *s = be_strindex(vm, var_tostr(b), c);
|
||
reg = vm->reg;
|
||
var_setstr(RA(), s);
|
||
} else {
|
||
vm_error(vm, "type_error",
|
||
"value '%s' does not support subscriptable",
|
||
be_vtype2str(b));
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(SETIDX): {
|
||
bvalue *a = RA(), *b = RKB(), *c = RKC();
|
||
if (var_isinstance(a)) {
|
||
bvalue *top = vm->top;
|
||
/* get method 'setitem' */
|
||
obj_method(vm, a, str_literal(vm, "setitem"), vm->top);
|
||
top[1] = *a; /* move object to argv[0] */
|
||
top[2] = *b; /* move key to argv[1] */
|
||
top[3] = *c; /* move src to argv[2] */
|
||
vm->top += 4;
|
||
be_dofunc(vm, top, 3); /* call method 'setitem' */
|
||
vm->top -= 4;
|
||
reg = vm->reg;
|
||
} else {
|
||
vm_error(vm, "type_error",
|
||
"value '%s' does not support index assignment",
|
||
be_vtype2str(a));
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(SETSUPER): {
|
||
bvalue *a = RA(), *b = RKB();
|
||
if (var_isclass(a) && var_isclass(b)) {
|
||
bclass *obj = var_toobj(a);
|
||
if (!gc_isconst(obj)) {
|
||
be_class_setsuper(obj, var_toobj(b));
|
||
} else {
|
||
vm_error(vm, "internal_error",
|
||
"cannot change superclass of a read-only class");
|
||
}
|
||
} else {
|
||
vm_error(vm, "type_error",
|
||
"value '%s' does not support set super",
|
||
be_vtype2str(b));
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(CLOSE): {
|
||
be_upvals_close(vm, RA());
|
||
dispatch();
|
||
}
|
||
opcase(IMPORT): {
|
||
bvalue *b = RKB();
|
||
if (var_isstr(b)) {
|
||
bstring *name = var_tostr(b);
|
||
int res = be_module_load(vm, name);
|
||
reg = vm->reg;
|
||
switch (res) {
|
||
case BE_OK: /* find the module */
|
||
be_stackpop(vm, 1);
|
||
*RA() = *vm->top;
|
||
break;
|
||
case BE_EXCEPTION: /* pop the exception value and message */
|
||
be_pop(vm, 2);
|
||
be_throw(vm, BE_EXCEPTION);
|
||
break;
|
||
default:
|
||
vm_error(vm, "import_error", "module '%s' not found", str(name));
|
||
}
|
||
} else {
|
||
vm_error(vm, "type_error",
|
||
"import '%s' does not support import",
|
||
be_vtype2str(b));
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(CATCH): {
|
||
bvalue *base = RA(), *top = vm->top;
|
||
int i = 0, ecnt = IGET_RKB(ins), vcnt = IGET_RKC(ins);
|
||
while (i < ecnt && !be_vm_iseq(vm, top, base + i)) {
|
||
++i;
|
||
}
|
||
if (!ecnt || i < ecnt) { /* exception catched */
|
||
base = RA(), top = vm->top;
|
||
for (i = 0; i < vcnt; ++i) {
|
||
*base++ = *top++;
|
||
}
|
||
vm->ip += 1; /* skip next instruction */
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(RAISE): {
|
||
if (IGET_RA(ins) < 2) { /* A==2 means no arguments are passed to RAISE, i.e. rethrow with current exception */
|
||
bvalue *top = vm->top;
|
||
top[0] = *RKB(); /* push the exception value to top */
|
||
if (IGET_RA(ins)) { /* has exception argument? */
|
||
top[1] = *RKC(); /* push the exception argument to top + 1 */
|
||
} else {
|
||
var_setnil(top + 1);
|
||
}
|
||
be_save_stacktrace(vm);
|
||
}
|
||
be_throw(vm, BE_EXCEPTION); /* throw / rethrow the exception */
|
||
dispatch();
|
||
}
|
||
opcase(EXBLK): {
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_try++;
|
||
#endif
|
||
if (!IGET_RA(ins)) {
|
||
be_except_block_setup(vm);
|
||
if (be_setjmp(vm->errjmp->b)) {
|
||
bvalue *top = vm->top;
|
||
bvalue e1 = top[0];
|
||
bvalue e2 = top[1];
|
||
be_except_block_resume(vm);
|
||
top = vm->top;
|
||
top[0] = e1;
|
||
top[1] = e2;
|
||
goto newframe;
|
||
}
|
||
reg = vm->reg;
|
||
} else {
|
||
be_except_block_close(vm, IGET_Bx(ins));
|
||
}
|
||
dispatch();
|
||
}
|
||
opcase(CALL): {
|
||
#if BE_USE_PERF_COUNTERS
|
||
vm->counter_call++;
|
||
#endif
|
||
bvalue *var = RA(); /* `var` is the register for the call followed by arguments */
|
||
int mode = 0, argc = IGET_RKB(ins); /* B contains number of arguments pushed on stack */
|
||
recall: /* goto: instantiation class and call constructor */
|
||
switch (var_type(var)) {
|
||
case NOT_METHOD:
|
||
var[0] = var[1];
|
||
++var, --argc, mode = 1;
|
||
goto recall;
|
||
case BE_CLASS:
|
||
if (be_class_newobj(vm, var_toobj(var), var - reg, ++argc, mode)) { /* instanciate object and find constructor */
|
||
reg = vm->reg + mode; /* constructor found */
|
||
mode = 0;
|
||
var = RA() + 1; /* to next register */
|
||
reg = vm->reg;
|
||
goto recall; /* call constructor */
|
||
}
|
||
break;
|
||
case BE_INSTANCE: {
|
||
bvalue *v = var + argc++, temp;
|
||
/* load the '()' method to `temp' */
|
||
obj_method(vm, var, str_literal(vm, "()"), &temp);
|
||
for (; v >= var; --v) v[1] = v[0];
|
||
*var = temp;
|
||
goto recall; /* call '()' method */
|
||
}
|
||
case BE_CLOSURE: {
|
||
prep_closure(vm, var - reg, argc, mode);
|
||
reg = vm->reg; /* `reg` has changed, now new base register */
|
||
goto newframe; /* continue execution of the closure */
|
||
}
|
||
case BE_NTVCLOS: {
|
||
bntvclos *f = var_toobj(var);
|
||
push_native(vm, var, argc, mode);
|
||
f->f(vm); /* call C primitive function */
|
||
ret_native(vm);
|
||
break;
|
||
}
|
||
case BE_NTVFUNC: {
|
||
bntvfunc f = var_tontvfunc(var);
|
||
push_native(vm, var, argc, mode);
|
||
f(vm); /* call C primitive function */
|
||
ret_native(vm);
|
||
break;
|
||
}
|
||
case BE_CTYPE_FUNC: {
|
||
if (vm->ctypefunc) {
|
||
push_native(vm, var, argc, mode);
|
||
const void* args = var_toobj(var);
|
||
vm->ctypefunc(vm, args);
|
||
ret_native(vm);
|
||
} else {
|
||
vm_error(vm, "internal_error", "missing ctype_func handler");
|
||
}
|
||
break;
|
||
}
|
||
case BE_MODULE: {
|
||
bvalue attr;
|
||
var_setstr(&attr, str_literal(vm, "()"));
|
||
module_attribute(vm, var, &attr, var); /* exception if not found */
|
||
goto recall; /* call '()' method */
|
||
break;
|
||
}
|
||
default:
|
||
call_error(vm, var);
|
||
}
|
||
reg = vm->reg;
|
||
dispatch();
|
||
}
|
||
opcase(RET): {
|
||
bcallframe *cf;
|
||
bvalue *ret;
|
||
#if BE_USE_DEBUG_HOOK
|
||
be_callhook(vm, BE_HOOK_RET);
|
||
#endif
|
||
cf = vm->cf;
|
||
ret = vm->cf->func;
|
||
/* copy return value */
|
||
if (IGET_RA(ins)) {
|
||
*ret = *RKB();
|
||
} else {
|
||
var_setnil(ret);
|
||
}
|
||
vm->reg = cf->reg;
|
||
vm->top = cf->top;
|
||
vm->ip = cf->ip;
|
||
be_stack_pop(&vm->callstack); /* pop don't delete */
|
||
if (cf->status & BASE_FRAME) { /* entrance function */
|
||
bstack *cs = &vm->callstack;
|
||
if (!be_stack_isempty(cs)) {
|
||
vm->cf = be_stack_top(cs);
|
||
}
|
||
return;
|
||
}
|
||
vm->cf = be_stack_top(&vm->callstack);
|
||
goto newframe;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void prep_closure(bvm *vm, int pos, int argc, int mode)
|
||
{
|
||
bvalue *v, *end;
|
||
bproto *proto = var2cl(vm->reg + pos)->proto;
|
||
push_closure(vm, vm->reg + pos, proto->nstack, mode);
|
||
end = vm->reg + proto->argc;
|
||
for (v = vm->reg + argc; v <= end; ++v) {
|
||
var_setnil(v);
|
||
}
|
||
if (proto->varg & BE_VA_VARARG) { /* there are vararg at the last argument, build the list */
|
||
/* code below uses mostly low-level calls for performance */
|
||
be_stack_require(vm, argc + 2); /* make sure we don't overflow the stack */
|
||
bvalue *top_save = vm->top; /* save original stack, we need fresh slots to create the 'list' instance */
|
||
vm->top = v; /* move top of stack right after last argument */
|
||
be_newobject(vm, "list"); /* this creates 2 objects on stack: list instance, BE_LIST object */
|
||
blist *list = var_toobj(vm->top-1); /* get low-level BE_LIST structure */
|
||
v = vm->reg + proto->argc - 1; /* last argument */
|
||
for (; v < vm->reg + argc; v++) {
|
||
be_list_push(vm, list, v); /* push all varargs into list */
|
||
}
|
||
*(vm->reg + proto->argc - 1) = *(vm->top-2); /* change the vararg argument to now contain the list instance */
|
||
vm->top = top_save; /* restore top of stack pointer */
|
||
}
|
||
}
|
||
|
||
static void do_closure(bvm *vm, int pos, int argc)
|
||
{
|
||
// bvalue *v, *end;
|
||
// bproto *proto = var2cl(reg)->proto;
|
||
// push_closure(vm, reg, proto->nstack, 0);
|
||
// v = vm->reg + argc;
|
||
// end = vm->reg + proto->argc;
|
||
// for (; v <= end; ++v) {
|
||
// var_setnil(v);
|
||
// }
|
||
prep_closure(vm, pos, argc, 0);
|
||
vm_exec(vm);
|
||
}
|
||
|
||
static void do_ntvclos(bvm *vm, int pos, int argc)
|
||
{
|
||
bntvclos *f = var_toobj(vm->reg + pos);
|
||
push_native(vm, vm->reg + pos, argc, 0);
|
||
f->f(vm); /* call C primitive function */
|
||
ret_native(vm);
|
||
}
|
||
|
||
static void do_ntvfunc(bvm *vm, int pos, int argc)
|
||
{
|
||
bntvfunc f = var_tontvfunc(vm->reg + pos);
|
||
push_native(vm, vm->reg + pos, argc, 0);
|
||
f(vm); /* call C primitive function */
|
||
ret_native(vm);
|
||
}
|
||
|
||
static void do_class(bvm *vm, int pos, int argc)
|
||
{
|
||
if (be_class_newobj(vm, var_toobj(vm->reg + pos), pos, ++argc, 0)) {
|
||
be_incrtop(vm);
|
||
be_dofunc(vm, vm->reg + pos + 1, argc);
|
||
be_stackpop(vm, 1);
|
||
}
|
||
}
|
||
|
||
void be_dofunc(bvm *vm, bvalue *v, int argc)
|
||
{
|
||
be_assert(vm->reg <= v && v < vm->stacktop);
|
||
be_assert(vm->stack <= vm->reg && vm->reg < vm->stacktop);
|
||
int pos = v - vm->reg;
|
||
be_assert(!var_isstatic(v));
|
||
switch (var_type(v)) {
|
||
case BE_CLASS: do_class(vm, pos, argc); break;
|
||
case BE_CLOSURE: do_closure(vm, pos, argc); break;
|
||
case BE_NTVCLOS: do_ntvclos(vm, pos, argc); break;
|
||
case BE_NTVFUNC: do_ntvfunc(vm, pos, argc); break;
|
||
default: call_error(vm, v);
|
||
}
|
||
}
|
||
|
||
BERRY_API void be_set_obs_hook(bvm *vm, bobshook hook)
|
||
{
|
||
(void)vm; /* avoid comiler warning */
|
||
(void)hook; /* avoid comiler warning */
|
||
|
||
vm->obshook = hook;
|
||
}
|
||
|
||
BERRY_API void be_set_ctype_func_hanlder(bvm *vm, bctypefunc handler)
|
||
{
|
||
vm->ctypefunc = handler;
|
||
}
|
||
|
||
BERRY_API bctypefunc be_get_ctype_func_hanlder(bvm *vm)
|
||
{
|
||
return vm->ctypefunc;
|
||
}
|