Berry now compiling in ``strict`` mode to catch more bugs

This commit is contained in:
Stephan Hadinger 2021-08-16 20:46:09 +02:00
parent 154928fe58
commit 63bbf46d7f
21 changed files with 427 additions and 387 deletions

View File

@ -7,6 +7,9 @@ All notable changes to this project will be documented in this file.
### Added
- Version bump to monitor possible HTTP issues releated to ``SetOption128``
### Changed
- Berry now compiling in ``strict`` mode to catch more bugs
## [9.5.0.5] 20210815
### Added
- Inital support for Wi-Fi extender (#12784)

View File

@ -20,6 +20,7 @@ be_extern_native_module(sys);
be_extern_native_module(debug);
be_extern_native_module(gc);
be_extern_native_module(solidify);
be_extern_native_module(strict);
be_extern_native_module(introspect);
/* Tasmota specific */
@ -72,6 +73,9 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
#endif
#if BE_USE_INTROSPECT_MODULE
&be_native_module(introspect),
#endif
#if BE_USE_STRICT_MODULE
&be_native_module(strict),
#endif
/* user-defined modules register start */

View File

@ -45,7 +45,7 @@
* Use precompiled objects to avoid creating these objects at
* runtime. Enable this macro can greatly optimize RAM usage.
* Default: 1
// **/
**/
#define BE_USE_PRECOMPILED_OBJECT 1
/* Macro: BE_DEBUG_RUNTIME_INFO
@ -142,6 +142,14 @@
**/
#define BE_USE_DEBUG_HOOK 0
/* Macro: BE_USE_DEBUG_GC
* Enable GC debug mode. This causes an actual gc after each
* allocation. It's much slower and should not be used
* in production code.
* Default: 0
**/
#define BE_USE_DEBUG_GC 0
/* Macro: BE_USE_XXX_MODULE
* These macros control whether the related module is compiled.
* When they are true, they will enable related modules. At this
@ -159,6 +167,7 @@
#define BE_USE_GC_MODULE 1
#define BE_USE_SOLIDIFY_MODULE 1
#define BE_USE_INTROSPECT_MODULE 1
#define BE_USE_STRICT_MODULE 1
/* Macro: BE_EXPLICIT_XXX
* If these macros are defined, the corresponding function will

View File

@ -93,7 +93,7 @@ class Tasmota
var sub_event = event
var rl = string.split(rl_list[0],'#')
for it:rl
found=self.find_key_i(sub_event,it)
var found=self.find_key_i(sub_event,it)
if found == nil return false end
sub_event = sub_event[found]
end
@ -152,7 +152,7 @@ class Tasmota
var i=0
while i<self._timers.size()
if self.time_reached(self._timers[i].due)
f=self._timers[i].f
var f=self._timers[i].f
self._timers.remove(i)
f()
else

View File

@ -0,0 +1,17 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libstrict_map) {
{ be_const_key(init, -1), be_const_func(m_init) },
};
static be_define_const_map(
m_libstrict_map,
1
);
static be_define_const_module(
m_libstrict,
"strict"
);
BE_EXPORT_VARIABLE be_define_const_native_module(strict, NULL);

View File

@ -640,7 +640,8 @@ BERRY_API bbool be_copy(bvm *vm, int index)
return bfalse;
}
static int ins_member(bvm *vm, int index, const char *k)
/* `onlyins` limits the search to instance, and discards module. Makes sure getmethod does not return anything for module. */
static int ins_member(bvm *vm, int index, const char *k, bbool onlyins)
{
int type = BE_NIL;
bvalue *o = be_indexof(vm, index);
@ -652,18 +653,25 @@ static int ins_member(bvm *vm, int index, const char *k)
if (type == BE_NONE) {
type = BE_NIL;
}
} else if (var_ismodule(o) && !onlyins) {
bmodule *module = var_toobj(o);
bvalue *v = be_module_attr(vm, module, be_newstr(vm, k));
if (v != NULL) {
*top = *v;
type = v->type;
}
}
return type;
}
BERRY_API bbool be_getmember(bvm *vm, int index, const char *k)
{
return ins_member(vm, index, k) != BE_NIL;
return ins_member(vm, index, k, bfalse) != BE_NIL;
}
BERRY_API bbool be_getmethod(bvm *vm, int index, const char *k)
{
return basetype(ins_member(vm, index, k)) == BE_FUNCTION;
return basetype(ins_member(vm, index, k, btrue)) == BE_FUNCTION;
}
BERRY_API bbool be_getindex(bvm *vm, int index)

View File

