2021-02-07 19:19:08 +00:00
|
|
|
/********************************************************************
|
|
|
|
** 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"
|
2021-08-26 18:30:57 +01:00
|
|
|
#include <string.h>
|
2021-02-07 19:19:08 +00:00
|
|
|
|
|
|
|
#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) {
|
2021-12-04 12:49:06 +00:00
|
|
|
be_map_compact(vm, c->members); /* clear space */
|
2021-02-07 19:19:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-12 17:14:22 +00:00
|
|
|
/* Return the type of the class attribute, only used to check if the attribute already exists */
|
2021-02-07 19:19:08 +00:00
|
|
|
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;
|
2021-02-07 19:19:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-12 17:14:22 +00:00
|
|
|
void be_class_member_bind(bvm *vm, bclass *c, bstring *name, bbool var)
|
2021-02-07 19:19:08 +00:00
|
|
|
{
|
|
|
|
bvalue *attr;
|
2021-03-27 18:02:22 +00:00
|
|
|
set_fixed(name);
|
2021-02-07 19:19:08 +00:00
|
|
|
check_members(vm, c);
|
|
|
|
attr = be_map_insertstr(vm, c->members, name, NULL);
|
2021-03-27 18:02:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-02-07 19:19:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-12 17:14:22 +00:00
|
|
|
void be_class_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p, bbool is_static)
|
2021-02-07 19:19:08 +00:00
|
|
|
{
|
|
|
|
bclosure *cl;
|
|
|
|
bvalue *attr;
|
2021-03-27 18:02:22 +00:00
|
|
|
set_fixed(name);
|
2021-02-07 19:19:08 +00:00
|
|
|
check_members(vm, c);
|
|
|
|
attr = be_map_insertstr(vm, c->members, name, NULL);
|
2021-03-27 18:02:22 +00:00
|
|
|
restore_fixed(name);
|
2021-02-07 19:19:08 +00:00
|
|
|
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
|
|
|
}
|
2021-02-07 19:19:08 +00:00
|
|
|
}
|
|
|
|
|
2022-02-12 17:14:22 +00:00
|
|
|
void be_class_native_method_bind(bvm *vm, bclass *c, bstring *name, bntvfunc f)
|
2021-02-07 19:19:08 +00:00
|
|
|
{
|
|
|
|
bvalue *attr;
|
2021-03-27 18:02:22 +00:00
|
|
|
set_fixed(name);
|
2021-02-07 19:19:08 +00:00
|
|
|
check_members(vm, c);
|
|
|
|
attr = be_map_insertstr(vm, c->members, name, NULL);
|
2021-03-27 18:02:22 +00:00
|
|
|
restore_fixed(name);
|
2021-02-07 19:19:08 +00:00
|
|
|
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)
|
2021-03-27 18:02:22 +00:00
|
|
|
{
|
|
|
|
bvalue *attr;
|
|
|
|
check_members(vm, c);
|
|
|
|
attr = be_map_insertstr(vm, c->members, name, NULL);
|
|
|
|
attr->v.gc = (bgcobject*) cl;
|
|
|
|
attr->type = MT_METHOD;
|
|
|
|
}
|
|
|
|
|
2021-02-07 19:19:08 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2021-02-07 19:19:08 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-16 19:46:09 +01:00
|
|
|
/* (internal) Instanciate an instance for a single class and initialize variables to nil */
|
2021-02-07 19:19:08 +00:00
|
|
|
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 */
|
2021-08-16 19:46:09 +01:00
|
|
|
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 */
|
2021-02-07 19:19:08 +00:00
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2021-08-16 19:46:09 +01:00
|
|
|
/* (internal) Instanciate the whole chain of instances when there is a class hierarchy */
|
|
|
|
/* All variables set to nil, constructors are not called here */
|
2021-02-07 19:19:08 +00:00
|
|
|
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 */
|
2021-08-16 19:46:09 +01:00
|
|
|
for (c = c->super; c; c = c->super) { /* initialize one instance object per class and per superclass */
|
2021-02-07 19:19:08 +00:00
|
|
|
prev->super = newobjself(vm, c);
|
2021-08-16 19:46:09 +01:00
|
|
|
prev->super->sub = prev; /* link the super/sub classes instances */
|
2021-02-07 19:19:08 +00:00
|
|
|
prev = prev->super;
|
|
|
|
}
|
|
|
|
be_stackpop(vm, 1);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2021-08-16 19:46:09 +01:00
|
|
|
/* 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)
|
2021-02-07 19:19:08 +00:00
|
|
|
{
|
|
|
|
bvalue init;
|
2021-08-16 19:46:09 +01:00
|
|
|
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 */
|
2021-02-07 19:19:08 +00:00
|
|
|
/* 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;
|
2021-02-07 19:19:08 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-16 19:46:09 +01:00
|
|
|
/* 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)
|
2021-02-07 19:19:08 +00:00
|
|
|
{
|
|
|
|
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) {
|
2021-02-07 19:19:08 +00:00
|
|
|
*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 */
|
2021-08-26 18:30:57 +01:00
|
|
|
/* 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);
|
2021-08-26 18:30:57 +01:00
|
|
|
} 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 */
|
|
|
|
}
|
2021-08-26 18:30:57 +01:00
|
|
|
}
|
2022-07-05 20:14:50 +01:00
|
|
|
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-02-07 19:19:08 +00:00
|
|
|
}
|
|
|
|
|
2021-05-20 18:34:51 +01:00
|
|
|
bbool be_instance_setmember(bvm *vm, binstance *o, bstring *name, bvalue *src)
|
2021-02-07 19:19:08 +00:00
|
|
|
{
|
|
|
|
bvalue v;
|
|
|
|
be_assert(name != NULL);
|
2021-05-20 18:34:51 +01:00
|
|
|
binstance * obj = instance_member(vm, o, name, &v);
|
2021-02-07 19:19:08 +00:00
|
|
|
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;
|
2022-07-05 20:14:50 +01:00
|
|
|
/* 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
|
|
|
}
|
2021-02-07 19:19:08 +00: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);
|
2021-10-19 19:32:41 +01:00
|
|
|
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;
|
|
|
|
}
|