Berry introspect.vcall

This commit is contained in:
Stephan Hadinger 2021-09-01 09:12:50 +02:00
parent 1b5406fdd9
commit cb42e241e6
11 changed files with 1923 additions and 1676 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,14 @@
static be_define_const_map_slots(m_libintrospect_map) {
{ be_const_key(members, -1), be_const_func(m_attrlist) },
{ be_const_key(set, -1), be_const_func(m_setmember) },
{ be_const_key(vcall, -1), be_const_func(m_vcall) },
{ be_const_key(get, -1), be_const_func(m_findmember) },
{ be_const_key(set, 2), be_const_func(m_setmember) },
};
static be_define_const_map(
m_libintrospect_map,
3
4
);
static be_define_const_module(

View File

@ -306,48 +306,48 @@ static uint8_t buf_get1(buf_impl* buf, int offset)
return 0;
}
static void buf_set1(buf_impl* buf, const size_t offset, const uint8_t data)
static void buf_set1(buf_impl* buf, size_t offset, uint8_t data)
{
if (offset < buf->len) {
buf->buf[offset] = data;
}
}
static void buf_set2_le(buf_impl* buf, const size_t offset, const uint16_t data)
static void buf_set2_le(buf_impl* buf, size_t offset, uint16_t data)
{
if ((offset >= 0) && (offset < buf->len - 1)) {
if (offset + 1 < buf->len) {
buf->buf[offset] = data & 0xFF;
buf->buf[offset+1] = data >> 8;
}
}
static void buf_set2_be(buf_impl* buf, const size_t offset, const uint16_t data)
static void buf_set2_be(buf_impl* buf, size_t offset, uint16_t data)
{
if ((offset >= 0) && (offset < buf->len - 1)) {
if (offset + 1 < buf->len) {
buf->buf[offset+1] = data & 0xFF;
buf->buf[offset] = data >> 8;
}
}
static uint16_t buf_get2_le(buf_impl* buf, int offset)
static uint16_t buf_get2_le(buf_impl* buf, size_t offset)
{
if ((offset >= 0) && (offset < buf->len - 1)) {
if (offset + 1 < buf->len) {
return buf->buf[offset] | (buf->buf[offset+1] << 8);
}
return 0;
}
static uint16_t buf_get2_be(buf_impl* buf, int offset)
static uint16_t buf_get2_be(buf_impl* buf, size_t offset)
{
if (offset < buf->len - 1) {
if (offset + 1 < buf->len) {
return buf->buf[offset+1] | (buf->buf[offset] << 8);
}
return 0;
}
static void buf_set4_le(buf_impl* buf, const size_t offset, const uint32_t data)
static void buf_set4_le(buf_impl* buf, size_t offset, uint32_t data)
{
if ((offset >= 0) && (offset < buf->len - 3)) {
if (offset + 3 < buf->len) {
buf->buf[offset] = data & 0xFF;
buf->buf[offset+1] = (data >> 8) & 0xFF;
buf->buf[offset+2] = (data >> 16) & 0xFF;
@ -355,9 +355,9 @@ static void buf_set4_le(buf_impl* buf, const size_t offset, const uint32_t data)
}
}
static void buf_set4_be(buf_impl* buf, const size_t offset, const uint32_t data)
static void buf_set4_be(buf_impl* buf, size_t offset, uint32_t data)
{
if ((offset >= 0) && (offset < buf->len - 3)) {
if (offset + 3 < buf->len) {
buf->buf[offset+3] = data & 0xFF;
buf->buf[offset+2] = (data >> 8) & 0xFF;
buf->buf[offset+1] = (data >> 16) & 0xFF;
@ -365,18 +365,18 @@ static void buf_set4_be(buf_impl* buf, const size_t offset, const uint32_t data)
}
}
static uint32_t buf_get4_le(buf_impl* buf, int offset)
static uint32_t buf_get4_le(buf_impl* buf, size_t offset)
{
if ((offset >= 0) && (offset < buf->len - 3)) {
if (offset + 3 < buf->len) {
return buf->buf[offset] | (buf->buf[offset+1] << 8) |
(buf->buf[offset+2] << 16) | (buf->buf[offset+3] << 24);
}
return 0;
}
static uint32_t buf_get4_be(buf_impl* buf, int offset)
static uint32_t buf_get4_be(buf_impl* buf, size_t offset)
{
if (offset < buf->len - 3) {
if (offset + 3 < buf->len) {
return buf->buf[offset+3] | (buf->buf[offset+2] << 8) |
(buf->buf[offset+1] << 16) | (buf->buf[offset] << 24);
}

View File

@ -76,12 +76,62 @@ static int m_setmember(bvm *vm)
be_return_nil(vm);
}
/* call a function with variable number of arguments */
/* first argument is a callable object (function, closure, native function, native closure) */
/* then all subsequent arguments are pushed except the last one */
/* If the last argument is a 'list', then all elements are pushed as arguments */
/* otherwise the last argument is pushed as well */
static int m_vcall(bvm *vm)
{
int top = be_top(vm);
if (top >= 1 && be_isfunction(vm, 1)) {
size_t arg_count = top - 1; /* we have at least 'top - 1' arguments */
/* test if last argument is a list */
if (top > 1 && be_isinstance(vm, top) && be_getmember(vm, top, ".p") && be_islist(vm, top + 1)) {
int32_t list_size = be_data_size(vm, top + 1);
if (list_size > 0) {
be_stack_require(vm, list_size + 3); /* make sure we don't overflow the stack */
for (int i = 0; i < list_size; i++) {
be_pushnil(vm);
}
be_moveto(vm, top + 1, top + 1 + list_size);
be_moveto(vm, top, top + list_size);
be_refpush(vm, -2);
be_pushiter(vm, -1);
while (be_iter_hasnext(vm, -2)) {
be_iter_next(vm, -2);
be_moveto(vm, -1, top);
top++;
be_pop(vm, 1);
}
be_pop(vm, 1); /* remove iterator */
be_refpop(vm);
}
be_pop(vm, 2);
arg_count = arg_count - 1 + list_size;
}
/* actual call */
be_call(vm, arg_count);
/* remove args */
be_pop(vm, arg_count);
/* return value */
be_return(vm);
}
be_raise(vm, "value_error", "first argument must be a function");
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(introspect) {
be_native_module_function("members", m_attrlist),
be_native_module_function("get", m_findmember),
be_native_module_function("set", m_setmember),
be_native_module_function("vcall", m_vcall),
};
be_define_native_module(introspect, NULL);
@ -92,6 +142,7 @@ module introspect (scope: global, depend: BE_USE_INTROSPECT_MODULE) {
get, func(m_findmember)
set, func(m_setmember)
vcall, func(m_vcall)
}
@const_object_info_end */
#include "../generate/be_fixed_introspect.h"

View File

@ -18,6 +18,7 @@
#include <stdio.h>
#if BE_USE_SOLIDIFY_MODULE
#include <inttypes.h>
#ifndef INST_BUF_SIZE
#define INST_BUF_SIZE 96
@ -60,7 +61,7 @@ static void m_solidify_bvalue(bvm *vm, bvalue * value)
#if BE_USE_SINGLE_FLOAT
logfmt("be_const_real_hex(0x%08X)", (uint32_t) var_toobj(value));
#else
logfmt("be_const_real_hex(0x%016llX)", (uint64_t) var_toobj(value));
logfmt("be_const_real_hex(0x%016" PRIx64 ")", (uint64_t)var_toobj(value));
#endif
break;
case BE_STRING:

View File

@ -123,6 +123,8 @@
_vm->cf->status = PRIM_FUNC; \
}
static void prep_closure(bvm *vm, bvalue *reg, int argc, int mode);
static void attribute_error(bvm *vm, const char *t, bvalue *b, bvalue *c)
{
const char *attr = var_isstr(c) ? str(var_tostr(c)) : be_vtype2str(c);
@ -1011,29 +1013,31 @@ newframe: /* a new call frame */
goto recall; /* call '()' method */
}
case BE_CLOSURE: {
bvalue *v, *end;
bproto *proto = var2cl(var)->proto; /* get proto for closure */
push_closure(vm, var, proto->nstack, mode); /* prepare stack for closure */
// bvalue *v, *end;
// 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);
// }
// if (proto->varg) { /* there are vararg at the last argument, build the list */
// /* code below uses mostly low-level calls for performance */
// be_stack_require(vm, argc + 2); /* make sure we don't overflow the stack */
// bvalue *top_save = vm->top; /* save original stack, we need fresh slots to create the 'list' instance */
// vm->top = v; /* move top of stack right after last argument */
// be_newobject(vm, "list"); /* this creates 2 objects on stack: list instance, BE_LIST object */
// blist *list = var_toobj(vm->top-1); /* get low-level BE_LIST structure */
// v = reg + proto->argc - 1; /* last argument */
// for (; v < reg + argc; v++) {
// be_list_push(vm, list, v); /* push all varargs into list */
// }
// *(reg + proto->argc - 1) = *(vm->top-2); /* change the vararg argument to now contain the list instance */
// vm->top = top_save; /* restore top of stack pointer */
// }
prep_closure(vm, var, argc, mode);
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);
}
if (proto->varg) { /* there are vararg at the last argument, build the list */
/* code below uses mostly low-level calls for performance */
be_stack_require(vm, argc + 2); /* make sure we don't overflow the stack */
bvalue *top_save = vm->top; /* save original stack, we need fresh slots to create the 'list' instance */
vm->top = v; /* move top of stack right after last argument */
be_newobject(vm, "list"); /* this creates 2 objects on stack: list instance, BE_LIST object */
blist *list = var_toobj(vm->top-1); /* get low-level BE_LIST structure */
v = reg + proto->argc - 1; /* last argument */
for (; v < reg + argc; v++) {
be_list_push(vm, list, v); /* push all varargs into list */
}
*(reg + proto->argc - 1) = *(vm->top-2); /* change the vararg argument to now contain the list instance */
vm->top = top_save; /* restore top of stack pointer */
}
goto newframe; /* continue execution of the closure */
}
case BE_NTVCLOS: {
@ -1094,16 +1098,43 @@ newframe: /* a new call frame */
}
}
static void do_closure(bvm *vm, bvalue *reg, int argc)
static void prep_closure(bvm *vm, bvalue *reg, int argc, int mode)
{
bvalue *v, *end;
bproto *proto = var2cl(reg)->proto;
push_closure(vm, reg, proto->nstack, 0);
push_closure(vm, reg, proto->nstack, mode);
v = vm->reg + argc;
end = vm->reg + proto->argc;
for (; v <= end; ++v) {
var_setnil(v);
}
if (proto->varg) { /* there are vararg at the last argument, build the list */
/* code below uses mostly low-level calls for performance */
be_stack_require(vm, argc + 2); /* make sure we don't overflow the stack */
bvalue *top_save = vm->top; /* save original stack, we need fresh slots to create the 'list' instance */
vm->top = v; /* move top of stack right after last argument */
be_newobject(vm, "list"); /* this creates 2 objects on stack: list instance, BE_LIST object */
blist *list = var_toobj(vm->top-1); /* get low-level BE_LIST structure */
v = vm->reg + proto->argc - 1; /* last argument */
for (; v < vm->reg + argc; v++) {
be_list_push(vm, list, v); /* push all varargs into list */
}
*(vm->reg + proto->argc - 1) = *(vm->top-2); /* change the vararg argument to now contain the list instance */
vm->top = top_save; /* restore top of stack pointer */
}
}
static void do_closure(bvm *vm, bvalue *reg, int argc)
{
// bvalue *v, *end;
// bproto *proto = var2cl(reg)->proto;
// push_closure(vm, reg, proto->nstack, 0);
// v = vm->reg + argc;
// end = vm->reg + proto->argc;
// for (; v <= end; ++v) {
// var_setnil(v);
// }
prep_closure(vm, reg, argc, 0);
vm_exec(vm);
}

View File

@ -67,3 +67,27 @@ assert(type(a.h) == 'function')
assert_attribute_error("a.g(1,2)")
assert(a.h(1) == 'instance')
# A.h(1) - error
#- test static initializers -#
class A
static a = 1, b, c = 3.5, d = 42, e = "foo", f = [1], g = {}
var aa,ab
end
assert(A.a == 1)
assert(A.b == nil)
assert(A.c == 3.5)
assert(A.d == 42)
assert(A.e == "foo")
assert(A.f == [1])
a = A()
assert(a.a == 1)
assert(a.b == nil)
assert(a.c == 3.5)
assert(a.d == 42)
assert(a.e == "foo")
assert(a.f == [1])
assert(a.g == A.g)
assert(a.aa == nil)
assert(a.ab == nil)

View File

@ -0,0 +1,28 @@
#- introspect -#
import introspect
#- test for modules -#
m = module("m")
m.a = 1
m.b = def () return "foo" end
assert(introspect.members(m) == ['a', 'b'])
assert(introspect.get(m, 'a') == 1)
assert(type(introspect.get(m, 'b')) == 'function')
introspect.set(m, 'a', 2)
assert(m.a == 2)
#- test for instance -#
class A var a,b static c=1,d=2 def f() end end
a=A()
assert(introspect.members(A) == ['a', 'f', 'b', 'c', 'd']) #- class members -#
assert(introspect.members(a) == ['a', 'f', 'b', 'c', 'd']) #- instance members -#
assert(introspect.get(a, 'c') == 1)
assert(introspect.get(a, 'd') == 2)
assert(introspect.get(a, 'a') == nil)
introspect.set(a, 'a', 3)
assert(a.a == 3)

View File

@ -0,0 +1,96 @@
#- introspect vcall -#
import introspect
#- -#
#- witness function dumping first 3 args -#
#- -#
def f(a,b,c) return [a,b,c] end
#- simple calls with fixed args -#
assert(introspect.vcall(f) == [nil, nil, nil])
assert(introspect.vcall(f, 1) == [1, nil, nil])
assert(introspect.vcall(f, 1, 2) == [1, 2, nil])
assert(introspect.vcall(f, 1, 2, 3, 4) == [1, 2, 3])
#- call with var args -#
assert(introspect.vcall(f, []) == [nil, nil, nil])
assert(introspect.vcall(f, [1]) == [1, nil, nil])
assert(introspect.vcall(f, [1, 2]) == [1, 2, nil])
assert(introspect.vcall(f, [1, 2, 3, 4]) == [1, 2, 3])
#- mixed args -#
assert(introspect.vcall(f, 1, []) == [1, nil, nil])
assert(introspect.vcall(f, 1, [2]) == [1, 2, nil])
assert(introspect.vcall(f, 1, [2, "foo", 4]) == [1, 2, "foo"])
#- non terminal list -#
assert(introspect.vcall(f, 1, [2, 3, 4], "foo") == [1, [2, 3, 4], "foo"])
#- -#
#- same tests with vararg function -#
#- -#
def g(a, *b)
if size(b) == 0 return [a, nil, nil]
elif size(b) == 1 return [a, b[0], nil]
elif size(b) > 1 return [a, b[0], b[1]]
end
end
#- simple calls with fixed args -#
assert(introspect.vcall(g) == [nil, nil, nil])
assert(introspect.vcall(g, 1) == [1, nil, nil])
assert(introspect.vcall(g, 1, 2) == [1, 2, nil])
assert(introspect.vcall(g, 1, 2, 3, 4) == [1, 2, 3])
#- call with var args -#
assert(introspect.vcall(g, []) == [nil, nil, nil])
assert(introspect.vcall(g, [1]) == [1, nil, nil])
assert(introspect.vcall(g, [1, 2]) == [1, 2, nil])
assert(introspect.vcall(g, [1, 2, 3, 4]) == [1, 2, 3])
#- mixed args -#
assert(introspect.vcall(g, 1, []) == [1, nil, nil])
assert(introspect.vcall(g, 1, [2]) == [1, 2, nil])
assert(introspect.vcall(g, 1, [2, "foo", 4]) == [1, 2, "foo"])
#- non terminal list -#
assert(introspect.vcall(g, 1, [2, 3, 4], "foo") == [1, [2, 3, 4], "foo"])
#- -#
#- test with vararg only -#
#- -#
def c(*a) return size(a) end
#- simple calls with fixed args -#
assert(introspect.vcall(c) == 0)
assert(introspect.vcall(c, 1) == 1)
assert(introspect.vcall(c, 1, 2) == 2)
assert(introspect.vcall(c, 1, 2, 3, 4) == 4)
#- call with var args -#
assert(introspect.vcall(c, []) == 0)
assert(introspect.vcall(c, [1]) == 1)
assert(introspect.vcall(c, [1, 2]) == 2)
assert(introspect.vcall(c, [1, 2, 3, 4]) == 4)
#- mixed args -#
assert(introspect.vcall(c, 1, []) == 1)
assert(introspect.vcall(c, 1, [2]) == 2)
assert(introspect.vcall(c, 1, [2, "foo", 4]) == 4)
#- non terminal list -#
assert(introspect.vcall(c, 1, [2, 3, 4], "foo") == 3)
#- -#
#- test with native function -#
#- -#
import string
assert(introspect.vcall(string.format, "a") == "a")
assert(introspect.vcall(string.format, "%i", 1) == "1")
assert(introspect.vcall(string.format, "%i - %i", 1, 2) == "1 - 2")
assert(introspect.vcall(string.format, "%i - %i", [1, 2]) == "1 - 2")
assert(introspect.vcall(string.format, "%i - %i", [1, 2, 3]) == "1 - 2")
assert(introspect.vcall(string.format, "%i - %i", 1, [1, 2, 3]) == "1 - 1")

View File

@ -0,0 +1,12 @@
#- check the gc bug fixed in #110 -#
#- Berry must be compiled with `#define BE_USE_DEBUG_GC 1` -#
#- for the initial bug to happen -#
code = "()" #- this code triggers a lexer exception -#
try
compile(code)
assert(false) #- this should never be reached -#
except .. as e, m
assert(m == "string:1: unexpected symbol near ')'")
end