@ -178,6 +178,7 @@ void be_class_upvalue_init(bvm *vm, bclass *c)
}
}
/* (internal) Instanciate an instance for a single class and initialize variables to nil */
static binstance* newobjself(bvm *vm, bclass *c)
{
size_t size = sizeof(binstance) + sizeof(bvalue) * (c->nvar - 1);
@ -185,15 +186,17 @@ static binstance* newobjself(bvm *vm, bclass *c)
binstance *obj = cast_instance(gco);
be_assert(obj != NULL);
if (obj) { /* initialize members */
bvalue *v = obj->members, *end = v + c->nvar;
while (v < end) { var_setnil(v); ++v; }
obj->_class = c;
obj->super = NULL;
obj->sub = NULL;
bvalue *v = obj->members, *end = v + c->nvar; /* instance variables is a simple array of pointers at obj->members of size c->nvar */
while (v < end) { var_setnil(v); ++v; } /* Initialize all instance variables to `nil` */
obj->_class = c; /* set its class object */
obj->super = NULL; /* no super class instance for now */
obj->sub = NULL; /* no subclass instance for now */
}
return obj;
}
/* (internal) Instanciate the whole chain of instances when there is a class hierarchy */
/* All variables set to nil, constructors are not called here */
static binstance* newobject(bvm *vm, bclass *c)
{
binstance *obj, *prev;
@ -201,23 +204,26 @@ static binstance* newobject(bvm *vm, bclass *c)
obj = prev = newobjself(vm, c);
var_setinstance(vm->top, obj);
be_incrtop(vm); /* protect new objects from GC */
for (c = c->super; c; c = c->super) {
for (c = c->super; c; c = c->super) { /* initialize one instance object per class and per superclass */
prev->super = newobjself(vm, c);
prev->super->sub = prev;
prev->super->sub = prev; /* link the super/sub classes instances */
prev = prev->super;
}
be_stackpop(vm, 1);
return obj;
}
/* Instanciate new instance from stack with argc parameters */
/* Pushes the constructor on the stack to be executed if a construtor is found */
/* Returns true if a constructor is found */
bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
{
bvalue init;
size_t pos = reg - vm->reg;
binstance *obj = newobject(vm, c);
reg = vm->reg + pos - mode; /* the stack may have changed */
binstance *obj = newobject(vm, c); /* create empty object hierarchy from class hierarchy */
reg = vm->reg + pos - mode; /* the stack may have changed, mode=1 when class is instanciated from module #104 */
var_setinstance(reg, obj);
var_setinstance(reg + mode, obj);
var_setinstance(reg + mode, obj); /* copy to reg and reg+1 if mode==1 */
/* find constructor */
obj = instance_member(vm, obj, str_literal(vm, "init"), &init);
if (obj && var_type(&init) != MT_VARIABLE) {
@ -231,6 +237,10 @@ bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
return bfalse;
}
/* Find instance member by name and copy value to `dst` */
/* Input: none of `obj`, `name` and `dst` may not be NULL */
/* Returns the type of the member or BE_NONE if member not found */
/* TODO need to support synthetic members */
int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst)
{
int type;

View File

@ -56,6 +56,7 @@ static void codelineinfo(bfuncinfo *finfo)
#define codelineinfo(finfo)
#endif
/* Add new instruction in the code vector */
static int codeinst(bfuncinfo *finfo, binstruction ins)
{
/* put new instruction in code array */
@ -77,10 +78,13 @@ static int codeABx(bfuncinfo *finfo, bopcode op, int a, int bx)
return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx));
}
/* Move value from register b to register a */
/* Check the previous instruction to compact both instruction as one if possible */
/* If b is a constant, add LDCONST or add MOVE otherwise */
static void code_move(bfuncinfo *finfo, int a, int b)
{
if (finfo->pc) {
binstruction *i = be_vector_end(&finfo->code);
if (finfo->pc) { /* If not the first instruction of the function */
binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */
bopcode op = IGET_OP(*i);
if (op <= OP_LDNIL) { /* binop or unop */
/* remove redundant MOVE instruction */
@ -98,6 +102,8 @@ static void code_move(bfuncinfo *finfo, int a, int b)
}
}
/* Free register at top (checks that it´s a register) */
/* Warning: the register must be at top of stack */
static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
{
/* release temporary register */
@ -106,6 +112,8 @@ static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
}
}
/* Privat. Allocate `count` new registers on the stack and uptade proto´s max nstack accordingly */
/* Note: deallocate is simpler and handled by a macro */
static void allocstack(bfuncinfo *finfo, int count)
{
int nstack = finfo->freereg + count;
@ -117,6 +125,7 @@ static void allocstack(bfuncinfo *finfo, int count)
}
}
/* Allocate `count` registers at top of stack, update stack accordingly */
int be_code_allocregs(bfuncinfo *finfo, int count)
{
int base = finfo->freereg;
@ -227,6 +236,8 @@ void be_code_patchjump(bfuncinfo *finfo, int jmp)
patchlistaux(finfo, jmp, finfo->pc, finfo->pc);
}
/* Allocate new constant for value k */
/* If k is NULL then push `nil` value */
static int newconst(bfuncinfo *finfo, bvalue *k)
{
int idx = be_vector_count(&finfo->kvec);
@ -239,6 +250,8 @@ static int newconst(bfuncinfo *finfo, bvalue *k)
return idx;
}
/* Find constant by value and return constant number, or -1 if constant does not exist */
/* The search is linear and lilited to 50 elements for performance reasons */
static int findconst(bfuncinfo *finfo, bexpdesc *e)
{
int i, count = be_vector_count(&finfo->kvec);
@ -273,10 +286,11 @@ static int findconst(bfuncinfo *finfo, bexpdesc *e)
return -1;
}
/* convert expdesc to constant and return kreg index (either constant kindex or register number) */
static int exp2const(bfuncinfo *finfo, bexpdesc *e)
{
int idx = findconst(finfo, e);
if (idx == -1) {
int idx = findconst(finfo, e); /* does the constant already exist? */
if (idx == -1) { /* if not add it */
bvalue k;
switch (e->type) {
case ETINT:
@ -291,16 +305,16 @@ static int exp2const(bfuncinfo *finfo, bexpdesc *e)
k.type = BE_STRING;
k.v.s = e->v.s;
break;
default:
default: /* all other values are filled later */
break;
}
idx = newconst(finfo, &k);
idx = newconst(finfo, &k); /* create new constant */
}
if (idx < 256) {
e->type = ETCONST;
if (idx < 256) { /* if constant number fits in KB or KC */
e->type = ETCONST; /* new type is constant by index */
e->v.idx = setK(idx);
} else { /* index value is too large */
e->type = ETREG;
e->type = ETREG; /* does not fit in compact mode, allocate an explicit register and emit LDCONTS */
e->v.idx = be_code_allocregs(finfo, 1);
codeABx(finfo, OP_LDCONST, e->v.idx, idx);
}
@ -368,6 +382,9 @@ static void code_closure(bfuncinfo *finfo, int idx, int dst)
codeABx(finfo, OP_CLOSURE, dst, idx); /* load closure to register */
}
/* Given an integer, check if we should create a constant */
/* True for values 0..3 and if there is room for kindex */
/* This optimization makes code more compact for commonly used ints */
static bbool constint(bfuncinfo *finfo, bint i)
{
/* cache common numbers */
@ -378,6 +395,9 @@ static bbool constint(bfuncinfo *finfo, bint i)
return bfalse;
}
/* Compute variable from an expdesc */
/* Return constant index, or existing register or fallback to dst */
/* At exit, If dst is `freereg`, the register is allocated */
static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{
if (dst < 0) { /* if unspecified, allocate a new register if needed */
@ -435,7 +455,7 @@ static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{
int reg = var2reg(finfo, e, dst);
if (hasjump(e)) {
if (hasjump(e)) { /* if conditional expression */
int pcf = NO_JUMP; /* position of an eventual LOAD false */
int pct = NO_JUMP; /* position of an eventual LOAD true */
int jpt = appendjump(finfo, jumpboolop(e, 1), e);
@ -452,6 +472,11 @@ static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
return reg;
}
/* Select dest registers from both expressions */
/* If one of them is already a register, keep it */
/* If e1 or e2 are registers, we keep the lowest and free the highest (that must be at top) */
/* If none is a register, allocate a new one */
/* Returns the destination register, guaranteed to be ETREG */
static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, int dst)
{
int cand_dst = dst;
@ -477,6 +502,8 @@ static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, int dst)
}
}
/* compute binary expression and update e1 as result */
/* On exit, e1 is guaranteed to be ETREG, which may have been allocated */
static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2, int dst)
{
if (dst < 0) { dst = finfo->freereg; }
@ -485,7 +512,7 @@ static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2,
dst = codedestreg(finfo, e1, e2, dst);
codeABC(finfo, op, dst, src1, src2);
e1->type = ETREG;
e1->v.idx = dst;
e1->v.idx = dst; /* update register as output */
}
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
@ -503,6 +530,7 @@ void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
}
}
/* Apply binary operator `op` to e1 and e2, result in e1 */
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst)
{
switch (op) {
@ -527,6 +555,8 @@ void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst
}
}
/* Apply unary operator and return register number */
/* If input is register, change in place or allocate new register */
static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
{
int src = exp2anyreg(finfo, e);
@ -536,6 +566,9 @@ static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
e->v.idx = dst;
}
/* Apply not to conditional expression */
/* If literal compute the value */
/* Or invert t/f subexpressions */
static void code_not(bexpdesc *e)
{
switch (e->type) {
@ -555,6 +588,7 @@ static void code_not(bexpdesc *e)
e->type = ETBOOL;
}
/* Negative value of literal or emit NEG opcode */
static int code_neg(bfuncinfo *finfo, bexpdesc *e)
{
switch (e->type) {
@ -568,6 +602,7 @@ static int code_neg(bfuncinfo *finfo, bexpdesc *e)
return 0;
}
/* Bit flip of literal or emit FLIP opcode */
static int code_flip(bfuncinfo *finfo, bexpdesc *e)
{
switch (e->type) {
@ -580,6 +615,7 @@ static int code_flip(bfuncinfo *finfo, bexpdesc *e)
return 0;
}
/* Apply unary operator: not, neg or bitflip */
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e)
{
switch (op) {
@ -624,24 +660,28 @@ static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src)
codeABC(finfo, op, obj, e1->v.ss.idx, src);
}
/* Assign expr e2 to e1 */
/* e1 must be in a register and have a valid idx */
/* return 1 if assignment was possible, 0 if type is not compatible */
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
int src = exp2reg(finfo, e2,
e1->type == ETLOCAL ? e1->v.idx : -1);
e1->type == ETLOCAL ? e1->v.idx : -1); /* Convert e2 to kreg */
/* If e1 is a local variable, use the register */
if (e1->type != ETLOCAL || e1->v.idx != src) {
free_expreg(finfo, e2); /* free source (only ETREG) */
free_expreg(finfo, e2); /* free source (checks only ETREG) */ /* TODO e2 is at top */
}
switch (e1->type) {
case ETLOCAL: /* It can't be ETREG. */
if (e1->v.idx != src) {
code_move(finfo, e1->v.idx, src);
code_move(finfo, e1->v.idx, src); /* do explicit move only if needed */
}
break;
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) */
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) by global index */
setsupvar(finfo, OP_SETGBL, e1, src);
break;
case ETNGLOBAL: /* store to grobal R(A) -> G(Bx) */
case ETNGLOBAL: /* store to global R(A) -> G(Bx) by name */
setbgblvar(finfo, OP_SETNGBL, e1, src);
break;
case ETUPVAL:
@ -659,6 +699,9 @@ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
return 0;
}
/* Get the expdesc as a register */
/* if already in a register, use the existing register */
/* if local or const, allocate a new register and copy value */
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e)
{
int dst = finfo->freereg;
@ -682,6 +725,9 @@ int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e)
return dst;
}
/* Generate a CALL instruction at base register with argc consecutive values */
/* i.e. arg1 is base+1... */
/* Important: argc registers are freed upon call, which are supposed to be registers above base */
void be_code_call(bfuncinfo *finfo, int base, int argc)
{
codeABC(finfo, OP_CALL, base, argc, 0);
@ -758,6 +804,8 @@ void be_code_ret(bfuncinfo *finfo, bexpdesc *e)
}
}
/* Package a suffix object from `c` with key `k` */
/* Both expdesc are materialized in kregs */
static void package_suffix(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
int key = exp2anyreg(finfo, k);
@ -771,12 +819,14 @@ int be_code_nglobal(bfuncinfo *finfo, bexpdesc *k)
return exp2anyreg(finfo, k);
}
/* Package a MEMBER suffix object from `c` with key `k` */
void be_code_member(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
package_suffix(finfo, c, k);
c->type = ETMEMBER;
}
/* Package a INDEX suffix object from `c` with key `k` */
void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
package_suffix(finfo, c, k);
@ -787,15 +837,15 @@ void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c)
{
int src;
bvalue var;
var_setclass(&var, c);
src = newconst(finfo, &var);
if (dst->type == ETLOCAL) {
var_setclass(&var, c); /* new var of CLASS type */
src = newconst(finfo, &var); /* allocate a new constant and return kreg */
if (dst->type == ETLOCAL) { /* if target is a local variable, just assign */
codeABx(finfo, OP_LDCONST, dst->v.idx, src);
} else {
} else { /* otherwise set as global with same name as class name */
codeABx(finfo, OP_LDCONST, finfo->freereg, src);
codeABx(finfo, OP_SETGBL, finfo->freereg, dst->v.idx);
}
codeABx(finfo, OP_CLASS, 0, src);
codeABx(finfo, OP_CLASS, 0, src); /* emit CLASS opcode to register class */
}
void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
@ -807,6 +857,12 @@ void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
free_expreg(finfo, s);
}
/* Emit IMPORT opcode for import module */
/* `m` is module name, is copied to register if not already */
/* `v` is destination where the imported module is stored */
/* If destination is a local variable, it is the destination of the IMPORT opcode */
/* otherwise the value is copied to a temporary register and stored to the destination */
/* TODO is this optilization useful, isn´t it done anyways by be_code_move optim? */
void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v)
{
int dst, src = exp2anyreg(finfo, m);
@ -840,6 +896,10 @@ void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp)
}
}
/* Emit RAISE opcode */
/* e1 is the exception code */
/* e2 is the exception description */
/* both are materialized to a temp register (if not null) */
void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
if (e1) {
@ -847,7 +907,7 @@ void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
int src2 = e2 ? exp2anyreg(finfo, e2) : 0;
codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2);
} else {
codeABC(finfo, OP_RAISE, 2, 0, 0);
codeABC(finfo, OP_RAISE, 2, 0, 0); /* rethrow the current exception with parameters already on top of stack */
}
/* release the register occupied by the expression */
free_expreg(finfo, e1);

