From 494c594c491b0cea3786bb550cf977c1a2f3f8b9 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 26 May 2024 16:18:39 +0200 Subject: [PATCH] Berry add reuse of methods for interface-like code reuse --- CHANGELOG.md | 1 + lib/libesp32/berry/src/be_code.c | 6 +-- lib/libesp32/berry/src/be_code.h | 2 +- lib/libesp32/berry/src/be_debug.c | 2 +- lib/libesp32/berry/src/be_object.h | 10 ++--- lib/libesp32/berry/src/be_opcodes.h | 3 +- lib/libesp32/berry/src/be_parser.c | 53 ++++++++++++++++---------- lib/libesp32/berry/src/be_vm.c | 5 ++- lib/libesp32/berry/tests/set_method.be | 40 +++++++++++++++++++ 9 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 lib/libesp32/berry/tests/set_method.be diff --git a/CHANGELOG.md b/CHANGELOG.md index 26e2f2c7773a..59e2d3840146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/libesp32/berry/src/be_code.c b/lib/libesp32/berry/src/be_code.c index d6e1be99e922..ba8ea7dbaa0c 100644 --- a/lib/libesp32/berry/src/be_code.c +++ b/lib/libesp32/berry/src/be_code.c @@ -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); @@ -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*/ @@ -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); } } diff --git a/lib/libesp32/berry/src/be_code.h b/lib/libesp32/berry/src/be_code.h index f0ef2e1943c8..454a1c913c6f 100644 --- a/lib/libesp32/berry/src/be_code.h +++ b/lib/libesp32/berry/src/be_code.h @@ -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); diff --git a/lib/libesp32/berry/src/be_debug.c b/lib/libesp32/berry/src/be_debug.c index 142c4ea5eab4..32cb7fbe4f8f 100644 --- a/lib/libesp32/berry/src/be_debug.c +++ b/lib/libesp32/berry/src/be_debug.c @@ -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), diff --git a/lib/libesp32/berry/src/be_object.h b/lib/libesp32/berry/src/be_object.h index c7b4b8af62c9..e386cfa25491 100644 --- a/lib/libesp32/berry/src/be_object.h +++ b/lib/libesp32/berry/src/be_object.h @@ -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 @@ -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 */ diff --git a/lib/libesp32/berry/src/be_opcodes.h b/lib/libesp32/berry/src/be_opcodes.h index 12c3781e0645..ef80fa2546b4 100644 --- a/lib/libesp32/berry/src/be_opcodes.h +++ b/lib/libesp32/berry/src/be_opcodes.h @@ -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 */ diff --git a/lib/libesp32/berry/src/be_parser.c b/lib/libesp32/berry/src/be_parser.c index dcb4386b0ca1..1bec0863f645 100644 --- a/lib/libesp32/berry/src/be_parser.c +++ b/lib/libesp32/berry/src/be_parser.c @@ -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` */ @@ -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) @@ -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."); @@ -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."); @@ -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); } @@ -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; @@ -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); @@ -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 { @@ -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"); } @@ -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)); @@ -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); @@ -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"); } @@ -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) diff --git a/lib/libesp32/berry/src/be_vm.c b/lib/libesp32/berry/src/be_vm.c index 37352e588028..709b84c9e9d3 100644 --- a/lib/libesp32/berry/src/be_vm.c +++ b/lib/libesp32/berry/src/be_vm.c @@ -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 { @@ -994,6 +994,7 @@ static void vm_exec(bvm *vm) } dispatch(); } + opcase(SETMET): opcase(SETMBR): { #if BE_USE_PERF_COUNTERS vm->counter_set++; @@ -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)) { diff --git a/lib/libesp32/berry/tests/set_method.be b/lib/libesp32/berry/tests/set_method.be new file mode 100644 index 000000000000..b2fdc5d96846 --- /dev/null +++ b/lib/libesp32/berry/tests/set_method.be @@ -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)