mirror of https://github.com/arendst/Tasmota.git
Berry introspect.vcall
This commit is contained in:
parent
1b5406fdd9
commit
cb42e241e6
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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")
|
|
@ -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
|
Loading…
Reference in New Issue