View File

@ -82,6 +82,8 @@ void be_throw(bvm *vm, int errorcode)
}
}
/* Fatal error Exit */
/* Raise a BE_EXIT exception if within a try/catch block, or exit VM */
BERRY_API void be_exit(bvm *vm, int status)
{
if (vm->errjmp) {
@ -99,6 +101,8 @@ void be_throw_message(bvm *vm, int errorcode, const char *msg)
be_throw(vm, errorcode);
}
/* Exec protected: exec function and capture any exception and contain it within call */
/* Exceptions or fatal errors are not propagated */
int be_execprotected(bvm *vm, bpfunc f, void *data)
{
struct blongjmp jmp;
@ -292,6 +296,7 @@ static void m_pcall(bvm *vm, void *data)
be_dofunc(vm, p->v, p->argc);
}
/* Protected call: contain any exception of fatal error and restore context if something went wrong */
int be_protectedcall(bvm *vm, bvalue *v, int argc)
{
int res;
@ -308,7 +313,8 @@ int be_protectedcall(bvm *vm, bvalue *v, int argc)
}
#if BE_DEBUG && defined(be_assert)
/* increase top register */
/* increase top register and return new top */
/* Does not expand the stack if there is not enough room, but may corrupt memory */
bvalue* be_incrtop(bvm *vm)
{
bvalue *top = vm->top++;
@ -317,6 +323,7 @@ bvalue* be_incrtop(bvm *vm)
}
#endif
/* TODO what is the difference with be_stack_push? */
void be_stackpush(bvm *vm)
{
/* make sure there is enough stack space */
@ -324,6 +331,7 @@ void be_stackpush(bvm *vm)
be_incrtop(vm);
}
/* check that the stack is able to store `count` items, and increase stack if needed */
void be_stack_require(bvm *vm, int count)
{
if (vm->top + count >= vm->stacktop) {
@ -331,6 +339,7 @@ void be_stack_require(bvm *vm, int count)
}
}
/* Scan the entire callstack and adjust all pointer by `offset` */
static void update_callstack(bvm *vm, intptr_t offset)
{
bcallframe *cf = be_stack_top(&vm->callstack);
@ -353,20 +362,25 @@ static void update_upvalues(bvm *vm, intptr_t offset)
}
}
/* Resize the stack to new `size` as number of elements */
/* Then update all pointers in callstack and upvalues with the new stack address */
static void stack_resize(bvm *vm, size_t size)
{
intptr_t offset;
bvalue *old = vm->stack;
size_t os = (vm->stacktop - old) * sizeof(bvalue);
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size);
vm->stacktop = vm->stack + size;
offset = ptr_offset(vm->stack, old);
bvalue *old = vm->stack; /* save original pointer of stack before resize */
size_t os = (vm->stacktop - old) * sizeof(bvalue); /* size of current stack allocated in bytes */
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size); /* reallocate with the new size */
vm->stacktop = vm->stack + size; /* compute new stacktop */
offset = ptr_offset(vm->stack, old); /* compute the address difference between old and ne stack addresses */
/* update callframes */
update_callstack(vm, offset);
/* update open upvalues */
update_upvalues(vm, offset);
}
/* Stack resize internal API */
/* Increases the stack by `n` elements, reallocate stack if needed and update all callstacks and upvals */
/* Check if we are above the max allowed stack */
void be_stack_expansion(bvm *vm, int n)
{
size_t size = vm->stacktop - vm->stack;

View File

@ -83,4 +83,4 @@ module global (scope: global, depend: BE_USE_GLOBAL_MODULE) {
#include "../generate/be_fixed_global.h"
#endif
#endif /* BE_USE_INTROSPECT_MODULE */
#endif /* BE_USE_GLOBAL_MODULE */

View File

@ -252,6 +252,16 @@ static void cache_module(bvm *vm, bstring *name)
*v = vm->top[-1];
}
/* Try to run '()' function of module. Module is already loaded. */
static void module_init(bvm *vm) {
if (be_getmember(vm, -1, "init")) {
/* found, call it with no parameter */
be_call(vm, 0);
/* we don't care about the result */
}
be_pop(vm, 1);
}
/* load module to vm->top */
int be_module_load(bvm *vm, bstring *path)
{
@ -260,8 +270,11 @@ int be_module_load(bvm *vm, bstring *path)
res = load_native(vm, path);
if (res == BE_IO_ERROR)
res = load_package(vm, path);
if (res == BE_OK)
if (res == BE_OK) {
cache_module(vm, path);
/* on first load of the module, try running the '()' function */
module_init(vm);
}
}
return res;
}

View File

@ -51,7 +51,7 @@ OPCODE(CLOSE), /* A | close upvalues */
OPCODE(IMPORT), /* A, B, C | IF (A == C) import module name as RK(B) to RK(A), ELSE from module RK(C) import name as RK(B) to RK(A) */
OPCODE(EXBLK), /* A, Bx | ... */
OPCODE(CATCH), /* A, B, C | ... */
OPCODE(RAISE), /* A, B, C | ... */
OPCODE(RAISE), /* A, B, C | RAISE(B,C) B is code, C is description. A==0 only B provided, A==1 B and C are provided, A==2 rethrow with both parameters already on stack */
OPCODE(CLASS), /* Bx | init class in K[Bx] */
OPCODE(GETNGBL), /* A, B | R(A) <- GLOBAL[B] by name */
OPCODE(SETNGBL) /* A, B | R(A) -> GLOBAL[B] by name */

View File

@ -87,6 +87,8 @@ static void match_token(bparser *parser, btokentype type)
scan_next_token(parser); /* skip this token */
}
/* Check that the next token is not of type `type` */
/* or raise an exception */
static void match_notoken(bparser *parser, btokentype type)
{
if (next_type(parser) == type) { /* error when token is match */
@ -95,6 +97,7 @@ static void match_notoken(bparser *parser, btokentype type)
}
}
/* check that if the expdesc is a symbol, it is avalid one or raise an exception */
static void check_symbol(bparser *parser, bexpdesc *e)
{
if (e->type == ETVOID && e->v.s == NULL) { /* error when token is not a symbol */
@ -103,6 +106,7 @@ static void check_symbol(bparser *parser, bexpdesc *e)
}
}
/* check that the value in `e` is valid for a variable, i.e. conatins a value or a valid symbol */
static void check_var(bparser *parser, bexpdesc *e)
{
check_symbol(parser, e); /* check the token is a symbol */
@ -172,14 +176,15 @@ void end_varinfo(bparser *parser, int beginpc)
#endif
/* Initialize bblockinfo structure */
static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type)
{
binfo->prev = finfo->binfo;
finfo->binfo = binfo;
binfo->prev = finfo->binfo; /* save previous block */
finfo->binfo = binfo; /* tell parser this is the current block */
binfo->type = (bbyte)type;
binfo->hasupval = 0;
binfo->beginpc = finfo->pc;
binfo->nactlocals = (bbyte)be_list_count(finfo->local);
binfo->beginpc = finfo->pc; /* set starting pc for this block */
binfo->nactlocals = (bbyte)be_list_count(finfo->local); /* count number of local variables in previous block */
if (type & BLOCK_LOOP) {
binfo->breaklist = NO_JUMP;
binfo->continuelist = NO_JUMP;
@ -197,9 +202,9 @@ static void end_block_ex(bparser *parser, int beginpc)
be_code_patchlist(finfo, binfo->continuelist, binfo->beginpc);
}
end_varinfo(parser, beginpc);
be_list_resize(parser->vm, finfo->local, binfo->nactlocals);
finfo->freereg = binfo->nactlocals;
finfo->binfo = binfo->prev;
be_list_resize(parser->vm, finfo->local, binfo->nactlocals); /* remove local variables from this block, they are now out of scope */
finfo->freereg = binfo->nactlocals; /* adjust first free register accordingly */
finfo->binfo = binfo->prev; /* restore previous block */
}
static void end_block(bparser *parser)
@ -207,6 +212,8 @@ static void end_block(bparser *parser)
end_block_ex(parser, -1);
}
/* Return the name of the source for this parser, could be `string`,
`stdin` or the name of the current function */
static bstring* parser_source(bparser *parser)
{
if (parser->finfo) {
@ -215,29 +222,30 @@ static bstring* parser_source(bparser *parser)
return be_newstr(parser->vm, parser->lexer.fname);
}
/* Initialize a function block and create a new `bprotoˋ */
static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
{
bvm *vm = parser->vm;
bproto *proto = be_newproto(vm);
var_setproto(vm->top, proto);
be_stackpush(vm);
be_vector_init(vm, &finfo->code, sizeof(binstruction));
be_vector_init(vm, &finfo->code, sizeof(binstruction)); /* vector for code, vectors are not gced */
proto->code = be_vector_data(&finfo->code);
proto->codesize = be_vector_capacity(&finfo->code);
be_vector_init(vm, &finfo->kvec, sizeof(bvalue));
be_vector_init(vm, &finfo->kvec, sizeof(bvalue)); /* vector for constants */
proto->ktab = be_vector_data(&finfo->kvec);
proto->nconst = be_vector_capacity(&finfo->kvec);
be_vector_init(vm, &finfo->pvec, sizeof(bproto*));
be_vector_init(vm, &finfo->pvec, sizeof(bproto*)); /* vector for subprotos */
proto->ptab = be_vector_data(&finfo->pvec);
proto->nproto = be_vector_capacity(&finfo->pvec);
proto->source = parser_source(parser);
finfo->local = be_list_new(vm);
var_setlist(vm->top, finfo->local);
proto->source = parser_source(parser); /* keep a copy of source for function */
finfo->local = be_list_new(vm); /* list for local variables */
var_setlist(vm->top, finfo->local); /* push list of local variables on the stack (avoid gc) */
be_stackpush(vm);
finfo->upval = be_map_new(vm);
finfo->upval = be_map_new(vm); /* push a map for upvals on stack */
var_setmap(vm->top, finfo->upval);
be_stackpush(vm);
finfo->prev = parser->finfo;
finfo->prev = parser->finfo; /* init finfo */
finfo->lexer = &parser->lexer;
finfo->proto = proto;
finfo->freereg = 0;
@ -258,6 +266,7 @@ static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
begin_block(finfo, binfo, 0);
}
/* compute the upval structure */
static void setupvals(bfuncinfo *finfo)
{
bproto *proto = finfo->proto;
@ -282,6 +291,7 @@ static void setupvals(bfuncinfo *finfo)
}
}
/* Function is complete, finalize bproto */
static void end_func(bparser *parser)
{
bvm *vm = parser->vm;
@ -289,9 +299,9 @@ static void end_func(bparser *parser)
bproto *proto = finfo->proto;
be_code_ret(finfo, NULL); /* append a return to last code */
end_block(parser);
setupvals(finfo);
proto->code = be_vector_release(vm, &finfo->code);
end_block(parser); /* close block */
setupvals(finfo); /* close upvals */
proto->code = be_vector_release(vm, &finfo->code); /* compact all vectors and return NULL if empty */
proto->codesize = finfo->pc;
proto->ktab = be_vector_release(vm, &finfo->kvec);
proto->nconst = be_vector_count(&finfo->kvec);
@ -305,10 +315,11 @@ static void end_func(bparser *parser)
proto->varinfo = be_vector_release(vm, &finfo->varvec);
proto->nvarinfo = be_vector_count(&finfo->varvec);
#endif
parser->finfo = parser->finfo->prev;
parser->finfo = parser->finfo->prev; /* restore previous `finfo` */
be_stackpop(vm, 2); /* pop upval and local */
}
/* is the next token a binary operator? If yes return the operator or `OP_NOT_BINARY` */
static btokentype get_binop(bparser *parser)
{
btokentype op = next_type(parser);
@ -318,6 +329,8 @@ static btokentype get_binop(bparser *parser)
return OP_NOT_BINARY;
}
/* is the next token a unary operator? If yes return the operator or `OP_NOT_BINARY` */
/* operator 'negative' 'not' and 'flip' */
static btokentype get_unary_op(bparser *parser)
{
btokentype op = next_type(parser);
@ -327,6 +340,8 @@ static btokentype get_unary_op(bparser *parser)
return OP_NOT_UNARY;
}
/* is the next token an assignment operator? If yes return the operator or `OP_NOT_BINARY` */
/* `=`, `+=`, ... `>>=` */
static btokentype get_assign_op(bparser *parser)
{
btokentype op = next_type(parser);
@ -336,6 +351,7 @@ static btokentype get_assign_op(bparser *parser)
return OP_NOT_ASSIGN;
}
/* Initialize bexpdesc structure with specific type and value as int */
static void init_exp(bexpdesc *e, exptype_t type, bint i)
{
e->type = (bbyte)type;
@ -346,6 +362,8 @@ static void init_exp(bexpdesc *e, exptype_t type, bint i)
e->v.i = i;
}
/* find local variable by name, starting at index `begin` */
/* linear search, returns -1 if not found */
static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
{
int i, count = be_list_count(finfo->local);
@ -358,12 +376,22 @@ static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
return -1; /* not found */
}
/* create a new local variable by name, or return the current register if already exists */
/* returns the Reg number for the variable */
/* If STRICT, we don't allow a var to hide a var from outer scope */
/* We don't allow the same var to be defined twice in same scope */
static int new_localvar(bparser *parser, bstring *name)
{
bfuncinfo *finfo = parser->finfo;
int reg = find_localvar(finfo, name, finfo->binfo->nactlocals);
int reg = find_localvar(finfo, name, finfo->binfo->nactlocals); /* look if already exists skipping the local variables from upper blocks */
/* 'strict': raise an exception if the local variable shadows another local variable */
if (reg == -1) {
bvalue *var;
if (comp_is_strict(parser->vm)) {
if (find_localvar(finfo, name, 0) >= 0 && str(name)[0] != '.') { /* we do accept nested redifinition of internal variables starting with ':' */
push_error(parser, "strict: redefinition of '%s' from outer scope", str(name));
}
}
reg = be_list_count(finfo->local); /* new local index */
var = be_list_push(parser->vm, finfo->local, NULL);
var_setstr(var, name);
@ -371,10 +399,13 @@ static int new_localvar(bparser *parser, bstring *name)
be_code_allocregs(finfo, 1); /* use a register */
}
begin_varinfo(parser, name);
} else {
push_error(parser, "redefinition of '%s'", str(name));
}
return reg;
}
/* Find upval by name, if found return its index number, or -1 */
static int find_upval(bfuncinfo *finfo, bstring *s)
{
bvm *vm = finfo->lexer->vm;
@ -385,6 +416,8 @@ static int find_upval(bfuncinfo *finfo, bstring *s)
return -1;
}
/* Recursively search for upper blocks that are referenced in upvals */
/* and mark them with `hasupval` */
static void mark_upval(bfuncinfo *finfo, int level)
{
bblockinfo *binfo = finfo->prev->binfo;
@ -413,12 +446,14 @@ static int new_upval(bvm *vm, bfuncinfo *finfo, bstring *name, bexpdesc *var)
return index;
}
/* create a new variable in currenr context as name, and create expdesc for it */
/* If within a block, create as local, otherwise as global */
static void new_var(bparser *parser, bstring *name, bexpdesc *var)
{
bfuncinfo *finfo = parser->finfo;
if (finfo->prev || finfo->binfo->prev || parser->islocal) {
init_exp(var, ETLOCAL, 0);
var->v.idx = new_localvar(parser, name);
var->v.idx = new_localvar(parser, name); /* if local, contains the index in current local var list */
} else {
init_exp(var, ETGLOBAL, 0);
var->v.idx = be_global_new(parser->vm, name);
@ -465,6 +500,9 @@ static int singlevaraux(bvm *vm, bfuncinfo *finfo, bstring *s, bexpdesc *var)
}
}
/* get variable from next toden as name */
/* and create an expdesc from it */
/* can be new, global, named global, upval */
static void singlevar(bparser *parser, bexpdesc *var)
{
bexpdesc key;
@ -490,6 +528,10 @@ static void singlevar(bparser *parser, bexpdesc *var)
}
}
/* Parse function or method definition variable list */
/* Create an implicit local variable for each argument starting at R0 */
/* Update function proto argc to the expected number or arguments */
/* Raise an exception if multiple arguments have the same name */
static void func_varlist(bparser *parser)
{
bexpdesc v;
@ -502,36 +544,36 @@ static void func_varlist(bparser *parser)
str = next_token(parser).u.s;
match_token(parser, TokenId); /* match and skip ID */
/* new local variable */
if (find_localvar(parser->finfo, str, 0) == -1) {
new_var(parser, str, &v);
} else {
push_error(parser, "redefinition of '%s'", str(str));
}
new_var(parser, str, &v);
}
}
match_token(parser, OptRBK); /* skip ')' */
parser->finfo->proto->argc = parser->finfo->freereg;
}
/* Parse a function includind arg list and body */
/* Given name and type (function or method) */
/* Returns `bproto` object */
static bproto* funcbody(bparser *parser, bstring *name, int type)
{
bfuncinfo finfo;
bblockinfo binfo;
/* '(' varlist ')' block 'end' */
begin_func(parser, &finfo, &binfo);
begin_func(parser, &finfo, &binfo); /* init new function context */
finfo.proto->name = name;
if (type & FUNC_METHOD) {
if (type & FUNC_METHOD) { /* If method, add an implicit first argument `self` */
new_localvar(parser, parser_newstr(parser, "self"));
}
func_varlist(parser);
stmtlist(parser);
end_func(parser);
func_varlist(parser); /* parse arg list */
stmtlist(parser); /* parse statement without final `end` */
end_func(parser); /* close function context */
match_token(parser, KeyEnd); /* skip 'end' */
return finfo.proto;
return finfo.proto; /* return fully constructed `bproto` */
}
/* anonymous function */
/* anonymous function, build `bproto` object with name `<anonymous>` */
/* and build a expdesc for the bproto */
static void anon_func(bparser *parser, bexpdesc *e)
{
bproto *proto;
@ -559,11 +601,7 @@ static void lambda_varlist(bparser *parser)
str = next_token(parser).u.s;
match_token(parser, TokenId); /* match and skip ID */
/* new local variable */
if (find_localvar(parser->finfo, str, 0) == -1) {
new_var(parser, str, &v);
} else {
push_error(parser, "redefinition of '%s'", str(str));
}
new_var(parser, str, &v);
}
}
match_token(parser, OptArrow); /* skip '->' */
@ -590,6 +628,9 @@ static void lambda_expr(bparser *parser, bexpdesc *e)
be_stackpop(parser->vm, 1);
}
/* Instanciate a builtin type by name */
/* Allocates a new register for the value, and call empty constructor */
/* Is allocated as LOCAL and must be changed to REG when completed */
static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
{
int idx;
@ -601,17 +642,19 @@ static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
init_exp(e, ETGLOBAL, idx);
idx = be_code_nextreg(finfo, e);
be_code_call(finfo, idx, 0);
e->type = ETLOCAL;
e->type = ETLOCAL; /* declare as local, will be changed to ETREG when completely initialized */
}
/* Parse next member within a list */
/* `l` contains the current list. The expr is evaluated and added to the list */
static void list_nextmember(bparser *parser, bexpdesc *l)
{
bexpdesc e, v = *l;
bfuncinfo *finfo = parser->finfo;
expr(parser, &e); /* value */
check_var(parser, &e);
be_code_binop(finfo, OptConnect, &v, &e, -1);
be_code_freeregs(finfo, 1);
check_var(parser, &e); /* check that we don´t have an unknown symbol */
be_code_binop(finfo, OptConnect, &v, &e, -1); /* add it to list with CONNECT */
be_code_freeregs(finfo, 1); /* since left arg is LOCAL, an ETREG was allocated. Free it */
}
static void map_nextmember(bparser *parser, bexpdesc *l)
@ -619,25 +662,25 @@ static void map_nextmember(bparser *parser, bexpdesc *l)
bexpdesc e, v = *l;
bfuncinfo *finfo = parser->finfo;
expr(parser, &e); /* key */
check_var(parser, &e);
be_code_index(finfo, &v, &e);
check_var(parser, &e); /* check if value is valid */
be_code_index(finfo, &v, &e); /* package as `v` as INDEX suffix for value `e` */
match_token(parser, OptColon); /* ':' */
expr(parser, &e); /* value */
check_var(parser, &e);
be_code_setvar(finfo, &v, &e);
expr(parser, &e); /* value in `e` */
check_var(parser, &e); /* check if value is correct */
be_code_setvar(finfo, &v, &e); /* set suffi INDEX value to e */
}
static void list_expr(bparser *parser, bexpdesc *e)
{
/* '[' {expr ','} [expr] ']' */
new_primtype(parser, "list", e); /* new list */
new_primtype(parser, "list", e); /* new list, created as LOCAL first */
while (next_type(parser) != OptRSB) {
list_nextmember(parser, e);
if (!match_skip(parser, OptComma)) { /* ',' */
break;
}
}
e->type = ETREG;
e->type = ETREG; /* then turned to REG */
match_token(parser, OptRSB); /* skip ']' */
}
@ -655,14 +698,16 @@ static void map_expr(bparser *parser, bexpdesc *e)
match_token(parser, OptRBR); /* skip '}' */
}
/* push each argument as new reg and return number of args */
/* TODO `e` is ignored by caller */
static int exprlist(bparser *parser, bexpdesc *e)
{
bfuncinfo *finfo = parser->finfo;
int n = 1;
/* expr { ',' expr } */
expr(parser, e);
check_var(parser, e);
be_code_nextreg(finfo, e);
expr(parser, e); /* parse expr */
check_var(parser, e); /* check if valid */
be_code_nextreg(finfo, e); /* move result to next reg */
while (match_skip(parser, OptComma)) { /* ',' */
expr(parser, e);
check_var(parser, e);
@ -672,6 +717,9 @@ static int exprlist(bparser *parser, bexpdesc *e)
return n;
}
/* parse call to method or function */
/* `e` can be a member (method) or a register */
/* On return, `e` is ETREG to the result of the call */
static void call_expr(bparser *parser, bexpdesc *e)
{
bexpdesc args;
@ -685,14 +733,15 @@ static void call_expr(bparser *parser, bexpdesc *e)
if (ismember) {
base = be_code_getmethod(finfo, e);
} else {
base = be_code_nextreg(finfo, e);
base = be_code_nextreg(finfo, e); /* allocate a new base reg if not at top already */
}
/* base is always taken at top of freereg and allocates 1 reg for function and 2 regs for method */
scan_next_token(parser); /* skip '(' */
if (next_type(parser) != OptRBK) {
argc = exprlist(parser, &args);
if (next_type(parser) != OptRBK) { /* if arg list is not empty */
argc = exprlist(parser, &args); /* push each argument as new reg and return number of args */
}
match_token(parser, OptRBK); /* skip ')' */
argc += ismember;
argc += ismember; /* if method there is an additional implicit arg */
be_code_call(finfo, base, argc);
if (e->type != ETREG) {
e->type = ETREG;
@ -700,6 +749,8 @@ static void call_expr(bparser *parser, bexpdesc *e)
}
}
/* Parse member expression */
/* Generates an ETMEMBER object that is materialized later into GETMBR, GETMET or SETMBR */
static void member_expr(bparser *parser, bexpdesc *e)
{
bstring *str;
@ -841,15 +892,32 @@ static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r)
}
}
/* check if we need to create a new local variable with this name to be assigned to */
/* Returns true if it´s a new local variable */
/* A new implicit local variable is created if no global has the same name (excluding builtins) */
/* This means that you can override a builtin silently */
/* This also means that a function cannot create a global, they must preexist or create with `global` module */
/* TODO add warning in strict mode */
static int check_newvar(bparser *parser, bexpdesc *e)
{
if (e->type == ETGLOBAL) {
if (e->v.idx < be_builtin_count(parser->vm)) {
e->v.s = be_builtin_name(parser->vm, e->v.idx);
if (comp_is_strict(parser->vm)) {
push_error(parser, "strict: redefinition of builtin '%s'",
str(e->v.s));
}
return btrue;
}
return bfalse;
}
if (comp_is_strict(parser->vm)) {
bfuncinfo *finfo = parser->finfo;
if ((e->type == ETVOID) && (finfo->prev || finfo->binfo->prev || parser->islocal)) {
push_error(parser, "strict: no global '%s', did you mean 'var %s'?",
str(e->v.s), str(e->v.s));
}
}
return e->type == ETVOID;
}
@ -915,39 +983,42 @@ static void cond_expr(bparser *parser, bexpdesc *e)
static void sub_expr(bparser *parser, bexpdesc *e, int prio)
{
bfuncinfo *finfo = parser->finfo;
btokentype op = get_unary_op(parser);
if (op != OP_NOT_UNARY) {
btokentype op = get_unary_op(parser); /* check if first token in unary op */
if (op != OP_NOT_UNARY) { /* unary op found */
int line, res;
scan_next_token(parser);
line = parser->lexer.linenumber;
sub_expr(parser, e, UNARY_OP_PRIO);
check_var(parser, e);
res = be_code_unop(finfo, op, e);
scan_next_token(parser); /* move to next token */
line = parser->lexer.linenumber; /* remember line number for error reporting */
sub_expr(parser, e, UNARY_OP_PRIO); /* parse subexpr with new prio */
check_var(parser, e); /* check that the value is ok */
res = be_code_unop(finfo, op, e); /* apply unary op with optimizations if the token is a value */
if (res) { /* encode unary op */
parser->lexer.linenumber = line;
push_error(parser, "wrong type argument to unary '%s'",
res == 1 ? "negative" : "bit-flip");
}
} else {
suffix_expr(parser, e);
suffix_expr(parser, e); /* parse left part of binop */
}
op = get_binop(parser);
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) {
op = get_binop(parser); /* check if binop */
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) { /* is binop applicable */
bexpdesc e2;
check_var(parser, e);
scan_next_token(parser);
check_var(parser, e); /* check that left part is valid */
scan_next_token(parser); /* move to next token */
be_code_prebinop(finfo, op, e); /* and or */
init_exp(&e2, ETVOID, 0);
sub_expr(parser, &e2, binary_op_prio(op));
check_var(parser, &e2);
sub_expr(parser, &e2, binary_op_prio(op)); /* parse right side */
check_var(parser, &e2); /* check if valid */
be_code_binop(finfo, op, e, &e2, -1); /* encode binary op */
op = get_binop(parser);
op = get_binop(parser); /* is there a following binop? */
}
if (prio == ASSIGN_OP_PRIO) {
cond_expr(parser, e);
}
}
/* Parse new expression and return value in `e` (overwritten) */
/* Initializes an empty expdes and parse subexpr */
/* Always allocates a new temp register at top of freereg */
static void expr(bparser *parser, bexpdesc *e)
{
init_exp(e, ETVOID, 0);

View File

@ -12,7 +12,7 @@
#include "be_string.h"
typedef enum {
ETVOID,
ETVOID, /* unknown (new variable or error) */
ETNIL,
ETBOOL,
ETREAL,
@ -20,13 +20,13 @@ typedef enum {
ETSTRING,
ETPROTO,
ETCONST,
ETLOCAL,
ETGLOBAL,
ETLOCAL, /* local variable, allocated until end of scope */
ETGLOBAL, /* global by index number */
ETUPVAL,
ETMEMBER,
ETINDEX,
ETREG,
ETNGLOBAL
ETMEMBER, /* member accessor (by name) */
ETINDEX, /* index accessor (ex array index) */
ETREG, /* temporary register, can be freed if top of stack */
ETNGLOBAL /* named global */
} exptype_t;
typedef struct {

View File

@ -0,0 +1,40 @@
/********************************************************************
** Copyright (c) 2018-2021 Guan Wenliang & Stephan Hadinger
** 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_object.h"
#include "be_module.h"
#include "be_string.h"
#include "be_vector.h"
#include "be_class.h"
#include "be_debug.h"
#include "be_map.h"
#include "be_vm.h"
#if BE_USE_STRICT_MODULE
static int m_init(bvm *vm)
{
comp_set_strict(vm); /* enable compiler strict mode */
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(strict) {
be_native_module_function("init", m_init),
};
be_define_native_module(strict, NULL);
#else
/* @const_object_info_begin
module strict (scope: strict, depend: BE_USE_STRICT_MODULE) {
init, func(m_init)
}
@const_object_info_end */
#include "../generate/be_fixed_strict.h"
#endif
#endif /* BE_USE_STRICT_MODULE */

View File

@ -28,14 +28,14 @@
#define vm_error(vm, except, ...) \
be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__))
#define RA() (reg + IGET_RA(ins))
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins)))
#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins)))
#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))
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i)
#define val2bool(v) ((v) ? btrue : bfalse)
#define ibinop(op, a, b) ((a)->v.i op (b)->v.i)
#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() \
@ -149,6 +149,8 @@ static void call_error(bvm *vm, bvalue *v)
"'%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)) {
@ -182,25 +184,29 @@ static void do_linehook(bvm *vm)
}
#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;
if (vm->stacktop < func + expan) {
size_t fpos = func - vm->stack;
be_stack_expansion(vm, expan);
func = vm->stack + fpos;
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);
cf = be_stack_top(&vm->callstack);
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;
cf->reg = vm->reg;
vm->reg = func + 1;
vm->top = vm->reg + nstack;
vm->cf = cf;
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);
@ -469,9 +475,9 @@ static void vm_exec(bvm *vm)
vm->cf->status |= BASE_FRAME;
newframe: /* a new call frame */
be_assert(var_isclosure(vm->cf->func));
clos = var_toobj(vm->cf->func);
ktab = clos->proto->ktab;
reg = vm->reg;
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 */
vm_exec_loop() {
opcase(LDNIL): {
var_setnil(RA());
@ -1019,7 +1025,7 @@ newframe: /* a new call frame */
dispatch();
}
opcase(RAISE): {
if (IGET_RA(ins) < 2) {
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? */
@ -1046,8 +1052,8 @@ newframe: /* a new call frame */
dispatch();
}
opcase(CALL): {
bvalue *var = RA();
int mode = 0, argc = IGET_RKB(ins);
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:
@ -1055,8 +1061,8 @@ newframe: /* a new call frame */
++var, --argc, mode = 1;
goto recall;
case BE_CLASS:
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) {
reg = vm->reg + mode;
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) { /* instanciate object and find constructor */
reg = vm->reg + mode; /* constructor found */
mode = 0;
var = RA() + 1; /* to next register */
goto recall; /* call constructor */
@ -1072,15 +1078,15 @@ newframe: /* a new call frame */
}
case BE_CLOSURE: {
bvalue *v, *end;
bproto *proto = var2cl(var)->proto;
push_closure(vm, var, proto->nstack, mode);
reg = vm->reg;
v = reg + argc;
end = reg + proto->argc;
for (; v < end; ++v) {
bproto *proto = var2cl(var)->proto; /* get proto for closure */
push_closure(vm, var, proto->nstack, mode); /* prepare stack for closure */
reg = vm->reg; /* `reg` has changed, now new base register */
v = reg + argc; /* end of provided arguments */
end = reg + proto->argc; /* end of expected arguments */
for (; v < end; ++v) { /* set all not provided arguments to nil */
var_setnil(v);
}
goto newframe;
goto newframe; /* continue execution of the closure */
}
case BE_NTVCLOS: {
bntvclos *f = var_toobj(var);

View File

@ -14,9 +14,14 @@
#define comp_set_named_gbl(vm) ((vm)->compopt |= (1<<COMP_NAMED_GBL))
#define comp_clear_named_gbl(vm) ((vm)->compopt &= ~(1<<COMP_NAMED_GBL))
#define comp_is_strict(vm) ((vm)->compopt & (1<<COMP_STRICT))
#define comp_set_strict(vm) ((vm)->compopt |= (1<<COMP_STRICT))
#define comp_clear_strict(vm) ((vm)->compopt &= ~(1<<COMP_STRICT))
/* Compilation options */
typedef enum {
COMP_NAMED_GBL = 0x00, /* compile with named globals */
COMP_STRICT = 0x01, /* compile with named globals */
} compoptmask;
typedef struct {

View File

@ -1,219 +0,0 @@
#-
- Demo for M5Stack black edition, using ILI9341
-
-#
if gpio.pin_used(gpio.ILI9341_CS) && gpio.pin_used(gpio.ILI9341_DC) && gpio.pin_used(gpio.OLED_RESET)
lv.start(udisplay.ILI9341_M5Stack_Core)
hres = lv.get_hor_res()
vres = lv.get_ver_res()
scr = lv.scr_act()
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.RED))
scr.set_style_local_bg_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.OPA_COVER)
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFA000))
scr.set_style_local_bg_grad_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFF4000))
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x000080))
scr.set_style_local_bg_grad_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x000030))
scr.set_style_local_bg_grad_dir(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.GRAD_DIR_VER)
#- logo = lv.img_create(scr)
logo.set_src("A:/Sunrise320.bin")
logo.align(0,0,0,0) -#
btn = lv_btn(scr)
btn.set_pos(40,40)
btn.set_size(180, 45)
#btn.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x1fa3ec))
label = lv_label(btn)
label.set_text(lv.SYMBOL_HOME + "Tasmota")
#label.set_style_local_text_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFFFFF))
#f28 = lv.montserrat_font(28)
#if f28 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f28) end
btn.align(0, lv.ALIGN_CENTER, 0, 0)
log = lv_label(scr)
f20 = lv.montserrat_font(20)
if f20 != nil log.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f20) end
#- log.set_long_mode(lv.LABEL_LONG_DOT) -#
log.set_long_mode(lv.LABEL_LONG_SROLL)
log.set_width(hres)
log.set_align(lv.LABEL_ALIGN_LEFT)
log.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xD00000))
log.set_style_local_bg_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.OPA_COVER)
log.set_style_local_text_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFFFFF))
log.set_text("Welcome to Tasmota - long text rolling")
log.set_text("Welcome to Tasmota")
tastyle = lv_style()
tastyle.set_bg_color(lv.STATE_DEFAULT, lv_color(0x1fa3ec))
tastyle.set_text_color(lv.STATE_DEFAULT, lv_color(0xFFFFFF))
tastyle.set_radius(lv.STATE_DEFAULT, 10)
tastyle.set_bg_opa(lv.STATE_DEFAULT, lv.OPA_COVER)
f20 = lv.montserrat_font(20)
if f20 != nil tastyle.set_text_font(lv.STATE_DEFAULT, f20) end
btn.add_style(lv.OBJ_PART_MAIN, tastyle)
prev_btn = lv_btn(scr)
prev_btn.set_pos(20,vres-40)
prev_btn.set_size(80, 30)
prev_btn.add_style(lv.OBJ_PART_MAIN, tastyle)
prev_label = lv_label(prev_btn)
# prev_label.set_text("Prev")
prev_label.set_text("<")
next_btn = lv_btn(scr)
next_btn.set_pos(220,vres-40)
next_btn.set_size(80, 30)
next_btn.add_style(lv.OBJ_PART_MAIN, tastyle)
next_label = lv_label(next_btn)
#next_label.set_text("Next")
next_label.set_text(">")
home_btn = lv_btn(scr)
home_btn.set_pos(120,vres-40)
home_btn.set_size(80, 30)
home_btn.add_style(lv.OBJ_PART_MAIN, tastyle)
home_label = lv_label(home_btn)
home_label.set_text(lv.SYMBOL_OK)
btn.del()
logo.set_src("A:/Sunrise.bin")
logo.align(0,0,0,0)
btn.set_height(120)
label.del()
logo = lv.img_create(btn)
logo.set_tasmota_logo()
logo.set_zoom(384)
logo.set_angle(400)
logo.set_style_local_image_recolor_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, 255)
logo.set_style_local_image_recolor(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.BLUE))
#- logo on splash screen -#
btn.del()
logo = lv.img_create(lv.scr_act())
logo.set_tasmota_logo()
logo.set_style_local_image_recolor_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, 255)
logo.set_style_local_image_recolor(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.WHITE))
logo.align(0,0,0,0)
logo.set_zoom(400)
#- anil -#
logo.set_zoom(384)
angle = 0
do_rotate = nil
do_rotate = def () angle += 100 logo.set_angle(angle) tasmota.timer(100, do_rotate) end
t48 = lv.tasmota_font(48)
label.set_text("A")
if t48 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, t48) end
btn.set_height(120)
f10 = lv.montserrat_font(10)
if f10 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f10) end
#- try zooming an image -#
img = lv.img_create(btn)
label.del()
img.set_src(lv.SYMBOL_OK)
#-
f8 = lv.montserrat_font(8)
if f8 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f8) end
-#
#-
f14 = lv.montserrat_font(14)
f28 = lv.montserrat_font(28)
btn.set_height(80)
btn.set_style_local_bg_color(0, lv.STATE_DEFAULT, lv_color(0xFFEEFF))
label.set_style_local_text_font(0, lv.STATE_DEFAULT, f28)
scr = lv.scr_act()
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x400000))
scr.set_style_local_value_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f28)
label.set_style_local_value_font(lv.BTN_PART_MAIN, lv.STATE_DEFAULT, f28)
-#
#- lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); -#
#- lv.obj_set_style_local_bg_color(lv.scr_act(), lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x00FF00)) -#
#- lv_obj_set_style_local_bg_opa( lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER); -#
#-
lv.demo()
-#
else
print('ILI9341 pins are not configured')
end
#-
lv.obj_set_style_local_bg_color(lv.scr_act(), lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.BLACK))
gauge = lv.gauge_create(lv.scr_act())
gauge.set_size(150,150)
co = lv.cpicker_create(lv.scr_act())
co.set_size(150,150)
co.set_pos(170,20)
k = lv.keyboard_create(lv.scr_act())
cal = lv.calendar_create(lv.scr_act())
cal.del()
c = lv.checkbox_create(lv.scr_act())
c.del()
c = lv.chart_create(lv.scr_act())
c.del()
co.del()
k = lv.keyboard_create(lv.scr_act())
k.del()
led = lv.led_create(lv.scr_act())
led.del()
m = lv.msgbox_create(lv.scr_act())
m.del()
# menu item
rol = lv.roller_create(lv.scr_act())
rol.del()
sl = lv.slider_create(lv.scr_act())
sl.del()
sp = lv.spinner_create(lv.scr_act())
sp.del()
w = lv.win_create(lv.scr_act())
w.del()
t = lv.textarea_create(lv.scr_act())
t.set_text("Tasmota")
t.del()
-#

