mirror of https://github.com/arendst/Tasmota.git
Berry now compiling in ``strict`` mode to catch more bugs
This commit is contained in:
parent
154928fe58
commit
63bbf46d7f
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
||||
-#
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue