changeset 432:167faab0c0be

first usable version of test suite for aggregates, handling only non-nested struct params, at the moment; still missing: - unions - arrays - aggregates as return values
author Tassilo Philipp
date Fri, 21 Jan 2022 15:42:29 +0100
parents 1cb8a65ea27f
children 45662241d9cd
files test/suite_aggrs/CMakeLists.txt test/suite_aggrs/Makefile.embedded test/suite_aggrs/Makefile.generic test/suite_aggrs/Nmakefile test/suite_aggrs/README.txt test/suite_aggrs/cases.c test/suite_aggrs/cases.h test/suite_aggrs/cases.txt test/suite_aggrs/config.lua test/suite_aggrs/design.txt test/suite_aggrs/globals.c test/suite_aggrs/globals.h test/suite_aggrs/main.c test/suite_aggrs/mk-cases.lua test/suite_aggrs/mkfile test/suite_aggrs/rand-sig.lua
diffstat 16 files changed, 966 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/CMakeLists.txt	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,4 @@
+add_executable(suite_aggrs globals.c cases.c main.c)
+target_link_libraries(suite_aggrs dyncall_s)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/Makefile.embedded	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,25 @@
+TARGET	= suite_aggrs ${OBJS} 
+OBJS	= globals.o cases.o main.o
+
+SRCDIR 	= ../..
+BLDDIR 	= ${SRCDIR}
+LUA	= lua
+
+CPPFLAGS	+= -I${SRCDIR}/dyncall
+LDFLAGS		+= -L${BLDDIR}/dyncall
+LDLIBS		+= -ldyncall_s
+
+.PHONY: all clean config config-random
+
+all: ${TARGET}
+suite_aggrs: ${OBJS}
+	${CC} ${OBJS} ${LDFLAGS} ${LDLIBS} -o $@
+config:
+	${LUA} mk-cases.lua <design.txt >cases.h
+config-random: 
+	${LUA} rand-sig.lua >cases.txt
+	${LUA} mk-cases.lua <cases.txt >cases.h
+cases.o: cases.h 
+clean:
+	rm -f ${TARGET}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/Makefile.generic	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,21 @@
+APP     = suite_aggrs
+OBJS    = globals.o cases.o main.o
+SRCTOP  = ${VPATH}/../..
+BLDTOP  = ../..
+CFLAGS += -I${SRCTOP}/dyncall
+LDLIBS += -L${BLDTOP}/dyncall -ldyncall_s 
+LUA     = lua
+.PHONY: all clean install config config-random
+all: ${APP}
+${APP}: ${OBJS} 
+	${CC} ${CFLAGS} ${LDFLAGS} ${OBJS} ${LDLIBS} -o ${APP} 
+clean:
+	rm -f ${APP} ${OBJS}
+install:
+	mkdir -p ${PREFIX}/test
+	cp ${APP} ${PREFIX}/test
+config:
+	${LUA} mk-cases.lua <design.txt >cases.h
+config-random: 
+	${LUA} rand-sig.lua >cases.txt
+	${LUA} mk-cases.lua <cases.txt >cases.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/Nmakefile	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,68 @@
+#//////////////////////////////////////////////////////////////////////////////
+#
+# Copyright (c) 2022 Tassilo Philipp <tphilipp@potion-studios.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#//////////////////////////////////////////////////////////////////////////////
+
+#///////////////////////////////////////////////////
+#
+#	nmake makefile
+#	Nmakefile
+#
+#///////////////////////////////////////////////////
+
+
+TOP = ..\..
+
+!INCLUDE $(TOP)\buildsys\nmake\prolog.nmake
+
+
+AUTOS = cases.h
+
+
+!IF "$(BUILD_OS)" == "windows"
+
+TARGETS = suite_aggrs.exe
+OBJS = main.obj cases.obj globals.obj
+
+$(TARGETS): $(OBJS)
+	echo Linking $@ ...
+	$(LD) /OUT:"$@" $(LDFLAGS) $(OBJS) $(TOP)\dyncall\dyncall_s.lib
+
+
+!ELSE IF "$(BUILD_OS)" == "nds"
+
+TARGETS = suite_aggrs.nds
+OBJS = main.o cases.o globals.o
+
+$(TARGETS): $(OBJS)
+	echo Linking $@ ...
+	$(LD) $(LDFLAGS) $(OBJS) $(DEVKITPRO_PATH)\libnds\lib\libnds9.a $(TOP)/dyncall/libdyncall_s.a -o "$(@B).elf"
+	$(OCP) -O binary "$(@B).elf" "$(@B).arm9"
+	ndstool -c "$@" -9 "$(@B).arm9"
+	del "$(@B).elf" "$(@B).arm9"
+
+!ENDIF
+
+
+
+$(OBJS): $(AUTOS)
+
+$(AUTOS): mk-cases.lua
+	lua mk-cases.lua <cases.txt >cases.h
+
+
+!INCLUDE $(TOP)\buildsys\nmake\epilog.nmake
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/README.txt	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,6 @@
+suite_aggrs for dyncall written in C and Lua.
+
+Tests aggregates (structs, unions and arrays) passed by value, along with other, non-aggregate args.
+
+@@@ unions and arrays missing
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/cases.c	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,54 @@
+/*
+
+ Package: dyncall
+ Library: test
+ File: test/call_aggrs/cases.c
+ Description: 
+ License:
+
+   Copyright (c) 2022 Tassilo Philipp <tphilipp@potion-studios.com>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#include "globals.h"
+
+#define ret_v(X) 
+#define ret_c(X) return(K_c[X]);
+#define ret_s(X) return(K_s[X]);
+#define ret_i(X) return(K_i[X]);
+#define ret_j(X) return(K_j[X]);
+#define ret_l(X) return(K_l[X]);
+#define ret_p(X) return(K_p[X]);
+#define ret_f(X) return(K_f[X]);
+#define ret_d(X) return(K_d[X]);
+#define ret_a(X) return(K_a[X]);
+
+#define v void
+#define c char
+#define s short
+#define i int
+#define j long
+#define l long long
+#define p void*
+#define f float
+#define d double
+#define a void*  /* all generated aggregates */
+
+#include "dyncall_struct.h"
+#include "cases.h"
+
+int G_ncases = sizeof(G_sigtab)/sizeof(G_sigtab[0]);
+int G_naggs  = sizeof(G_agg_sigs)/sizeof(G_agg_sigs[0]);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/cases.h	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,175 @@
+/* {ici} */
+struct A2{ i m0; c m1; i m2; };
+void f_cpA2(struct A2 *x, const struct A2 *y) { x->m0 = y->m0; x->m1 = y->m1; x->m2 = y->m2; };
+int f_cmpA2(const struct A2 *x, const struct A2 *y) { return x->m0 == y->m0 && x->m1 == y->m1 && x->m2 == y->m2; };
+DCstruct* f_newdcstA2() { DCstruct* st = dcNewStruct(3, sizeof(struct A2), 0, 1); dcStructField(st, 'i', offsetof(struct A2, m0), 1); dcStructField(st, 'c', offsetof(struct A2, m1), 1); dcStructField(st, 'i', offsetof(struct A2, m2), 1);  dcCloseStruct(st); return st; };
+/* {ii} */
+struct A1{ i m0; i m1; };
+void f_cpA1(struct A1 *x, const struct A1 *y) { x->m0 = y->m0; x->m1 = y->m1; };
+int f_cmpA1(const struct A1 *x, const struct A1 *y) { return x->m0 == y->m0 && x->m1 == y->m1; };
+DCstruct* f_newdcstA1() { DCstruct* st = dcNewStruct(2, sizeof(struct A1), 0, 1); dcStructField(st, 'i', offsetof(struct A1, m0), 1); dcStructField(st, 'i', offsetof(struct A1, m1), 1);  dcCloseStruct(st); return st; };
+/* {ps} */
+struct A5{ p m0; s m1; };
+void f_cpA5(struct A5 *x, const struct A5 *y) { x->m0 = y->m0; x->m1 = y->m1; };
+int f_cmpA5(const struct A5 *x, const struct A5 *y) { return x->m0 == y->m0 && x->m1 == y->m1; };
+DCstruct* f_newdcstA5() { DCstruct* st = dcNewStruct(2, sizeof(struct A5), 0, 1); dcStructField(st, 'p', offsetof(struct A5, m0), 1); dcStructField(st, 's', offsetof(struct A5, m1), 1);  dcCloseStruct(st); return st; };
+/* {iiii} */
+struct A9{ i m0; i m1; i m2; i m3; };
+void f_cpA9(struct A9 *x, const struct A9 *y) { x->m0 = y->m0; x->m1 = y->m1; x->m2 = y->m2; x->m3 = y->m3; };
+int f_cmpA9(const struct A9 *x, const struct A9 *y) { return x->m0 == y->m0 && x->m1 == y->m1 && x->m2 == y->m2 && x->m3 == y->m3; };
+DCstruct* f_newdcstA9() { DCstruct* st = dcNewStruct(4, sizeof(struct A9), 0, 1); dcStructField(st, 'i', offsetof(struct A9, m0), 1); dcStructField(st, 'i', offsetof(struct A9, m1), 1); dcStructField(st, 'i', offsetof(struct A9, m2), 1); dcStructField(st, 'i', offsetof(struct A9, m3), 1);  dcCloseStruct(st); return st; };
+/* {iii} */
+struct A8{ i m0; i m1; i m2; };
+void f_cpA8(struct A8 *x, const struct A8 *y) { x->m0 = y->m0; x->m1 = y->m1; x->m2 = y->m2; };
+int f_cmpA8(const struct A8 *x, const struct A8 *y) { return x->m0 == y->m0 && x->m1 == y->m1 && x->m2 == y->m2; };
+DCstruct* f_newdcstA8() { DCstruct* st = dcNewStruct(3, sizeof(struct A8), 0, 1); dcStructField(st, 'i', offsetof(struct A8, m0), 1); dcStructField(st, 'i', offsetof(struct A8, m1), 1); dcStructField(st, 'i', offsetof(struct A8, m2), 1);  dcCloseStruct(st); return st; };
+/* {li} */
+struct A4{ l m0; i m1; };
+void f_cpA4(struct A4 *x, const struct A4 *y) { x->m0 = y->m0; x->m1 = y->m1; };
+int f_cmpA4(const struct A4 *x, const struct A4 *y) { return x->m0 == y->m0 && x->m1 == y->m1; };
+DCstruct* f_newdcstA4() { DCstruct* st = dcNewStruct(2, sizeof(struct A4), 0, 1); dcStructField(st, 'l', offsetof(struct A4, m0), 1); dcStructField(st, 'i', offsetof(struct A4, m1), 1);  dcCloseStruct(st); return st; };
+/* {} */
+struct A6{ };
+void f_cpA6(struct A6 *x, const struct A6 *y) { 1; };
+int f_cmpA6(const struct A6 *x, const struct A6 *y) { return 1; };
+DCstruct* f_newdcstA6() { DCstruct* st = dcNewStruct(0, sizeof(struct A6), 0, 1);  dcCloseStruct(st); return st; };
+/* {i} */
+struct A7{ i m0; };
+void f_cpA7(struct A7 *x, const struct A7 *y) { x->m0 = y->m0; };
+int f_cmpA7(const struct A7 *x, const struct A7 *y) { return x->m0 == y->m0; };
+DCstruct* f_newdcstA7() { DCstruct* st = dcNewStruct(1, sizeof(struct A7), 0, 1); dcStructField(st, 'i', offsetof(struct A7, m0), 1);  dcCloseStruct(st); return st; };
+/* {il} */
+struct A3{ i m0; l m1; };
+void f_cpA3(struct A3 *x, const struct A3 *y) { x->m0 = y->m0; x->m1 = y->m1; };
+int f_cmpA3(const struct A3 *x, const struct A3 *y) { return x->m0 == y->m0 && x->m1 == y->m1; };
+DCstruct* f_newdcstA3() { DCstruct* st = dcNewStruct(2, sizeof(struct A3), 0, 1); dcStructField(st, 'i', offsetof(struct A3, m0), 1); dcStructField(st, 'l', offsetof(struct A3, m1), 1);  dcCloseStruct(st); return st; };
+/* 0:viiiii{ii} */ v f0(i a1,i a2,i a3,i a4,i a5,struct A1 a6){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA1(V_a[6],&a6);ret_v(6)}
+/* 1:viiiii{ici} */ v f1(i a1,i a2,i a3,i a4,i a5,struct A2 a6){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA2(V_a[6],&a6);ret_v(6)}
+/* 2:viiiii{il} */ v f2(i a1,i a2,i a3,i a4,i a5,struct A3 a6){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA3(V_a[6],&a6);ret_v(6)}
+/* 3:vfiiiii{il} */ v f3(f a1,i a2,i a3,i a4,i a5,i a6,struct A3 a7){V_f[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;V_i[6]=a6;f_cpA3(V_a[7],&a7);ret_v(7)}
+/* 4:viiiiif{il} */ v f4(i a1,i a2,i a3,i a4,i a5,f a6,struct A3 a7){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;V_f[6]=a6;f_cpA3(V_a[7],&a7);ret_v(7)}
+/* 5:viiiiif{li} */ v f5(i a1,i a2,i a3,i a4,i a5,f a6,struct A4 a7){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;V_f[6]=a6;f_cpA4(V_a[7],&a7);ret_v(7)}
+/* 6:viiiii{il}f */ v f6(i a1,i a2,i a3,i a4,i a5,struct A3 a6,f a7){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA3(V_a[6],&a6);V_f[7]=a7;ret_v(7)}
+/* 7:viiiii{li}f */ v f7(i a1,i a2,i a3,i a4,i a5,struct A4 a6,f a7){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA4(V_a[6],&a6);V_f[7]=a7;ret_v(7)}
+/* 8:viiffiii{ps} */ v f8(i a1,i a2,f a3,f a4,i a5,i a6,i a7,struct A5 a8){V_i[1]=a1;V_i[2]=a2;V_f[3]=a3;V_f[4]=a4;V_i[5]=a5;V_i[6]=a6;V_i[7]=a7;f_cpA5(V_a[8],&a8);ret_v(8)}
+/* 9:viiiiiff{li} */ v f9(i a1,i a2,i a3,i a4,i a5,f a6,f a7,struct A4 a8){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;V_f[6]=a6;V_f[7]=a7;f_cpA4(V_a[8],&a8);ret_v(8)}
+/* 10:viiiiiff{il} */ v f10(i a1,i a2,i a3,i a4,i a5,f a6,f a7,struct A3 a8){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;V_f[6]=a6;V_f[7]=a7;f_cpA3(V_a[8],&a8);ret_v(8)}
+/* 11:viiiii{il}ff */ v f11(i a1,i a2,i a3,i a4,i a5,struct A3 a6,f a7,f a8){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA3(V_a[6],&a6);V_f[7]=a7;V_f[8]=a8;ret_v(8)}
+/* 12:viiiii{li}ff */ v f12(i a1,i a2,i a3,i a4,i a5,struct A4 a6,f a7,f a8){V_i[1]=a1;V_i[2]=a2;V_i[3]=a3;V_i[4]=a4;V_i[5]=a5;f_cpA4(V_a[6],&a6);V_f[7]=a7;V_f[8]=a8;ret_v(8)}
+/* 13:i */ i f13(){ret_i(0)}
+/* 14:i{} */ i f14(struct A6 a1){f_cpA6(V_a[1],&a1);ret_i(1)}
+/* 15:i{i} */ i f15(struct A7 a1){f_cpA7(V_a[1],&a1);ret_i(1)}
+/* 16:i{ii} */ i f16(struct A1 a1){f_cpA1(V_a[1],&a1);ret_i(1)}
+/* 17:i{iii} */ i f17(struct A8 a1){f_cpA8(V_a[1],&a1);ret_i(1)}
+/* 18:i{iiii} */ i f18(struct A9 a1){f_cpA9(V_a[1],&a1);ret_i(1)}
+/* 19:f */ f f19(){ret_f(0)}
+/* 20:f{} */ f f20(struct A6 a1){f_cpA6(V_a[1],&a1);ret_f(1)}
+/* 21:f{i} */ f f21(struct A7 a1){f_cpA7(V_a[1],&a1);ret_f(1)}
+/* 22:f{ii} */ f f22(struct A1 a1){f_cpA1(V_a[1],&a1);ret_f(1)}
+/* 23:d{ii} */ d f23(struct A1 a1){f_cpA1(V_a[1],&a1);ret_d(1)}
+/* 24:f{iii} */ f f24(struct A8 a1){f_cpA8(V_a[1],&a1);ret_f(1)}
+/* 25:f{iiii} */ f f25(struct A9 a1){f_cpA9(V_a[1],&a1);ret_f(1)}
+/* 26:ss{ii} */ s f26(s a1,struct A1 a2){V_s[1]=a1;f_cpA1(V_a[2],&a2);ret_s(2)}
+funptr G_funtab[] = {
+	(funptr)&f0,
+	(funptr)&f1,
+	(funptr)&f2,
+	(funptr)&f3,
+	(funptr)&f4,
+	(funptr)&f5,
+	(funptr)&f6,
+	(funptr)&f7,
+	(funptr)&f8,
+	(funptr)&f9,
+	(funptr)&f10,
+	(funptr)&f11,
+	(funptr)&f12,
+	(funptr)&f13,
+	(funptr)&f14,
+	(funptr)&f15,
+	(funptr)&f16,
+	(funptr)&f17,
+	(funptr)&f18,
+	(funptr)&f19,
+	(funptr)&f20,
+	(funptr)&f21,
+	(funptr)&f22,
+	(funptr)&f23,
+	(funptr)&f24,
+	(funptr)&f25,
+	(funptr)&f26,
+};
+char const * G_sigtab[] = {
+	"viiiii{ii}",
+	"viiiii{ici}",
+	"viiiii{il}",
+	"vfiiiii{il}",
+	"viiiiif{il}",
+	"viiiiif{li}",
+	"viiiii{il}f",
+	"viiiii{li}f",
+	"viiffiii{ps}",
+	"viiiiiff{li}",
+	"viiiiiff{il}",
+	"viiiii{il}ff",
+	"viiiii{li}ff",
+	"i",
+	"i{}",
+	"i{i}",
+	"i{ii}",
+	"i{iii}",
+	"i{iiii}",
+	"f",
+	"f{}",
+	"f{i}",
+	"f{ii}",
+	"d{ii}",
+	"f{iii}",
+	"f{iiii}",
+	"ss{ii}",
+};
+const char* G_agg_sigs[]  = {
+	"{ici}",
+	"{ii}",
+	"{ps}",
+	"{iiii}",
+	"{iii}",
+	"{li}",
+	"{}",
+	"{i}",
+	"{il}"
+};
+int G_agg_sizes[] = {
+	sizeof(struct A2),
+	sizeof(struct A1),
+	sizeof(struct A5),
+	sizeof(struct A9),
+	sizeof(struct A8),
+	sizeof(struct A4),
+	sizeof(struct A6),
+	sizeof(struct A7),
+	sizeof(struct A3)
+};
+funptr G_agg_newdcstfuncs[] = {
+	(funptr)&f_newdcstA2,
+	(funptr)&f_newdcstA1,
+	(funptr)&f_newdcstA5,
+	(funptr)&f_newdcstA9,
+	(funptr)&f_newdcstA8,
+	(funptr)&f_newdcstA4,
+	(funptr)&f_newdcstA6,
+	(funptr)&f_newdcstA7,
+	(funptr)&f_newdcstA3
+};
+funptr G_agg_cmpfuncs[] = {
+	(funptr)&f_cmpA2,
+	(funptr)&f_cmpA1,
+	(funptr)&f_cmpA5,
+	(funptr)&f_cmpA9,
+	(funptr)&f_cmpA8,
+	(funptr)&f_cmpA4,
+	(funptr)&f_cmpA6,
+	(funptr)&f_cmpA7,
+	(funptr)&f_cmpA3
+};
+int G_maxargs = 8;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/cases.txt	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,13 @@
+viiiii{ii}
+viiiii{ici}
+viiiii{il}
+vfiiiii{il}
+viiiiif{il}
+viiiiif{li}
+viiiii{il}f
+viiiii{li}f
+viiffiii{ps}
+viiiiiff{li}
+viiiiiff{il}
+viiiii{il}ff
+viiiii{li}ff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/config.lua	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,18 @@
+-- user config for rand-sig.lua:
+minargs      = 0
+maxargs      = 32
+maxaggrdepth = 3    -- max nesting depth of aggregates, 1 = no nesting
+ncases       = 400
+types        = "csijlpfd{}"
+seed         = 2108
+
+-- specify types more than once to increase relative occurance, e.g.:
+
+-- this favors non-struct args, especially ints (and also increases avg num of struct fields):
+--types       = "ccssiiiiijjllpfd{}"
+
+-- this heavily favors nested structs:
+--types       = "csijlpfd{{{{{{}"
+
+-- this heavily favors flat and short/empty structs:
+--types       = "csijlpfd{}}}}}}"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/design.txt	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,27 @@
+viiiii{ii}
+viiiii{ici}
+viiiii{il}
+vfiiiii{il}
+viiiiif{il}
+viiiiif{li}
+viiiii{il}f
+viiiii{li}f
+viiffiii{ps}
+viiiiiff{li}
+viiiiiff{il}
+viiiii{il}ff
+viiiii{li}ff
+i
+i{}
+i{i}
+i{ii}
+i{iii}
+i{iiii}
+f
+f{}
+f{i}
+f{ii}
+d{ii}
+f{iii}
+f{iiii}
+ss{ii}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/globals.c	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,83 @@
+/*
+
+ Package: dyncall
+ Library: test
+ File: test/call_aggrs/globals.c
+ Description: 
+ License:
+
+   Copyright (c) 2022 Tassilo Philipp <tphilipp@potion-studios.com>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#include <stdlib.h>
+#include "globals.h"
+#include <float.h>
+
+#define X(CH,T) T *V_##CH; T *K_##CH; 
+DEF_TYPES
+#undef X
+
+static double rand_d()                    { return ( ( (double) rand() )  / ( (double) RAND_MAX ) ); }
+static void   rand_mem(void* p, size_t s) { for(int i=0; i<s; ++i) ((char*)p)[i] = (char)rand(); }
+
+static int calc_max_aggr_size()
+{
+  int i, s = 0;
+  for(i=0; i<G_naggs; ++i)
+    if(G_agg_sizes[i] > s)
+      s = G_agg_sizes[i];
+  return s;
+}
+
+void init_K()
+{
+  int i;
+  int maxaggrsize = calc_max_aggr_size();
+#define X(CH,T) V_##CH = (T*) malloc(sizeof(T)*(G_maxargs+1)); K_##CH = (T*) malloc(sizeof(T)*(G_maxargs+1));
+DEF_TYPES
+#undef X
+
+
+  for(i=0;i<G_maxargs+1;++i) {
+    K_c[i] = (char)      (((rand_d()-0.5)*2) * (1<<7));
+    K_s[i] = (short)     (((rand_d()-0.5)*2) * (1<<(sizeof(short)*8-1)));
+    K_i[i] = (int)       (((rand_d()-0.5)*2) * (1<<(sizeof(int)*8-2)));
+    K_j[i] = (long)      (((rand_d()-0.5)*2) * (1L<<(sizeof(long)*8-2)));
+    K_l[i] = (long long) (((rand_d()-0.5)*2) * (1LL<<(sizeof(long long)*8-2)));
+    K_p[i] = (void*)     (long) (((rand_d()-0.5)*2) * (1LL<<(sizeof(void*)*8-1)));
+    K_f[i] = (float)     (rand_d() * FLT_MAX);
+    K_d[i] = (double)    (((rand_d()-0.5)*2) * 1.7976931348623157E+308/*__DBL_MAX__*/);	/* Plan9 doesn't know the macro. */
+    K_a[i] = malloc(maxaggrsize); rand_mem(K_a[i], maxaggrsize);
+  }
+}
+
+void clear_V()
+{
+  static int aggr_init = 0;
+  int maxaggrsize = calc_max_aggr_size();
+
+  int i;
+  for(i=0;i<G_maxargs+1;++i) {
+    if(aggr_init)
+	  free(V_a[i]);
+#define X(CH,T) V_##CH[i] = (T) 0;
+DEF_TYPES
+#undef X
+    V_a[i] = malloc(maxaggrsize);
+  }
+  aggr_init = 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/globals.h	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,48 @@
+/*
+
+ Package: dyncall
+ Library: test
+ File: test/call_aggrs/globals.h
+ Description: 
+ License:
+
+   Copyright (c) 2022 Tassilo Philipp <tphilipp@potion-studios.com>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+/* the 'a'ggregate type points to memory with random data that is big enough to hold all different struct types */
+#define DEF_TYPES X(c,char) X(s,short) X(i,int) X(j,long) X(l,long long) X(p,void*) X(f,float) X(d,double) X(a,void*)
+
+#define X(CH,T) extern T *K_##CH; extern T *V_##CH;
+DEF_TYPES
+#undef X
+
+typedef void (*funptr)();
+
+extern funptr       G_funtab[];
+extern char const * G_sigtab[];
+extern int          G_ncases;
+extern int          G_maxargs;
+extern char const * G_agg_sigs[];
+extern int          G_agg_sizes[];
+extern funptr       G_agg_newdcstfuncs[];
+extern funptr       G_agg_cmpfuncs[];
+extern int          G_naggs;
+
+void init_K();
+void init_T();
+void clear_V();
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/main.c	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,179 @@
+/*
+
+ Package: dyncall
+ Library: test
+ File: test/call_aggrs/main.c
+ Description: 
+ License:
+
+   Copyright (c) 2022 Tassilo Philipp <tphilipp@potion-studios.com>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#include "dyncall.h"
+#include "dyncall_struct.h"
+#include "globals.h"
+#include <string.h>
+#include "../common/platformInit.h"
+#include "../common/platformInit.c" /* Impl. for functions only used in this translation unit */
+
+
+void* G_callvm;
+
+
+static int find_agg_idx(int* len, const char* sig)
+{
+  for(int i=0; i<G_naggs; ++i) {
+    const char* agg_sig = G_agg_sigs[i];
+    *len = strlen(agg_sig);
+    if(strncmp(agg_sig, sig, *len) == 0)
+      return i;
+  }
+  return -1;
+}
+
+int invoke(char const* signature, void* t)
+{
+  DCCallVM   * p = (DCCallVM*) G_callvm;
+  char const * sig = signature;
+  char         rtype;
+  char         atype;
+  int          pos = 0;
+  int          s = 0;
+
+  clear_V();
+  
+  rtype = *sig++;
+  dcReset(p);
+
+  while ( (atype = *sig) != '\0') {
+    pos++;
+    switch(atype) {
+      case 'c': dcArgChar    (p,K_c[pos]); break;
+      case 's': dcArgShort   (p,K_s[pos]); break;
+      case 'i': dcArgInt     (p,K_i[pos]); break;
+      case 'j': dcArgLong    (p,K_j[pos]); break;
+      case 'l': dcArgLongLong(p,K_l[pos]); break;
+      case 'p': dcArgPointer (p,K_p[pos]); break;
+      case 'f': dcArgFloat   (p,K_f[pos]); break;
+      case 'd': dcArgDouble  (p,K_d[pos]); break;
+      case '{': {
+	    /* find struct sig */
+		int len;
+		int i = find_agg_idx(&len, sig);
+        if(i == -1) {
+		  printf("unknown sig at '%s' ;", sig);
+		  return 0;
+		}
+		DCstruct *st = ((DCstruct*(*)())G_agg_newdcstfuncs[i])();
+		dcArgStruct(p, st, K_a[pos]);
+		sig += len-1; /* advance to next arg char */
+		break;
+      }
+      default: printf("unknown atype '%c' (1) ;", atype); return 0;
+    }
+	++sig;
+  }
+  
+  switch(rtype) 
+  {
+    case 'v': dcCallVoid(p,t); s=1; /*TODO:check that no return-arg was touched.*/ break;
+    case 'c': s = (dcCallChar    (p,t) == K_c[pos]) ; break;
+    case 's': s = (dcCallShort   (p,t) == K_s[pos]) ; break;
+    case 'i': s = (dcCallInt     (p,t) == K_i[pos]) ; break;
+    case 'j': s = (dcCallLong    (p,t) == K_j[pos]) ; break;
+    case 'l': s = (dcCallLongLong(p,t) == K_l[pos]) ; break;
+    case 'p': s = (dcCallPointer (p,t) == K_p[pos]) ; break;
+    case 'f': s = (dcCallFloat   (p,t) == K_f[pos]) ; break;
+    case 'd': s = (dcCallDouble  (p,t) == K_d[pos]) ; break;
+    //@@@ handle return types case '{': @@@ ; break;
+    default: printf("unknown rtype '%c'", rtype); return 0;
+  }
+
+  if (!s) { printf("rval wrong;"); return 0; }
+  /* test: */
+  sig = signature+1;
+  pos = 1;
+  while ( (atype = *sig) != '\0') {
+    switch(atype) {
+      case 'c': s = ( V_c[pos] == K_c[pos] ); if (!s) printf("'c':%d: %d != %d ; ", pos, V_c[pos], K_c[pos]); break;
+      case 's': s = ( V_s[pos] == K_s[pos] ); if (!s) printf("'s':%d: %d != %d ; ", pos, V_s[pos], K_s[pos]); break;
+      case 'i': s = ( V_i[pos] == K_i[pos] ); if (!s) printf("'i':%d: %d != %d ; ", pos, V_i[pos], K_i[pos]); break;
+      case 'j': s = ( V_j[pos] == K_j[pos] ); if (!s) printf("'j':%d: %ld != %ld ; ", pos, V_j[pos], K_j[pos]); break;
+      case 'l': s = ( V_l[pos] == K_l[pos] ); if (!s) printf("'l':%d: %lld != %lld ; ", pos, V_l[pos], K_l[pos]); break;
+      case 'p': s = ( V_p[pos] == K_p[pos] ); if (!s) printf("'p':%d: %lld != %lld ; ", pos, (long long) V_p[pos], (long long) K_p[pos]); break;
+      case 'f': s = ( V_f[pos] == K_f[pos] ); if (!s) printf("'f':%d: %f != %f ; ", pos, V_f[pos], K_f[pos]); break;
+      case 'd': s = ( V_d[pos] == K_d[pos] ); if (!s) printf("'d':%d: %f != %f ; ", pos, V_d[pos], K_d[pos]); break;
+      case '{': {
+	    /* no check: guaranteed to exist, or invoke func would've exited when passing args, above */
+		int len;
+		int i = find_agg_idx(&len, sig);
+		s = ((int(*)())G_agg_cmpfuncs[i])(V_a[pos], K_a[pos]);
+		if (!s) printf("'{':%d:  %lld != %lld ; ", pos, (long long) V_a[pos], (long long) K_a[pos]);
+		sig += len-1; /* advance to next arg char */
+		break;
+	  }
+      default: printf("unknown atype '%c' ; ", atype); return 0;
+    }
+    if (!s) {
+      printf("arg mismatch at %d ; ", pos);
+      return 0;
+    }
+	++sig;
+    ++pos;
+  }
+  return 1;
+}
+
+int run_test(int i)
+{  
+  char const * sig;
+  void * target;
+  int success;
+  sig = G_sigtab[i];
+  target = (void*) G_funtab[i];
+  printf("%d:%s:",i,sig);
+  success = invoke(sig,target);
+  printf("%d\n",success);
+  return success;
+}
+
+int run_all()
+{
+  int i;
+  int failure = 0;
+  for(i=0;i<G_ncases;++i)
+    failure |= !( run_test(i) );
+
+  return !failure;
+}
+
+int main(int argc, char* argv[])
+{
+  int total;
+
+  dcTest_initPlatform();
+
+  init_K(G_maxargs);
+  G_callvm = (DCCallVM*) dcNewCallVM(32768);
+  dcReset(G_callvm);
+  total = run_all();
+  printf("result: call_aggrs: %d\n", total);
+
+  dcTest_deInitPlatform();
+
+  return !total;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/mk-cases.lua	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,155 @@
+require"math"
+local max = math.max
+local maxargs = 0
+
+local n_aggrs = 0
+local seen_aggrs = { }
+
+
+function trim(l) return l:gsub("^%s+",""):gsub("%s+$","") end
+function mkcase(id,sig)
+  local sig = trim(sig)
+  -- @@@ return value hard-guessed by first char, doesn't hold anymore
+  local h = { "/* ",id,":",sig," */ ",sig:sub(1,1), " f", id,"(","" }
+  local t = { "" }
+  local pos = 1
+  local n_nest = 0
+  local aggr
+  local aggr_sig = ''
+  for i = 2, #sig do 
+    local name = "a"..pos
+    local ch   = sig:sub(i,i)
+
+    aggr_sig = aggr_sig..ch
+
+    -- aggregate nest level change?
+    if ch == '{' then
+      n_nest = n_nest + 1
+      aggr = { }
+      aggr_sig = ch  -- @@@ handle nesting
+    else
+      if ch == '}' then  -- @@@ handle nesting, here, by reusing structs
+        n_nest = n_nest - 1
+        -- aggr sig complete?
+        if n_nest == 0 then
+          -- register yet unseen aggregates, key is sig, val is body and name
+          if seen_aggrs[aggr_sig] == nil then
+            n_aggrs = n_aggrs + 1
+            ch = 'A'..n_aggrs
+            seen_aggrs[aggr_sig] = { aggr, ch }
+          end
+          ch = seen_aggrs[aggr_sig][2]
+        end
+      else
+        if n_nest > 0 then
+          aggr[#aggr+1] = ch
+          aggr[#aggr+1] = 'm'..(#aggr >> 1)
+        end
+      end
+    end
+
+    if n_nest == 0 then
+      -- struct types (more than one char) need copying via a func
+      if #ch > 1 then
+        h[#h+1] = 'struct '..ch.." "..name
+        t[#t+1] = 'f_cp'..ch..'(V_a['..pos.."],&"..name..");"
+      else
+        h[#h+1] = ch.." "..name
+        t[#t+1] = "V_"..ch.."["..pos.."]="..name..";"
+      end
+      h[#h+1] = ","
+
+      pos = pos + 1
+    end
+  end
+  maxargs = max(maxargs, pos-1)
+  h[#h] = "){"
+  t[#t+1] = "ret_"..sig:sub(1,1).."("..(pos-1)..")}\n"
+  return table.concat(h,"")..table.concat(t,"")
+end
+
+function mkfuntab(n)
+  local s = { "funptr G_funtab[] = {\n"}
+  for i = 0, n-1 do
+    s[#s+1] = "\t(funptr)&f"..i..",\n"
+  end
+  s[#s+1] = "};\n"
+  return table.concat(s,"")
+end
+
+function mksigtab(sigs)
+  local s = { "char const * G_sigtab[] = {\n"}
+  for k,v in pairs(sigs) do
+    s[#s+1] = '\t"'..v..'",\n'
+  end
+  s[#s+1] = "};\n"
+  return table.concat(s,"")
+end
+
+function mkall()
+  local lineno = 0
+  local sigtab = { }
+  local cases = ''
+  for line in io.lines() do
+    local sig = trim(line)
+    cases = cases..mkcase(lineno,sig)
+    sigtab[#sigtab+1] = sig
+    lineno = lineno + 1
+  end
+
+  agg_sizes = {}
+  agg_sigs  = {}
+  agg_names = {}
+  for k, v in pairs(seen_aggrs) do
+    st = 'struct '..v[2]
+
+    agg_sizes[#agg_sizes + 1] = 'sizeof('..st..')'
+    agg_sigs [#agg_sigs  + 1] = k
+    agg_names[#agg_names + 1] = v[2]
+
+    -- struct def
+    io.write('/* '..k..' */\n')
+    io.write(st..'{ ')
+    for i = 1, #v[1], 2 do
+      io.write(v[1][i]..' '..v[1][i+1]..'; ')
+    end
+    io.write("};\n")
+
+    -- struct cp and cmp funcs
+    s = {
+      'void f_cp'..v[2]..'('..st..' *x, const '..st..' *y) { ',
+      'int f_cmp'..v[2]..'(const '..st..' *x, const '..st..' *y) { return '
+    }
+    o = { '=', '==', '; ', ' && '  }
+    for t = 1, 2 do
+      io.write(s[t])
+      b = {}
+      for i = 1, #v[1], 2 do
+        b[#b+1] = 'x->'..v[1][i+1]..' '..o[t]..' y->'..v[1][i+1];
+      end
+      if #b == 0 then
+        b[1] = '1'  -- to handle empty structs
+      end
+      io.write(table.concat(b,o[t+2]).."; };\n")
+    end
+
+    -- convenient dcnewstruct helper funcs
+    io.write('DCstruct* f_newdcst'..v[2]..'() { DCstruct* st = dcNewStruct('..(#v[1]>>1)..', sizeof('..st..'), 0, 1); ')
+    for i = 1, #v[1], 2 do
+      io.write("dcStructField(st, '"..v[1][i].."', offsetof("..st..', '..v[1][i+1]..'), 1); ')
+    end
+    io.write(" dcCloseStruct(st); return st; };\n")
+  end
+
+  io.write(cases)
+  io.write(mkfuntab(lineno))
+  io.write(mksigtab(sigtab))
+  io.write('const char* G_agg_sigs[]  = {\n\t"'..table.concat(agg_sigs, '",\n\t"')..'"\n};\n')
+  io.write('int G_agg_sizes[] = {\n\t'..table.concat(agg_sizes, ',\n\t')..'\n};\n')
+  io.write('funptr G_agg_newdcstfuncs[] = {\n\t(funptr)&f_newdcst'..table.concat(agg_names, ',\n\t(funptr)&f_newdcst')..'\n};\n')
+  io.write('funptr G_agg_cmpfuncs[] = {\n\t(funptr)&f_cmp'..table.concat(agg_names, ',\n\t(funptr)&f_cmp')..'\n};\n')
+  io.write("int G_maxargs = "..maxargs..";\n")
+end
+
+mkall()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/mkfile	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,36 @@
+#//////////////////////////////////////////////////////////////////////////////
+#
+# Copyright (c) 2022 Tassilo Philipp <tphilipp@potion-studios.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#//////////////////////////////////////////////////////////////////////////////
+
+
+TOP		= ../..
+<$TOP/buildsys/mk/prolog.mk
+
+
+UNITS       = globals cases main
+APPLICATION = call_aggrs
+LIBS        = $TOP/dyncall/libdyncall_s.a$O
+
+#.PHONY: config 
+#config: mkconfig.lua rand-sig.lua mk-cases.lua
+#	echo Generating test cases...
+#	lua mk-config.lua >config.h
+#	lua rand-sig.lua >cases.txt
+#	lua mk-cases.lua <cases.txt >cases.h 
+
+
+<$TOP/buildsys/mk/epilog.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/suite_aggrs/rand-sig.lua	Fri Jan 21 15:42:29 2022 +0100
@@ -0,0 +1,54 @@
+require"config"
+
+-- assure aggr chars are present in pairs (can be weighted, though), to avoid
+-- inf loops
+if string.match(types,'{') and not string.match(types,'}') then types = types..'}' end
+
+rtypes   = "v"..types
+
+
+function mkstruct(n_nest)
+  local s = "{"
+
+  repeat
+    local id = math.random(#types)
+    local t = types:sub(id,id)
+    s = s..mktype(t, n_nest)
+  until t == '}'
+
+  return s
+end
+
+function mktype(t, n_nest)
+  -- ignore new structs if above depth limit
+  if t == '{' then
+    if n_nest < maxaggrdepth then
+      return mkstruct(n_nest + 1)
+    else
+      return ''
+    end
+  end
+
+  -- if '}', without any open, use empty struct
+  if n_nest == 0 and t == '}' then
+    return "{}"
+  end
+
+  return t
+end
+
+
+math.randomseed(seed)
+local id
+for i = 1, ncases do
+  id = math.random(#rtypes)
+  local nargs = math.random(minargs,maxargs)
+  local sig   = { mktype(rtypes:sub(id,id), 0) }
+  for j = 1, nargs do
+    id = math.random(#types)
+    sig[#sig+1] = mktype(types:sub(id,id), 0)
+  end
+  io.write(table.concat(sig))
+  io.write("\n")
+end
+