View File

@ -38,9 +38,9 @@ ren_sec.set_pos(110,10)
prev_day = -1
def set_watch()
now = tasmota.rtc()
time_raw = now['local'] + now['timezone'] * 60
time = tasmota.time_dump(time_raw)
var now = tasmota.rtc()
var time_raw = now['local'] + now['timezone'] * 60
var time = tasmota.time_dump(time_raw)
# set second
ren_sec.set_angle(60 * time['sec'])
# set minutes

View File

@ -44,7 +44,7 @@ def crc32_create_table()
return a
end
crc32_table = crc32_create_table()
var crc32_table = crc32_create_table()
def crc32_update(buf, crc)
crc ^= 0xffffffff
@ -137,7 +137,6 @@ class Partition_info
# print("Segment count", seg_count)
var seg_offset = addr + 0x20 # sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) = 24 + 8
var seg_size = 0
for seg_num:0..seg_count-1
# print(string.format("Reading 0x%08X", seg_offset))
@ -375,7 +374,6 @@ class Partition
#- parse the raw bytes to a structured list of partition items -#
def parse()
var i
for i:0..94 # there are maximum 95 slots + md5 (0xC00)
var item_raw = self.raw[i*32..(i+1)*32-1]
var magic = item_raw.get(0,2)
@ -754,7 +752,7 @@ class Partition_manager : Driver
end
#- create and register driver in Tasmota -#
partition_manager = Partition_manager()
var partition_manager = Partition_manager()
tasmota.add_driver(partition_manager)
## can be removed if put in 'autoexec.bat'
partition_manager.web_add_handler()

View File

@ -277,7 +277,8 @@ void BerryInit(void) {
do {
berry.vm = be_vm_new(); /* create a virtual machine instance */
be_set_obs_hook(berry.vm, &BerryObservability);
comp_set_named_gbl(berry.vm);
comp_set_named_gbl(berry.vm); /* Enable named globals in Berry compiler */
comp_set_strict(berry.vm); /* Enable strict mode in Berry compiler */
be_load_custom_libs(berry.vm);
// Register functions