Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Berry add reuse of methods for interface-like code reuse #21500

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- Optional command ``WebRun`` (as WebQuery extension) (#21364)
- Support for Knx dimmer and color (#21434)
- Support for Matter 1.3 Water leak detectors (#21456)
- Berry add reuse of methods for interface-like code reuse

### Breaking Changed

Expand Down
6 changes: 3 additions & 3 deletions lib/libesp32/berry/src/be_code.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src)
/* e1 must be in a register and have a valid idx */
/* if `keep_reg` is true, do not release register */
/* return 1 if assignment was possible, 0 if type is not compatible */
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg)
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg, bbool ismethod)
{
/* free_e2 indicates special case where ETINDEX or ETMEMBER need to be freed if top of registers */
bbool free_e2 = (e2->type == ETINDEX || e2->type == ETMEMBER) && (e2->v.ss.idx != e1->v.idx) && (e2->v.ss.idx == finfo->freereg - 1);
Expand Down Expand Up @@ -729,7 +729,7 @@ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg)
break;
case ETMEMBER: /* store to member R(A).RK(B) <- RK(C) */
case ETINDEX: /* store to member R(A)[RK(B)] <- RK(C) */
setsfxvar(finfo, (e1->type == ETMEMBER) ? OP_SETMBR : OP_SETIDX, e1, src);
setsfxvar(finfo, (e1->type == ETMEMBER) ? (ismethod ? OP_SETMET : OP_SETMBR) : OP_SETIDX, e1, src);
if (keep_reg && e2->type == ETREG && e1->v.ss.obj >= be_list_count(finfo->local)) {
/* special case of walrus assignemnt when we need to recreate an ETREG */
code_move(finfo, e1->v.ss.obj, src); /* move from ETREG to MEMBER instance*/
Expand Down Expand Up @@ -923,7 +923,7 @@ void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v)
codeABC(finfo, OP_IMPORT, dst, src, 0);
m->type = ETREG;
m->v.idx = dst;
be_code_setvar(finfo, v, m, bfalse);
be_code_setvar(finfo, v, m, bfalse, bfalse);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/libesp32/berry/src/be_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ int be_code_allocregs(bfuncinfo *finfo, int count);
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e);
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst);
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e);
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg);
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg, bbool ismethod);
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e);
int be_code_jump(bfuncinfo *finfo);
void be_code_jumpto(bfuncinfo *finfo, int dst);
Expand Down
2 changes: 1 addition & 1 deletion lib/libesp32/berry/src/be_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void be_print_inst(binstruction ins, int pc, void* fout)
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
case OP_MOD: case OP_LT: case OP_LE: case OP_EQ:
case OP_NE: case OP_GT: case OP_GE: case OP_CONNECT:
case OP_GETMBR: case OP_SETMBR: case OP_GETMET:
case OP_GETMBR: case OP_SETMBR: case OP_GETMET: case OP_SETMET:
case OP_GETIDX: case OP_SETIDX: case OP_AND:
case OP_OR: case OP_XOR: case OP_SHL: case OP_SHR:
logbuf("%s\tR%d\t%c%d\t%c%d", opc2str(op), IGET_RA(ins),
Expand Down
10 changes: 5 additions & 5 deletions lib/libesp32/berry/src/be_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#define BE_FUNCTION 6

#define BE_GCOBJECT 16 /* from this type can be gced */
#define BE_GCOBJECT_MAX (3<<5) /* from this type can't be gced */
#define BE_GCOBJECT_MAX (3<<5) /* 96 - from this type can't be gced */

#define BE_STRING 16
#define BE_CLASS 17
Expand All @@ -32,10 +32,10 @@
#define BE_MODULE 22
#define BE_COMOBJ 23 /* common object */

#define BE_NTVFUNC ((0 << 5) | BE_FUNCTION) /* 6 */
#define BE_CLOSURE ((1 << 5) | BE_FUNCTION) /* 38 */
#define BE_NTVCLOS ((2 << 5) | BE_FUNCTION) /* 70 */
#define BE_CTYPE_FUNC ((3 << 5) | BE_FUNCTION) /* 102 */
#define BE_NTVFUNC ((0 << 5) | BE_FUNCTION) /* 6 - cannot be gced */
#define BE_CLOSURE ((1 << 5) | BE_FUNCTION) /* 38 - can be gced */
#define BE_NTVCLOS ((2 << 5) | BE_FUNCTION) /* 70 - can be gced*/
#define BE_CTYPE_FUNC ((3 << 5) | BE_FUNCTION) /* 102 - cannot be gced */
#define BE_STATIC (1 << 7) /* 128 */

/* values for bproto.varg */
Expand Down
3 changes: 2 additions & 1 deletion lib/libesp32/berry/src/be_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ OPCODE(CATCH), /* 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[RK(B)] by name */
OPCODE(SETNGBL) /* A, B | R(A) -> GLOBAL[RK(B)] by name */
OPCODE(SETNGBL), /* A, B | R(A) -> GLOBAL[RK(B)] by name */
OPCODE(SETMET), /* A, B, C | R(A).RK(B) <- RK(C) only if R(A) is a class, and RK(C) is marked as non-static */
53 changes: 32 additions & 21 deletions lib/libesp32/berry/src/be_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ static bproto* funcbody(bparser *parser, bstring *name, bclass *c, int type)
new_var(parser, parser_newstr(parser, "_class"), &e1); /* new implicit variable '_class' */
init_exp(&e2, ETCONST, 0);
be_code_implicit_class(parser->finfo, &e2, c);
be_code_setvar(parser->finfo, &e1, &e2, bfalse);
be_code_setvar(parser->finfo, &e1, &e2, bfalse, bfalse);
finfo.proto->varg |= BE_VA_STATICMETHOD;
}
stmtlist(parser); /* parse statement without final `end` */
Expand Down Expand Up @@ -744,7 +744,7 @@ static void map_nextmember(bparser *parser, bexpdesc *l)
match_token(parser, OptColon); /* ':' */
expr(parser, &e); /* value in `e` */
check_var(parser, &e); /* check if value is correct */
be_code_setvar(finfo, &v, &e, bfalse); /* set suffi INDEX value to e */
be_code_setvar(finfo, &v, &e, bfalse, bfalse); /* set suffi INDEX value to e */
}

