Tasmota/lib/libesp32/berry/src/be_class.c

375 lines
12 KiB
C
Raw Permalink Normal View History

/********************************************************************
** Copyright (c) 2018-2020 Guan Wenliang
** This file is part of the Berry default interpreter.
** skiars@qq.com, https://github.com/Skiars/berry
** See Copyright Notice in the LICENSE file or at
** https://github.com/Skiars/berry/blob/master/LICENSE
********************************************************************/
#include "be_class.h"
#include "be_string.h"
#include "be_map.h"
#include "be_exec.h"
#include "be_gc.h"
#include "be_vm.h"
#include "be_func.h"
2022-06-15 21:00:31 +01:00
#include "be_module.h"
#include <string.h>
#define check_members(vm, c) \
if (!(c)->members) { \
(c)->members = be_map_new(vm); \
}
bclass* be_newclass(bvm *vm, bstring *name, bclass *super)
{
bgcobject *gco = be_gcnew(vm, BE_CLASS, bclass);
bclass *obj = cast_class(gco);
bvalue *buf = be_incrtop(vm); /* protect new objects from GC */
var_setclass(buf, obj);
if (obj) {
obj->super = super;
obj->members = NULL; /* gc protection */
obj->nvar = 0;
obj->name = name;
}
be_stackpop(vm, 1);
return obj;
}
void be_class_compress(bvm *vm, bclass *c)
{
if (!gc_isconst(c) && c->members) {
be_map_compact(vm, c->members); /* clear space */
}
}
2022-02-12 17:14:22 +00:00
/* Return the type of the class attribute, only used to check if the attribute already exists */
int be_class_attribute(bvm *vm, bclass *c, bstring *attr)
{
for (; c; c = c->super) {
if (c->members) {
bvalue *v = be_map_findstr(vm, c->members, attr);
if (v) {
return var_type(v);
}
}
}
2021-07-19 17:45:04 +01:00
return BE_NONE;
}
2022-02-12 17:14:22 +00:00
void be_class_member_bind(bvm *vm, bclass *c, bstring *name, bbool var)
{
bvalue *attr;
set_fixed(name);
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
restore_fixed(name);
2021-07-19 17:45:04 +01:00
if (var) {
/* this is an instance variable so we set it as MT_VARIABLE */
attr->v.i = c->nvar++;
attr->type = MT_VARIABLE;
} else {
/* this is a static class constant, leave it as BE_NIL */
attr->v.i = 0;
attr->type = BE_NIL;
}
}
2022-02-12 17:14:22 +00:00
void be_class_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p, bbool is_static)
{
bclosure *cl;
bvalue *attr;
set_fixed(name);
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
restore_fixed(name);
var_setnil(attr);
cl = be_newclosure(vm, p->nupvals);
cl->proto = p;
var_setclosure(attr, cl);
2021-11-25 18:46:02 +00:00
if (is_static) {
2022-02-12 17:14:22 +00:00
var_markstatic(attr);
2021-11-25 18:46:02 +00:00
}
}
2022-02-12 17:14:22 +00:00
void be_class_native_method_bind(bvm *vm, bclass *c, bstring *name, bntvfunc f)
{
bvalue *attr;
set_fixed(name);
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
restore_fixed(name);
attr->v.nf = f;
attr->type = MT_PRIMMETHOD;
}
2022-02-12 17:14:22 +00:00
void be_class_closure_method_bind(bvm *vm, bclass *c, bstring *name, bclosure *cl)
{
bvalue *attr;
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
attr->v.gc = (bgcobject*) cl;
attr->type = MT_METHOD;
}
/* get the closure method count that need upvalues */
int be_class_closure_count(bclass *c)
{
int count = 0;
if (c->members) {
bmapnode *node;
bmap *members = c->members;
bmapiter iter = be_map_iter();
while ((node = be_map_next(members, &iter)) != NULL) {
if (var_isproto(&node->value)) {
++count;
}
}
}
return count;
}
static binstance* instance_member(bvm *vm,
binstance *obj, bstring *name, bvalue *dst)
{
for (; obj; obj = obj->super) {
bmap *members = obj->_class->members;
if (members) {
bvalue *v = be_map_findstr(vm, members, name);
if (v) {
*dst = *v;
return obj;
}
}
}
var_setnil(dst);
return NULL;
}
2021-07-19 17:45:04 +01:00
static bclass* class_member(bvm *vm,
bclass *obj, bstring *name, bvalue *dst)
{
for (; obj; obj = obj->super) {
bmap *members = obj->members;
if (members) {
bvalue *v = be_map_findstr(vm, members, name);
if (v) {
*dst = *v;
return obj;
}
}
}
var_setnil(dst);
return NULL;
}
void be_class_upvalue_init(bvm *vm, bclass *c)
{
bmap *mbr = c->members;
if (mbr != NULL) {
bmapnode *node;
bmapiter iter = be_map_iter();
while ((node = be_map_next(mbr, &iter)) != NULL) {
if (var_isclosure(&node->value)) {
bclosure *cl = var_toobj(&node->value);
if (cl->proto->nupvals) {
/* initialize the closure's upvalues */
be_release_upvalues(vm, cl);
be_initupvals(vm, cl);
}
}
}
}
}
/* (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);
bgcobject *gco = be_newgcobj(vm, BE_INSTANCE, size);
binstance *obj = cast_instance(gco);
be_assert(obj != NULL);
if (obj) { /* initialize members */
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;
be_assert(c != NULL);
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) { /* initialize one instance object per class and per superclass */
prev->super = newobjself(vm, c);
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 */
2021-12-04 12:46:43 +00:00
bbool be_class_newobj(bvm *vm, bclass *c, int pos, int argc, int mode)
{
bvalue init;
binstance *obj = newobject(vm, c); /* create empty object hierarchy from class hierarchy */
2021-11-07 19:02:56 +00:00
// reg = vm->reg + pos - mode; /* the stack may have changed, mode=1 when class is instanciated from module #104 */
var_setinstance(vm->reg + pos, obj);
var_setinstance(vm->reg + pos - 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) {
/* copy argv */
2021-11-07 19:02:56 +00:00
bvalue * reg;
for (reg = vm->reg + pos + 1; argc > 0; --argc) {
reg[argc] = reg[argc - 2];
}
*reg = init; /* set constructor */
return btrue;
}
return bfalse;
}
2021-08-26 20:51:19 +01:00
/* Find instance member by name and copy value to `dst` */
/* Do not look into virtual members */
int be_instance_member_simple(bvm *vm, binstance *instance, bstring *name, bvalue *dst)
{
int type;
be_assert(name != NULL);
binstance * obj = instance_member(vm, instance, name, dst);
2022-02-12 17:14:22 +00:00
if (obj && var_type(dst) == MT_VARIABLE) {
2021-08-26 20:51:19 +01:00
*dst = obj->members[dst->v.i];
}
2022-02-12 17:14:22 +00:00
type = var_type(dst);
var_clearstatic(dst);
2021-08-26 20:51:19 +01:00
return type;
}
/* 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 */
2021-08-24 21:44:33 +01:00
int be_instance_member(bvm *vm, binstance *instance, bstring *name, bvalue *dst)
{
int type;
be_assert(name != NULL);
2022-02-12 17:14:22 +00:00
binstance *obj = instance_member(vm, instance, name, dst);
if (obj && var_type(dst) == MT_VARIABLE) {
*dst = obj->members[dst->v.i];
}
2022-02-12 17:14:22 +00:00
type = var_type(dst);
2021-07-19 17:45:04 +01:00
if (obj) {
2022-02-12 17:14:22 +00:00
var_clearstatic(dst);
2021-07-19 17:45:04 +01:00
return type;
2021-08-24 21:44:33 +01:00
} else { /* if no method found, try virtual */
/* if 'init' does not exist, create a virtual empty constructor */
if (strcmp(str(name), "init") == 0) {
2022-06-15 21:00:31 +01:00
var_setntvfunc(dst, be_default_init_native_function);
2022-02-12 17:14:22 +00:00
return var_primetype(dst);
} else {
/* get method 'member' */
obj = instance_member(vm, instance, str_literal(vm, "member"), vm->top);
if (obj && basetype(var_type(vm->top)) == BE_FUNCTION) {
bvalue *top = vm->top;
var_setinstance(&top[1], instance);
var_setstr(&top[2], name);
vm->top += 3; /* prevent gc collection results */
be_dofunc(vm, top, 2); /* call method 'member' */
vm->top -= 3;
*dst = *vm->top; /* copy result to R(A) */
if (obj && var_type(dst) == MT_VARIABLE) {
*dst = obj->members[dst->v.i];
}
type = var_type(dst);
2022-06-15 21:00:31 +01:00
if (type == BE_MODULE) {
/* check if the module is named `undefined` */
bmodule *mod = var_toobj(dst);
if (strcmp(be_module_name(mod), "undefined") == 0) {
return BE_NONE; /* if the return value is module `undefined`, consider it is an error */
}
}
var_clearstatic(dst);
return type;
2021-08-24 21:44:33 +01:00
}
}
2021-07-19 17:45:04 +01:00
}
2021-08-24 21:44:33 +01:00
return BE_NONE;
2021-07-19 17:45:04 +01:00
}
int be_class_member(bvm *vm, bclass *obj, bstring *name, bvalue *dst)
{
int type;
be_assert(name != NULL);
obj = class_member(vm, obj, name, dst);
type = var_type(dst);
2022-02-12 17:14:22 +00:00
var_clearstatic(dst);
return obj ? type : BE_NONE;
}
2021-05-20 18:34:51 +01:00
bbool be_instance_setmember(bvm *vm, binstance *o, bstring *name, bvalue *src)
{
bvalue v;
be_assert(name != NULL);
2021-05-20 18:34:51 +01:00
binstance * obj = instance_member(vm, o, name, &v);
if (obj && var_istype(&v, MT_VARIABLE)) {
obj->members[var_toint(&v)] = *src;
return btrue;
2021-05-20 18:34:51 +01:00
} else {
obj = instance_member(vm, o, str_literal(vm, "setmember"), &v);
if (obj && var_type(&v) == MT_VARIABLE) {
v = obj->members[v.v.i];
}
if (var_basetype(&v) == BE_FUNCTION) {
bvalue *top = vm->top;
var_setval(top, &v);
var_setinstance(top + 1, o); /* move instance to argv[0] */
var_setstr(top + 2, name); /* move method name to argv[1] */
var_setval(top + 3, src); /* move method name to argv[1] */
vm->top += 4; /* prevent collection results */
be_dofunc(vm, top, 3); /* call method 'member' */
vm->top -= 4;
/* if return value is `false` or `undefined` signal an unknown attribute */
int type = var_type(vm->top);
if (type == BE_BOOL) {
bbool ret = var_tobool(vm->top);
if (!ret) {
return bfalse;
}
} else if (type == BE_MODULE) {
/* check if the module is named `undefined` */
bmodule *mod = var_toobj(vm->top);
if (strcmp(be_module_name(mod), "undefined") == 0) {
return bfalse; /* if the return value is module `undefined`, consider it is an error */
}
}
2021-10-04 20:48:43 +01:00
return btrue;
2021-05-20 18:34:51 +01:00
}
}
return bfalse;
}
2021-07-19 17:45:04 +01:00
bbool be_class_setmember(bvm *vm, bclass *o, bstring *name, bvalue *src)
{
bvalue v;
be_assert(name != NULL);
if (!gc_isconst(o)) {
bclass * obj = class_member(vm, o, name, &v);
if (obj && !var_istype(&v, MT_VARIABLE)) {
be_map_insertstr(vm, obj->members, name, src);
return btrue;
}
2021-07-19 17:45:04 +01:00
}
return bfalse;
}