static void list_expr(bparser *parser, bexpdesc *e)
Expand Down Expand Up @@ -1024,7 +1024,7 @@ static void assign_expr(bparser *parser)
if (check_newvar(parser, &e)) { /* new variable */
new_var(parser, e.v.s, &e);
}
if (be_code_setvar(parser->finfo, &e, &e1, bfalse)) {
if (be_code_setvar(parser->finfo, &e, &e1, bfalse, bfalse)) {
parser->lexer.linenumber = line;
parser_error(parser,
"try to assign constant expressions.");
Expand Down Expand Up @@ -1127,7 +1127,7 @@ static void walrus_expr(bparser *parser, bexpdesc *e)
if (check_newvar(parser, &e1)) { /* new variable */
new_var(parser, e1.v.s, &e1);
}
if (be_code_setvar(parser->finfo, &e1, e, btrue /* do not release register */ )) {
if (be_code_setvar(parser->finfo, &e1, e, btrue /* do not release register */, bfalse)) {
parser->lexer.linenumber = line;
parser_error(parser,
"try to assign constant expressions.");
Expand Down Expand Up @@ -1260,7 +1260,7 @@ static void for_iter(bparser *parser, bstring *var, bexpdesc *it)
finfo->binfo->beginpc = finfo->pc;
/* itvar = .it() */
init_exp(&e, ETLOCAL, new_localvar(parser, var)); /* new itvar */
be_code_setvar(finfo, &e, it, bfalse); /* code function to variable '.it' */
be_code_setvar(finfo, &e, it, bfalse, bfalse); /* code function to variable '.it' */
be_code_call(finfo, e.v.idx, 0); /* itvar <- call .it() */
stmtlist(parser);
}
Expand Down Expand Up @@ -1441,7 +1441,7 @@ static void classvar_stmt(bparser *parser, bclass *c)
}
}

static void class_static_assignment_expr(bparser *parser, bexpdesc *e, bstring *name)
static void class_static_assignment_expr(bparser *parser, bexpdesc *e, bstring *name, bbool ismethod)
{
if (match_skip(parser, OptAssign)) { /* '=' */
bexpdesc e1, e2;
Expand All @@ -1454,22 +1454,32 @@ static void class_static_assignment_expr(bparser *parser, bexpdesc *e, bstring *
key.v.s = name;

be_code_member(parser->finfo, &e1, &key); /* compute member accessor */
be_code_setvar(parser->finfo, &e1, &e2, bfalse); /* set member */
be_code_setvar(parser->finfo, &e1, &e2, bfalse, ismethod); /* set member */
}
}

static void classdef_stmt(bparser *parser, bclass *c, bbool is_static)
static void classdef_stmt(bparser *parser, bclass *c, bexpdesc *e, bbool is_static)
{
bexpdesc e;
bexpdesc e1;
bstring *name;
bproto *proto;
/* 'def' ID '(' varlist ')' block 'end' */
/* 'def' ID = funcname */
/* 'def' ID = classname '.' method */
scan_next_token(parser); /* skip 'def' */
name = func_name(parser, &e, 1);
check_class_attr(parser, c, name);
proto = funcbody(parser, name, c, is_static ? FUNC_STATIC : FUNC_METHOD);
be_class_method_bind(parser->vm, c, proto->name, proto, is_static);
be_stackpop(parser->vm, 1);
name = func_name(parser, &e1, 1);
check_class_attr(parser, c, name); /* check that we don't redefine an existing name within the class */
if (next_type(parser) == OptAssign) {
/* 'def' ID = funcname */
/* 'def' ID = classname '.' method */
be_class_member_bind(parser->vm, c, name, bfalse);
class_static_assignment_expr(parser, e, name, !is_static);
} else {
/* 'def' ID '(' varlist ')' block 'end' */
proto = funcbody(parser, name, c, is_static ? FUNC_STATIC : FUNC_METHOD);
be_class_method_bind(parser->vm, c, proto->name, proto, is_static);
be_stackpop(parser->vm, 1);
}
}

static void classstaticclass_stmt(bparser *parser, bclass *c_out, bexpdesc *e_out);
Expand All @@ -1479,9 +1489,10 @@ static void classstatic_stmt(bparser *parser, bclass *c, bexpdesc *e)
bstring *name;
/* 'static' ['var'] ID ['=' expr] {',' ID ['=' expr] } */
/* 'static' 'def' ID '(' varlist ')' block 'end' */
/* 'static' 'def' ID '=' func */
scan_next_token(parser); /* skip 'static' */
if (next_type(parser) == KeyDef) { /* 'static' 'def' ... */
classdef_stmt(parser, c, btrue);
classdef_stmt(parser, c, e, btrue);
} else if (next_type(parser) == KeyClass) { /* 'static' 'class' ... */
classstaticclass_stmt(parser, c, e);
} else {
Expand All @@ -1491,13 +1502,13 @@ static void classstatic_stmt(bparser *parser, bclass *c, bexpdesc *e)
if (match_id(parser, name) != NULL) {
check_class_attr(parser, c, name);
be_class_member_bind(parser->vm, c, name, bfalse);
class_static_assignment_expr(parser, e, name);
class_static_assignment_expr(parser, e, name, bfalse);

while (match_skip(parser, OptComma)) { /* ',' */
if (match_id(parser, name) != NULL) {
check_class_attr(parser, c, name);
be_class_member_bind(parser->vm, c, name, bfalse);
class_static_assignment_expr(parser, e, name);
class_static_assignment_expr(parser, e, name, bfalse);
} else {
parser_error(parser, "class static error");
}
Expand Down Expand Up @@ -1527,7 +1538,7 @@ static void class_block(bparser *parser, bclass *c, bexpdesc *e)
switch (next_type(parser)) {
case KeyVar: classvar_stmt(parser, c); break;
case KeyStatic: classstatic_stmt(parser, c, e); break;
case KeyDef: classdef_stmt(parser, c, bfalse); break;
case KeyDef: classdef_stmt(parser, c, e, bfalse); break;
case OptSemic: scan_next_token(parser); break;
default: push_error(parser,
"unexpected token '%s'", token2str(parser));
Expand All @@ -1554,7 +1565,7 @@ static void class_stmt(bparser *parser)
bexpdesc e1; /* if inline class, we add a second local variable for _class */
init_exp(&e1, ETLOCAL, 0);
e1.v.idx = new_localvar(parser, class_str);
be_code_setvar(parser->finfo, &e1, &e, 1);
be_code_setvar(parser->finfo, &e1, &e, btrue, bfalse);

begin_varinfo(parser, class_str);

Expand Down Expand Up @@ -1592,7 +1603,7 @@ static void classstaticclass_stmt(bparser *parser, bclass *c_out, bexpdesc *e_ou
key.v.s = name;
/* assign the class to the static member */
be_code_member(parser->finfo, &e1, &key); /* compute member accessor */
be_code_setvar(parser->finfo, &e1, &e_class, bfalse); /* set member */
be_code_setvar(parser->finfo, &e1, &e_class, bfalse, bfalse); /* set member */
} else {
parser_error(parser, "class name error");
}
Expand Down Expand Up @@ -1644,7 +1655,7 @@ static void var_field(bparser *parser)
init_exp(&e2, ETNIL, 0);
}
new_var(parser, name, &e1); /* new variable */
be_code_setvar(parser->finfo, &e1, &e2, bfalse);
be_code_setvar(parser->finfo, &e1, &e2, bfalse, bfalse);
}

static void var_stmt(bparser *parser)
Expand Down
5 changes: 3 additions & 2 deletions lib/libesp32/berry/src/be_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ static void vm_exec(bvm *vm)
}
} else if (var_isclass(a)) {
/* in this case we have a class in a static or non-static member */
/* it's always treated like a statif function */
/* it's always treated like a static function */
a[1] = result;
var_settype(a, NOT_METHOD);
} else {
Expand All @@ -994,6 +994,7 @@ static void vm_exec(bvm *vm)
}
dispatch();
}
opcase(SETMET):
opcase(SETMBR): {
#if BE_USE_PERF_COUNTERS
vm->counter_set++;
Expand All @@ -1020,7 +1021,7 @@ static void vm_exec(bvm *vm)
bclass *obj = var_toobj(a);
bstring *attr = var_tostr(b);
bvalue result = *c;
if (var_isfunction(&result)) {
if (var_isfunction(&result) && (IGET_OP(ins) == OP_SETMBR)) { /* don't mark as static if SETMET was used */
var_markstatic(&result);
}
if (!be_class_setmember(vm, obj, attr, &result)) {
Expand Down
40 changes: 40 additions & 0 deletions lib/libesp32/berry/tests/set_method.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# test setting methods as an external class

class A
var a
def init(a)
self.a = a
end
def f0(x) return self end
def f1(x) return x end
def f2(x) return self.a end
static def ff0(x) return _class end
static def ff1(x) return x end
end

class B
var b
def init(b)
self.b = b
end
end

class C : B
var a
def init(a)
self.a = a
end

def fc0 = A.f0
def fc1 = A.f1
def fc2 = A.f2
static def ffc0 = A.ff0
static def ffc1 = A.ff1
end

c = C(10)
assert(c.fc0(1) == c)
assert(c.fc1(1) == 1)
assert(c.fc2(1) == 10)
assert(c.ffc0(1) == A)
assert(c.ffc1(1) == 1)