diff --git a/.gitignore b/.gitignore index 88e5e4b..e58eb04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,6 @@ # Generated by configure Makefile libwren*.pc -/src/basics.vapi -/src/marshal.vala -/src/trampoline.vala -/src/vm.vala -/src/wrennull.c /t/common.sh # Prerequisites diff --git a/.gitmodules b/.gitmodules index 8d0c0cb..efbb979 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "wren-pkg/wren"] + branch = wren-vala-2 path = wren-pkg/wren - url = https://github.com/wren-lang/wren.git + url = https://github.com/cxw42/wren.git diff --git a/Makefile.am b/Makefile.am index 434486a..c4bb66c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,12 +29,9 @@ distcheck-hook: phony += prettyprint prettyprint: -$(AM_V_GEN)uncrustify -l VALA -c $(top_srcdir)/.uncrustifyrc --replace \ - src/basics.vapi.in \ - src/marshal.vala.in \ - src/trampoline.vala.in \ - src/vm.vala.in \ - src/wrennull.c.in \ + $(vala_all_sources) \ t/000-sanity-t.vala \ + t/090-util-t.vala \ t/105-tramp-t.vala \ t/100-vmv-t.vala \ t/110-hello-world-s.vala \ @@ -43,11 +40,15 @@ prettyprint: t/140-roundtrip-data-t.vala \ t/150-marshal-t.vala \ t/200-expose-class-t.vala \ + t/210-marshal-class-t.vala \ + $(EOL) + -$(AM_V_GEN)uncrustify -c $(top_srcdir)/.uncrustifyrc --replace \ + $(c_all_sources) \ $(EOL) phony += p p: prettyprint - -ctags -R + -ctags -R --exclude='doc/*' --exclude='wren-vala-coverage/*' phony += cleanwren cleanwren: diff --git a/README.md b/README.md index 6bedd96..d19c8c4 100644 --- a/README.md +++ b/README.md @@ -26,30 +26,21 @@ After cloning: After doing one of the above "From" subsections: -If you do not already have Wren installed, run -`./configure --enable-wren-install && make -j && sudo make -j install` +``` +./configure && make -j && sudo make -j install +``` -After that, or if you do have Wren installed, run -`./configure && make -j && sudo make -j install`. +Wren is statically linked into wren-vala. -Note that `make` will only build EITHER Wren OR `wren-vala`. The default -is `wren-vala`. To build Wren itself, pass `--enable-wren-install` to -`./configure`. Yes, this is a bit odd, but it's the best I can think of -at the moment! :) - -## After building from a Git repo - -The `wren-pkg/wren` submodule may be dirty. To restore it to its clean state, -run `make cleanwren`. This will **remove** any files in `wren-pkg/wren` that -are not checked in, so use this command carefully! +Note: the version number of wren-vala matches the version of Wren in +the first three digits. # Repo contents - `src/`: source code for the Vala bindings - `t/`: tests for the Vala bindings - `doc/`: documentation for the Vala bindings -- `wren-pkg/`: Code to install wren to the system. Only tested on Linux, - as of present. +- `wren-pkg/`: Version of Wren that is statically linked into wren-vala - `wren-pkg/wren/`: [wren-lang/wren](https://github.com/wren-lang/wren), as a git submodule @@ -58,6 +49,8 @@ are not checked in, so use this command carefully! - Code coverage of the test suite: run `./coverage.sh`, then open `wren-vala-coverage/index.html` in a Web browser. Requires gcov(1) and lcov(1). + - Note: You can't run `make distcheck` if you're configured for + coverage. Just re-run `./configure` to go back to non-coverage mode. - Documentation: run `make html`, then open `doc/valadoc/index.html` in a Web browser. diff --git a/config.h.in b/config.h.in index 44b4382..eb11273 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,8 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Wren API version */ +#undef APIVER + /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H @@ -30,9 +33,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H -/* Define to 1 if you have the header file. */ -#undef HAVE_WREN_H - /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR diff --git a/configure.ac b/configure.ac index c30d9ac..7638805 100644 --- a/configure.ac +++ b/configure.ac @@ -53,67 +53,19 @@ AC_MSG_NOTICE([Wren version $WREN_VERSION]) PKG_INSTALLDIR AC_SUBST([APIVER], [$WREN_VERSION]) dnl Just to reduce typing :) +AC_DEFINE_UNQUOTED([APIVER],["$WREN_VERSION"], [Wren API version]) AC_SUBST([APIVERSH], [AS_TR_SH([$WREN_VERSION])]) -dnl === Installing Wren from wren-pkg/ ==================================== +dnl === Wren ============================================================== -AC_ARG_ENABLE([wren-install], - AS_HELP_STRING([--enable-wren-install], - [Build and install Wren itself INSTEAD OF wren-vala. You will need to re-run - configure without this option to install wren-vala.]) -) -AM_CONDITIONAL([WREN_INSTALL], [test "x$enable_wren_install" '=' 'xyes']) +AC_CHECK_FILE([$srcdir/wren-pkg/wren/README.md], [], + [AC_MSG_ERROR([You asked me to install Wren but the source tree does not appear to be full. Try 'git submodule update --init --recursive" if you have not yet done so.])]) -dnl Set up for the Wren compilation -whichtarget='wren-vala' -AM_COND_IF([WREN_INSTALL], - [ - dnl Sanity check - AC_CHECK_FILE([wren-pkg/wren/README.md], [], - [AC_MSG_ERROR([You asked me to install Wren but the source tree does not appear to be full. Try 'git submodule update --init --recursive" if you have not yet done so.])]) - - whichtarget='Wren itself (after installing, re-run ./configure to build wren-vala)' - ] -) - -dnl === Check for Wren ==================================================== -dnl Don't use pkg-config since Wren itself does not provide a .pc file. -dnl However, we could be using a hand-installed Wren or a Wren we installed -dnl using versioned names. - -wren_exists='yes' -AC_SUBST([WREN_HEADER]) -AC_CHECK_HEADERS([wren.h "wren-$APIVER.h"], - [ - WREN_HEADER="$ac_header" - break - ], -) -AS_IF([test "x$WREN_HEADER" '=' 'x'], - [ - dnl No wren.h found - AC_MSG_WARN([Could not find wren.h]) - wren_exists='' - ] -) +AC_SUBST([MERGED_HEADER], [wren-vala-$APIVER.h]) AC_SEARCH_LIBS([log2], [m], [], [ - AC_MSG_WARN([Could not find -lm]) - ] -) - -AC_SEARCH_LIBS([wrenNewVM], [wren "wren-$APIVER"], - [], [ - AC_MSG_WARN([Could not find -lwren]) - wren_exists='' - ], - [-lm] -) - -AS_IF([ test "x$wren_exists" '!=' 'xyes' && test "x$enable_wren_install" '!=' 'xyes' ], - [ - AC_MSG_ERROR([I could not find Wren on your system. Try "$0 --enable-wren-install" to build and install a Wren package.]) + AC_MSG_WARN([Could not find -lm, which Wren requires]) ] ) @@ -195,22 +147,8 @@ AC_CONFIG_FILES([ wren-pkg/Makefile ]) -dnl Source files: make them RO -AC_CONFIG_FILES([src/basics.vapi], [chmod a-w src/basics.vapi]) -AC_CONFIG_FILES([src/marshal.vala], [chmod a-w src/marshal.vala]) -AC_CONFIG_FILES([src/trampoline.vala], [chmod a-w src/trampoline.vala]) -AC_CONFIG_FILES([src/vm.vala], [chmod a-w src/vm.vala]) -AC_CONFIG_FILES([src/wrennull.c], [chmod a-w src/wrennull.c]) - dnl pkg-config files: name them after the Wren version -AC_CONFIG_FILES([wren-pkg/libwren-"${APIVER}".pc:wren-pkg/libwren.pc.in], [], [APIVER=$APIVER]) AC_CONFIG_FILES([src/libwren-vala-"${APIVER}".pc:src/libwren-vala.pc.in], [], [APIVER=$APIVER]) -dnl Tell the user what is going to happen -AC_CONFIG_COMMANDS([notify], - [echo "*** Ready to build $whichtarget ***"], - [whichtarget="$whichtarget"] -) - AM_SILENT_RULES([yes]) AC_OUTPUT diff --git a/doc/Makefile.am b/doc/Makefile.am index 523b01b..c6b8536 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -17,10 +17,7 @@ valadoc: $(AM_V_GEN)valadoc -o valadoc -b $(top_srcdir)/src --verbose --force \ --package-name='@PACKAGE_TARNAME@' \ --package-version='@PACKAGE_VERSION@' \ - $(top_srcdir)/src/basics.vapi \ - $(top_srcdir)/src/marshal.vala \ - $(top_srcdir)/src/trampoline.vala \ - $(top_srcdir)/src/vm.vala \ + $(vala_all_sources) \ --doclet=html \ $(MY_VALA_PKGS) \ $(EOL) diff --git a/rules.mk b/rules.mk index 7bc242f..4ea6b4a 100644 --- a/rules.mk +++ b/rules.mk @@ -17,6 +17,19 @@ vapidir = $(datadir)/vala/vapi # === Variables ========================================================= +vala_all_sources = \ + $(top_srcdir)/src/basics.vapi \ + $(top_srcdir)/src/marshal.vala \ + $(top_srcdir)/src/trampoline.vala \ + $(top_srcdir)/src/util.vala \ + $(top_srcdir)/src/vm.vala \ + $(EOL) + +c_all_sources = \ + $(top_srcdir)/src/myconfig.c \ + $(top_srcdir)/src/shim.c \ + $(EOL) + # Vala settings. # - LOCAL_VALA_FLAGS is filled in by each Makefile.am with any other valac # options that Makefile.am needs. @@ -35,7 +48,12 @@ MY_VALA_PKGS = \ # C settings, which are the same throughout. LOCAL_CFLAGS is filled in # by each Makefile.am. -AM_CFLAGS = $(LOCAL_CFLAGS) $(BASE_CFLAGS) $(CODE_COVERAGE_CFLAGS) +AM_CFLAGS = \ + -I$(top_srcdir)/wren-pkg -I$(top_builddir)/wren-pkg \ + $(LOCAL_CFLAGS) $(BASE_CFLAGS) \ + $(CODE_COVERAGE_CFLAGS) \ + $(EOL) + AM_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS) # Libs. $(LOCAL_LIBS) is added to $(LIBS) in configure.ac. diff --git a/src/.gitignore b/src/.gitignore index 9e0e637..a173ceb 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,3 +2,6 @@ *.c *.h lib*.vapi + +# Hand-made files +!wren-vala-merged.h diff --git a/src/Makefile.am b/src/Makefile.am index 508cd08..7849d5a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,50 +7,120 @@ EXTRA_DIST = .gitignore pkgconfig_DATA = lib_LTLIBRARIES = +noinst_LTLIBRARIES = dist_vapi_DATA = -include_HEADERS = +dist_include_HEADERS = +dist_noinst_HEADERS = +BUILT_SOURCES = + +DISTCLEANFILES = +MAINTAINERCLEANFILES = # === pkg-config === -if !WREN_INSTALL pkgconfig_DATA += \ libwren-vala-@APIVER@.pc \ $(EOL) -endif -DISTCLEANFILES = $(pkgconfig_DATA) +DISTCLEANFILES += $(pkgconfig_DATA) + +# === Library, pass 1 === +# The Vala files, as a convenience library. This is to get the ordering +# right with respect to shim.c. -# === Library === +noinst_LTLIBRARIES += libinternal.la -if !WREN_INSTALL -lib_LTLIBRARIES += libwren-vala.la -endif +wren_header = $(top_srcdir)/wren-pkg/wren/src/include/wren.h -libwren_vala_la_SOURCES = \ - basics.vapi \ - marshal.vala \ - myconfig.vapi \ - trampoline.vala \ - vm.vala \ - wrennull.c \ +libinternal_la_SOURCES = \ + $(vala_all_sources) \ + wren-vala-merged.h \ + $(wren_header) \ $(EOL) -libwren_vala_la_VALAFLAGS = \ - --library libwren-vala-@APIVER@.so -H libwren-vala-@APIVER@.h \ +# XXX not robust against Automake changes +libinternal_stamp = $(srcdir)/libinternal_la_vala.stamp + +# Make libinternal get built first. That way $(generated_header) will exist +# when it's time to build shim.c. +BUILT_SOURCES += $(libinternal_stamp) + +generated_header = $(srcdir)/wren-vala-generated.h +MAINTAINERCLEANFILES += $(generated_header) +dist_noinst_HEADERS += $(generated_header) + +# valac makes wren-vala-generated.h. At compile time, +# hand-made wren-vala-merged.h will pull in wren-vala-generated.h and wren.h. +libinternal_la_VALAFLAGS = \ + --library libinternal.so -H $(generated_header) \ --vapi libwren-vala.vapi \ $(AM_VALAFLAGS) \ $(EOL) -# === vapi and header === +libinternal_la_CFLAGS = \ + -I$(top_srcdir)/wren-pkg/wren/src/include \ + $(AM_CFLAGS) \ + $(EOL) + +# === Library, pass 2 === +# This is everything except the Vala files + +lib_LTLIBRARIES += libwren-vala-@APIVER@.la + +libwren_vala_@APIVER@_la_SOURCES = \ + $(c_all_sources) \ + $(EOL) + +libwren_vala_@APIVER@_la_CFLAGS = \ + -I$(top_srcdir)/wren-pkg/wren/src/include \ + $(AM_CFLAGS) \ + $(EOL) + +libwren_vala_@APIVER@_la_LIBADD = \ + libinternal.la \ + $(top_builddir)/wren-pkg/libwren-@APIVER@.la \ + $(EOL) + +# === Installed header === +# The client of this library uses a single header with everything in one. +# I make it by hand this way since I was having trouble getting Automake +# to cooperate. + +header_for_installation = wren-vala-@APIVER@.h +DISTCLEANFILES += $(header_for_installation) + +$(header_for_installation): $(generated_header) $(wren_header) Makefile.am + $(AM_V_GEN) + $(AM_V_at)echo "/* $@ generated at `date` */" > $@ + $(AM_V_at)perl -ne '$$shouldprint = 1../#include.+\bwren.*\.h/; print if $$shouldprint && $$shouldprint !~ /E0/' $(generated_header) >> $@ + $(AM_V_at)echo "/* inlining $(wren_header) */" >> $@ + $(AM_V_at)cat $(wren_header) >> $@ + $(AM_V_at)echo "/* end of inlined $(wren_header) */" >> $@ + $(AM_V_at)perl -ne 'print if $$shouldprint; $$shouldprint ||= /#include.+\bwren.*\.h/' $(generated_header) >> $@ + +# Inline Wren's header into the wren-vala header after we run valac +all-local: $(header_for_installation) + +install-data-local: $(header_for_installation) + $(MKDIR_P) "$(DESTDIR)$(includedir)" + $(INSTALL_DATA) "$(header_for_installation)" "$(DESTDIR)$(includedir)" + +# XXX not robust against automake changes +uninstall-local: + files="$(header_for_installation)" ; \ + dir="$(DESTDIR)$(includedir)"; \ + $(am__uninstall_files_from_dir) + +# === Installed vapi === # Before installing libwren-vala-@APIVER@.vapi, tack basics.vapi onto the end # of it. The compiler only writes the declarations from vm.vala into the vapi # file, but the client needs basics.vapi as well. -libwren-vala-@APIVER@.vapi: libwren-vala.vapi basics.vapi - cat $^ > $@ - -if !WREN_INSTALL -dist_vapi_DATA += libwren-vala-@APIVER@.vapi -DISTCLEANFILES += libwren-vala-@APIVER@.vapi -include_HEADERS += libwren-vala-@APIVER@.h -endif +# Also, change the cheader_filename in the vapi. +vapis_to_merge = $(srcdir)/libwren-vala.vapi basics.vapi +$(srcdir)/libwren-vala-@APIVER@.vapi: $(vapis_to_merge) Makefile.am $(libinternal_stamp) + rm -f "$@" + perl -pe 's/wren-vala-merged\.h/wren-vala-@APIVER@.h/g' $(vapis_to_merge) >> "$@" + +dist_vapi_DATA += $(srcdir)/libwren-vala-@APIVER@.vapi +MAINTAINERCLEANFILES += $(srcdir)/libwren-vala-@APIVER@.vapi diff --git a/src/basics.vapi.in b/src/basics.vapi similarity index 92% rename from src/basics.vapi.in rename to src/basics.vapi index 2e487d7..3803eee 100644 --- a/src/basics.vapi.in +++ b/src/basics.vapi @@ -1,5 +1,3 @@ -// @configure_input@ - // basics.vapi: Vala bindings for Wren. // Documentation is taken from wren.h (license below). This file is in the // same order as wren.h, as of writing. @@ -10,7 +8,7 @@ // // TODO: check the ownership on Handle instances. -[CCode(cheader_filename = "@WREN_HEADER@", lower_case_cprefix="wren")] +[CCode(cheader_filename = "wren-vala-merged.h", lower_case_cprefix="wren")] namespace Wren { [CCode(cname="WREN_VERSION_MAJOR")] @@ -33,10 +31,10 @@ namespace Wren { public delegate void *ReallocateFn(void *memory, size_t newSize); [CCode(has_target = false)] - public delegate void ForeignMethodFn(VM vm); + public delegate void ForeignMethodFn(VM vm, void *userData); [CCode(has_target = false)] - public delegate void FinalizerFn(void *data); + public delegate void FinalizerFn(void *data, void *userData); [CCode(has_target = false)] public delegate string ResolveModuleFn(VM vm, string importer, string name); @@ -55,8 +53,15 @@ namespace Wren { [CCode(has_target = false)] public delegate LoadModuleResult LoadModuleFn(VM vm, string name); + [SimpleType] + public struct BindForeignMethodResult + { + ForeignMethodFn executeFn; + void* userData; + } + [CCode(has_target = false)] - public delegate ForeignMethodFn BindForeignMethodFn(VM vm, string module, + public delegate BindForeignMethodResult BindForeignMethodFn(VM vm, string module, string className, bool isStatic, string signature); @@ -102,7 +107,9 @@ namespace Wren { public struct ForeignClassMethods { ForeignMethodFn allocate; + void *allocateUserData; FinalizerFn finalize; + void *finalizeUserData; } [CCode(has_target = false)] @@ -152,7 +159,7 @@ namespace Wren { * * This class includes all functions taking a WrenVM as the first parameter. */ - [CCode(cheader_filename = "@WREN_HEADER@", free_function = "wrenFreeVM", + [CCode(cheader_filename = "wren-vala-merged.h", free_function = "wrenFreeVM", has_type_id = false, cprefix="wren", lower_case_cprefix="wren")] [Compact] public class VM diff --git a/src/libwren-vala.pc.in b/src/libwren-vala.pc.in index 658a0c1..4c31fe0 100644 --- a/src/libwren-vala.pc.in +++ b/src/libwren-vala.pc.in @@ -10,10 +10,10 @@ Name: @PACKAGE_TARNAME@ Description: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ -Requires: libwren-@PACKAGE_VERSION@ @AX_PACKAGE_REQUIRES@ +Requires: @AX_PACKAGE_REQUIRES@ Requires.private: @AX_PACKAGE_REQUIRES_PRIVATE@ -Libs: -L${libdir} -l@PACKAGE_TARNAME@ +Libs: -L${libdir} -l@PACKAGE_TARNAME@-@APIVER@ Cflags: -I${includedir} # vi: set ft=config: # diff --git a/src/marshal.vala b/src/marshal.vala new file mode 100644 index 0000000..1e65dde --- /dev/null +++ b/src/marshal.vala @@ -0,0 +1,213 @@ +// marshal.vala: Wren<->Vala marshalling +// +// By Christopher White +// Copyright (c) 2021 Christopher White. All rights reserved. +// SPDX-License-Identifier: MIT + +[CCode(cheader_filename="wren-vala-merged.h")] +namespace Wren { + + /** + * Vala type representing the Wren null value. + * + * We use this because you can't create {@link GLib.Value}s of + * {@link GLib.Type.NONE}. + * + * Defined in shim.c. + */ + public extern GLib.Type get_null_type(); + + /** + * Convert GObject** to GObject* + * + * Defined in shim.c. + */ + private extern unowned Object obj_from_ppobj(void *ppobj); + + /** + * Initialize the Wren Vala bindings. + * + * Call this before calling any of the marshalling functions. + * {@link Wren.VMV}'s constructors call this for you. + * + * Idempotent. + */ + public void static_init() + { + if(static_init_done) { + return; + } + + // At the moment, this function is somewhat pointless, since the only + // thing in here is the idempotent initialization of the wren-null type. + // However, leaving the function here means that we won't have to change + // the API if we have to add more initialization steps in the future. + + get_null_type(); + static_init_done = true; + } + private bool static_init_done = false; + + [CCode(cheader_filename="wren-vala-merged.h")] + namespace Marshal { + + public errordomain Error { + /** Slot or other item not found */ + ENOENT, + /** Unknown type, or type I can't handle */ + ENOTSUP, + /** Forbidden by the language (can't be fixed by a wren-vala change) */ + EINVAL, + } + + // === Wren -> Vala ============================================= + + /** + * Get a GLib.Value from a single Wren slot. + * + * The mapping is: + * + * * Wren BOOL -> GLib.Type.BOOLEAN + * * Wren NUM -> double + * * Wren FOREIGN -> GLib.Object (this assumes you don't create any + * objects using something other than wren-vala) + * * Wren STRING -> string + * * Wren NULL -> Wren.get_null_type() + * + * @param vm The vm to read from + * @param slot The slot to grab. It must exist. + */ + public Value value_from_slot(Wren.VM vm, int slot) + throws Marshal.Error + { + var ty = vm.GetSlotType(slot); + + switch(ty) { + case BOOL: + return vm.GetSlotBool(slot); + case NUM: + return vm.GetSlotDouble(slot); + case FOREIGN: + var retval = Value(GLib.Type.OBJECT); + var obj = obj_from_ppobj(vm.GetSlotForeign(slot)); + retval.set_object(obj); // adds a ref + return retval; + case LIST: + // TODO + break; + case MAP: + // TODO + break; + case NULL: + return Value(get_null_type()); + case STRING: + return vm.GetSlotString(slot); + case UNKNOWN: + // TODO + break; + default: + break; + } + + throw new Marshal.Error.ENOTSUP( + "I don't know how to send type Wren %s to Vala".printf(ty.to_string())); + } // value_from_slot + + /** + * Wrap Wren slots in a GValue array. + * + * @param vm The vm to read from + * @param first_slot The first slot to grab (default 0) + * @param num_slots How many slots to grab (default -1 == the whole + * slot array) + * @return An array of freshly-created GValues. + */ + public Value[] values_from_slots(Wren.VM vm, int first_slot = 0, int num_slots = -1) + throws Marshal.Error + { + if(num_slots == -1) { + num_slots = vm.GetSlotCount(); + } + + if(first_slot + num_slots > vm.GetSlotCount()) { + throw new Marshal.Error.ENOENT( + "Slots up to %d requested, but only %d are available".printf( + first_slot + num_slots, vm.GetSlotCount())); + } + + Value[] retval = new Value[num_slots]; + for(int i=0; i Wren ============================================= + + /** + * Fill a Wren slot from a GValue. + * + * * Bool, number, and string are passed straight through. + * * Object is invalid --- there's no way to load an instance + * you didn't create in Wren. + * + * @param vm The VM + * @param slot Slot to set + * @param val New value + */ + public void slot_from_value(Wren.VM vm, int slot, Value val) + throws Marshal.Error + { + var vty = val.type(); + + if(vty == get_null_type()) { + // Outside the switch because it's not a compile-time constant + vm.SetSlotNull(slot); + return; + } + + switch(vty) { + case GLib.Type.BOOLEAN: + vm.SetSlotBool(slot, val.get_boolean()); + return; + case GLib.Type.DOUBLE: + case GLib.Type.FLOAT: + case GLib.Type.INT: + case GLib.Type.INT64: + case GLib.Type.LONG: + case GLib.Type.UINT: + case GLib.Type.UINT64: + case GLib.Type.ULONG: + var dblval = Value(GLib.Type.DOUBLE); + val.transform(ref dblval); + vm.SetSlotDouble(slot, dblval.get_double()); + return; + // FOREIGN: handled below + // case LIST: + // // TODO + // break; + // case MAP: + // // TODO + // break; + case GLib.Type.STRING: + vm.SetSlotString(slot, val.get_string()); + return; + // case UNKNOWN: + // // TODO + // break; + default: + if(vty.is_object()) { + throw new Marshal.Error.EINVAL("Cannot send Object instances to Wren"); + } + break; + } + + throw new Marshal.Error.ENOTSUP( + "I don't know how to send Vala type %s to Wren".printf(vty.to_string())); + } // set_slot() + + } // namespace Marshal + +} // namespace Wren diff --git a/src/marshal.vala.in b/src/marshal.vala.in deleted file mode 100644 index 773d0c4..0000000 --- a/src/marshal.vala.in +++ /dev/null @@ -1,127 +0,0 @@ -// @configure_input@ - -// marshal.vala: Wren<->Vala marshalling -// -// By Christopher White -// Copyright (c) 2021 Christopher White. All rights reserved. -// SPDX-License-Identifier: MIT - -[CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] -namespace Wren { - - /** - * Vala type representing the Wren null value. - * - * We use this because you can't create {@link GLib.Value}s of - * {@link GLib.Type.NONE}. - * - * In wrennull.c. - */ - public extern GLib.Type get_null_type(); - - /** - * Initialize the Wren Vala bindings. - * - * Call this before calling any of the marshalling functions. - * {@link Wren.VMV}'s constructors call this for you. - * - * Idempotent. - */ - public void static_init() - { - if(!static_init_done) { - return; - } - - // At the moment, this function is somewhat pointless, since the only - // thing in here is the idempotent initialization of the wren-null type. - // However, leaving the function here means that we won't have to change - // the API if we have to add more initialization steps in the future. - - get_null_type(); - static_init_done = true; - } - private bool static_init_done = false; - - [CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] - namespace Marshal { - - public errordomain MarshalError { - /** Slot or other item not found */ - ENOENT, - } - - /** - * Wrap a single Wren slot in a GValue. - * - * The mapping is: - * * Wren BOOL -> GLib.Type.BOOLEAN - * * Wren NULL -> GLib.Type.NONE - * - * @param vm The vm to read from - * @param slot The slot to grab. It must exist. - */ - public Value to_value_raw(Wren.VM vm, int slot) - { - var ty = vm.GetSlotType(slot); - - switch(ty) { - case BOOL: - return vm.GetSlotBool(slot); - case NUM: - return vm.GetSlotDouble(slot); - case FOREIGN: - // TODO - break; - case LIST: - // TODO - break; - case MAP: - // TODO - break; - case NULL: - return Value(get_null_type()); - case STRING: - return vm.GetSlotString(slot); - case UNKNOWN: - // TODO - break; - default: - assert_not_reached(); - } - - return Value(GLib.Type.INVALID); - } - - /** - * Wrap Wren slots in a GValue array. - * - * @param vm The vm to read from - * @param first_slot The first slot to grab (default 0) - * @param num_slots How many slots to grab (default -1 == the whole - * slot array) - * @return An array of freshly-created GValues. - */ - public Value[] to_values_raw(Wren.VM vm, int first_slot = 0, int num_slots = -1) - throws MarshalError - { - if(num_slots == -1) { - num_slots = vm.GetSlotCount(); - } - - if(first_slot + num_slots >= vm.GetSlotCount()) { - throw new MarshalError.ENOENT( - "Slots up to %d requested, but only %d are available".printf( - first_slot + num_slots, vm.GetSlotCount())); - } - - Value[] retval = new Value[num_slots]; - for(int i=0; i // Copyright (c) 2021 Christopher White. All Rights Reserved. @@ -8,8 +10,7 @@ #include #include -#include "libwren-vala-@APIVER@.h" -#include "@WREN_HEADER@" +#include "wren-vala-merged.h" G_BEGIN_DECLS @@ -18,15 +19,18 @@ G_BEGIN_DECLS static void value_nop(GValue *v) { } + static void value_nop2(const GValue *s, GValue *d) { } + static gchar *value_collect_nop(GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { return (gchar *)NULL; } + static gchar *value_collect_nop_const(const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) @@ -96,4 +100,19 @@ wrenget_null_type() return wrennull_type_id__volatile; } +/** + * wrenobj_from_ppobj: + * @ppobj: (transfer none): A GObject**, as a void* + * + * Does not touch the refcount of **ppobj. This exists because it was faster + * to write this function than to debug the compile-time errors I was getting + * when I tried to do the same in Vala! :) + * + * Return: (transfer none): A GObject* + */ +GObject *wrenobj_from_ppobj(void *ppobj) +{ + return *(GObject **)ppobj; +} + G_END_DECLS diff --git a/src/trampoline.vala b/src/trampoline.vala new file mode 100644 index 0000000..6129b34 --- /dev/null +++ b/src/trampoline.vala @@ -0,0 +1,330 @@ +// trampoline.vala: Functions for class bindings +// Part of wren-vala +// By Christopher White +// SPDX-License-Identifier: MIT + +[CCode(cheader_filename="wren-vala-merged.h")] +namespace Wren { + + /** + * Record describing a property + * + * This is used as the userData parameter for property-get and + * property-set calls. + */ + [Compact] + private class PropertyDescriptor + { + /** The name of the property */ + public string name; + /** The type of the property, cached for speed. */ + public GLib.Type type; + /** True if it's a setter --- for convenience */ + public bool is_setter; + + public PropertyDescriptor(string newname, GLib.Type newtype, bool new_is_setter) + { + name = newname; + type = newtype; + is_setter = new_is_setter; + } + } // class PropertyDescriptor + + /** + * Trampolines for allocating Object subclasses. + * + * Requires that the vm userdata be a {@link GLib.Object} subclass that: + * * has a tramp_ property returning an instance of this class; and + * * implements {@link Wren.HasMethods} + * */ + [CCode(cheader_filename="wren-vala-merged.h")] + public class Tramp : Object { + + /** + * Types we can instantiate + * + * Maps from {@link hash_key()} of the class to {@link GLib.Type}. + */ + HashTable types_ = + new HashTable(str_hash, str_equal); + + /** + * Functions that implement methods. + * + * Map from {@link hash_key()} of the method to static function. + */ + HashTable methodImpls = + new HashTable(str_hash, str_equal); + + /** + * Properties we know about. + * + * Map from {@link hash_key()} of the property accessor ({{{foo}}} or + * {{{foo=}}}) to descriptor. + */ + HashTable props_ = + new HashTable(str_hash, str_equal); + + // --- Public interface --------------------------------------- + + /** + * Make a hash key for a class or method. + * + * * You must not specify signature for a class. + * * You must specify signature for a method. + * + * @param module Name of the module + * @param className Name of the class + * @param signature The signature. For static methods, signature should + * begin with {{{static }}}. + */ + public static string hash_key(string module, string className, + string? signature = null) + { + if(signature == null) { + return @"$module!$className"; + } else { + + return @"$module!$className:$signature"; + } + } + + /** + * Return the allocator and finalizer functions for a type. + */ + public ForeignClassMethods get_class_methods(string module, string className) + { + ForeignClassMethods retval = {}; + + var key = hash_key(module, className); + if(types_.contains(key)) { + retval.allocate = allocate_instance; + retval.allocateUserData = (void *)types_[key]; + retval.finalize = deallocateObject; + retval.finalizeUserData = null; + debug("Functions for %s: %p, %p; userdata %p = %s", key, + &retval.allocate, &retval.finalize, retval.allocateUserData, + types_[key].name()); + } + return retval; + } // get_class_methods() + + /** + * Get a method of a class. + */ + public BindForeignMethodResult get_method(string module, string className, + bool isStatic, string signature) + { + BindForeignMethodResult retval = {}; + + var key = hash_key(module, className, make_sig(isStatic, signature)); + + // property gets/sets + if(props_.contains(key) && props_[key].is_setter) { + retval.executeFn = set_property_; + retval.userData = (void *)(props_[key]); + + } else if(props_.contains(key) && !props_[key].is_setter) { + retval.executeFn = get_property_; + retval.userData = (void *)(props_[key]); + + } else if (methodImpls.contains(key)) { + + // methods + retval.executeFn = call_method; + retval.userData = (void *)methodImpls[key]; + debug("Function for %s: %p, userdata %p", key, + &retval.executeFn, retval.userData); + + } else { + // TODO better error handling + error("No foreign-method implementation or foreign property found for key %s", key); + } + + return retval; + } // get_method() + + private static void foreach_method_of_type(GLib.Type type, + GLib.HFunc callback) + { + var instance = Object.new(type); + assert(instance != null); + var has_methods = instance as HasMethods; + if(has_methods != null) { + debug("Getting methods of %s", type.name()); + var methods = new HashTable(str_hash, str_equal); + has_methods.get_methods(ref methods); + methods.foreach(callback); + } + } // foreach_method_of_type() + + /** + * Add a type. + * + * Idempotent. + * @param module The module to load the class into + * @param className The Wren-visible name of the class + * @param type The GLib type of the class + * + * @return The Wren source for the class, or "" if the class was + * previously registered. + */ + public string add_type(string module, string className, GLib.Type type) + { + var type_key = hash_key(module, className); + + if(types_.contains(type_key)) { + return ""; + } + + types_[type_key] = type; + + debug("Class %s is %s", type_key, types_[type_key].name()); + + var wren_source = new StringBuilder(); + wren_source.append_printf("foreign class %s {\n", type.name()); + wren_source.append("construct new() {}\n"); + + debug("Loading methods"); + foreach_method_of_type(type, (sig, impl) => { + methodImpls[hash_key(module, className, sig)] = impl; + wren_source.append(foreign_decl_for(sig) + "\n"); + }); + + debug("Loading properties"); + ObjectClass ocl = (ObjectClass)type.class_ref(); + + foreach (ParamSpec spec in ocl.list_properties()) { + + // Getter + if((spec.flags & GLib.ParamFlags.READABLE) != 0) { + var propsig = spec.name; + var propkey = hash_key(module, className, propsig); + wren_source.append_printf("foreign %s\n", propsig); + props_[propkey] = new PropertyDescriptor(spec.name, spec.value_type, false); + } + + // Setter + if((spec.flags & GLib.ParamFlags.WRITABLE) != 0) { + var propsig = spec.name + "=(_)"; // setter + var propkey = hash_key(module, className, propsig); + wren_source.append_printf("foreign %s=(p0)\n", spec.name); + props_[propkey] = new PropertyDescriptor(spec.name, spec.value_type, true); + } + + } // foreach property + + wren_source.append("}\n"); + + return wren_source.str; + } // add_type() + + // --- Allocating and deallocating instances ------------------ + + /** + * Instantiate an instance of a foreign class + * + * @param vm The Wren VM to use + * @param index The index in {@link types_} to use. + */ + private static void allocate_instance(VM vm, void *userData) + { + var type = (GLib.Type)userData; + debug("instantiate: vm %p, userdata %p => type %s", + vm, userData, type.name()); + + // Do the work! + var instance = Object.new(type); + + // ref() to counteract the automatic unref() at the end of this function + instance.ref (); + + // Give the instance to Wren + unowned Object **ppobject = (Object **)vm.SetSlotNewForeign(0, 0, sizeof(Object)); + *ppobject = instance; + + debug("New %s is %p", type.name(), instance); + } + + /** Free a GObject instance */ + private static void deallocateObject(void *data, void *userData) + { + unowned Object **ppobject = (Object **)data; + debug("deallocateObject %p\n", ppobject); + if(ppobject != null) { + unowned Object *obj = *ppobject; + obj->unref(); + } + } + + // --- Interacting with instances ----------------------------- + + /** + * Call a method on a class instance + * + * This is a {@link Wren.ForeignMethodFn}. + * + * @param vm The Wren VM to use + * @param userData The {@link HasMethods.MethodImpl} that will handle + * this function call + */ + private static void call_method(VM vm, void *userData) + { + var fn = (HasMethods.MethodImpl)userData; + debug("call_method: vm %p, fn %p", vm, &fn); + + unowned Object **ppobject = (Object **)vm.GetSlotForeign(0); + var vmv = vm.GetUserData() as Wren.VMV; + fn(*ppobject, vmv); + } + + /** + * Get a property on a class instance + * + * This is a {@link Wren.ForeignMethodFn}. + * + * @param vm The Wren VM to use + * @param userData An unowned pointer to the {@link PropertyDescriptor} + * for this property. + */ + private static void get_property_(VM vm, void *userData) + { + unowned PropertyDescriptor prop = (PropertyDescriptor)userData; + debug("get property: vm %p, %s %s", vm, prop.type.name(), prop.name); + + unowned Object **ppobject = (Object **)vm.GetSlotForeign(0); + var val = Value(prop.type); + (*ppobject)->get_property(prop.name, ref val); + try { + Marshal.slot_from_value(vm, 0, val); + } catch(Marshal.Error e) { + error("marshalling error while getting property %s: %s", prop.name, e.message); + } + } + + /** + * Set a property on a class instance + * + * This is a {@link Wren.ForeignMethodFn}. + * + * @param vm The Wren VM to use + * @param userData An unowned pointer to the {@link PropertyDescriptor} + * for this property. + */ + private static void set_property_(VM vm, void *userData) + { + unowned PropertyDescriptor prop = (PropertyDescriptor)userData; + debug("set property: vm %p, %s %s", vm, prop.type.name(), prop.name); + + unowned Object **ppobject = (Object **)vm.GetSlotForeign(0); + Value val; + try { + val = Marshal.value_from_slot(vm, 1); + } catch(Marshal.Error e) { + error("marshalling error while setting property %s: %s", prop.name, e.message); + } + (*ppobject)->set_property(prop.name, val); + } + + } // class Tramp +} // namespace Wren diff --git a/src/trampoline.vala.in b/src/trampoline.vala.in deleted file mode 100644 index 2a34f88..0000000 --- a/src/trampoline.vala.in +++ /dev/null @@ -1,1110 +0,0 @@ -// @configure_input@ - -// trampoline.vala: Functions for class bindings -// Part of wren-vala -// By Christopher White -// SPDX-License-Identifier: MIT - -[CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] -namespace Wren { - - /** - * Trampolines for allocating Object subclasses. - * - * Requires that the vm userdata be a {@link GLib.Object} subclass that: - * * has a tramp_ property returning an instance of this class; and - * * implements {@link Wren.HasMethods} - * */ - [CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] - public class Tramp : Object { - - /** - * Classes exposed to Wren - * - * Maps from {@link hash_key()} retval to index in {@link types}. - */ - HashTable classes = new HashTable(str_hash, str_equal); - - /** - * Types we can instantiate - * - * Indices map 1-1 to {@link allocators}. - */ - public Array types = new Array(); - - - /** - * Functions that instantiate those types. - * - * Indices map 1-1 to {@link types}. - */ - public Array allocators; - - /** - * Methods exposed to Wren - * - * Maps from {@link hash_key()} retval to index in {@link methodImpls}. - */ - HashTable methodIdxes = new HashTable(str_hash, str_equal); - - /** - * Functions that implement methods - */ - public Array methodThunks; - - /** - * Functions that implement methods - */ - public Array methodImpls = new Array(); - - // --- Helper functions --------------------------------------- - - /** - * Get a Tramp instance from a VM. - * - * Requires the VM's userdata hold a GLib.Object instance with a - * property called `tramp_`. - */ - private static Tramp tramp_from_vm(VM vm) - { - var userdata = vm.GetUserData() as Object; - assert(userdata != null); // TODO better error handling - Value self_value = Value(typeof(Tramp)); - userdata.get_property("tramp_", ref self_value); - var self = self_value.get_object() as Tramp; - assert(self != null); - return self; - } - - // --- Public interface --------------------------------------- - - /** - * Make a hash key for a class or method. - * - * * You must specify neither isStatic nor signature for a class. - * * You must specify both of those for a method. - */ - public static string hash_key(string module, string className, - bool isStatic = false, string? signature = null) - { - if(signature == null) { - return @"$module!$className"; - } else { - - // $ = arbitrary character not valid in a Wren identifier - var staticflag = isStatic ? "$" : ""; - - return @"$module!$className.$staticflag$signature"; - } - } - - /** Regex for foreign_decl_for_key */ - private static Regex fdfk_re = null; - - /** - * Return a foreign-method declaration for a given hash key. - * - * This in effect reverses {@link hash_key}. - */ - public static string foreign_decl_for_key(string key) - { - - if(fdfk_re == null) { - try { - var ident = "(?:[A-Za-z_][A-Za-z_0-9]*)"; - fdfk_re = new Regex( - "(?%s)!(?%s).(?\\$?)(?%s)(?.*)$".printf( - ident, ident, ident)); - } catch(RegexError e) { - debug("Could not create regex for Tramp.foreign_decl_for_key(): %s", e.message); - assert(false); // I can't go on - } - } - - MatchInfo matches; - if(!fdfk_re.match(key, 0, out matches)) { - return "INVALID_FORMAT!"; // an invalid function declaration - } - - var declaration = new StringBuilder(); - declaration.append("foreign "); - var staticflag = matches.fetch_named("static"); - if((!)staticflag != "") { - declaration.append("static "); - } - - declaration.append((!)matches.fetch_named("name")); - - var rest = (!)matches.fetch_named("rest"); - if(rest.data[0] == '=') { // setter - declaration.append("="); - rest = rest.substring(1); - } - - // Parameters, if any: convert `_` to unique identifiers - int paramnum = -1; - if(rest.data[0] == '(') { - declaration.append("("); - var parms = rest.substring(1,rest.length-2).split(","); - for(int i=0; i < parms.length; ++i) { - if(i!=0) { - declaration.append(","); - } - declaration.append_printf("p%d", ++paramnum); - } - declaration.append(")"); - } - - return declaration.str; - } // foreign_decl_for_key - - /** - * Return the allocator and finalizer functions for a type. - */ - public ForeignClassMethods get_functions(string module, string className) - { - ForeignClassMethods retval = {}; - - var key = hash_key(module, className); - if(classes.contains(key)) { - uint idx = classes[key]; - retval.allocate = allocators.data[idx]; - retval.finalize = deallocateObject; - debug("Functions for %s @ %u: %p, %p", key, idx, &retval.allocate, &retval.finalize); - } - return retval; - } // get_functions() - - /** - * Get a method of a class. - */ - public ForeignMethodFn get_method(string module, string className, bool isStatic, - string signature) - { - ForeignMethodFn retval = null; - - var key = hash_key(module, className, isStatic, signature); - assert(methodIdxes.contains(key)); // TODO better error handling - - uint idx = methodIdxes[key]; - retval = methodThunks.data[idx]; - debug("Function for %s @ %u: %p", key, idx, &retval); - - return retval; - } // get_method() - - public static void foreach_method_of_type(GLib.Type type, GLib.HFunc callback) - { - var instance = Object.new(type); - assert(instance != null); - var has_methods = instance as HasMethods; - if(has_methods != null) { - debug("Getting methods of %s", type.to_string()); - var methods = new HashTable(str_hash, str_equal); - has_methods.get_methods(ref methods); - methods.foreach(callback); - } - } - - /** - * Add a type. - * - * Idempotent. - */ - public void add_type(string module, string className, GLib.Type type) - { - var key = hash_key(module, className); - - if(classes.contains(key)) { - return; - } - - types.append_val(type); - classes[key] = types.length - 1; - assert(types.length <= allocators.length); // TODO better error handling - debug("Class %s now at index %u", key, classes[key]); - - debug("Loading methods"); - foreach_method_of_type(type, (k,v) => { - methodImpls.append_val(v); - var idx = methodImpls.length - 1; - methodIdxes[k] = idx; - }); - } - - // --- Allocating and deallocating instances ------------------ - - /** - * Instantiate a foreign class - * - * @param vm The Wren VM to use - * @param index The index in {@link types} and {@link allocators} to use. - */ - private static void instantiate(VM vm, uint index) - { - debug("instantiate: vm %p, index %u", vm, index); - - var self = tramp_from_vm(vm); - assert(index < self.types.length); - - // Do the work! - var type = self.types.data[index]; - var instance = Object.new(type); - - // ref() to counteract the automatic unref() at the end of this function - instance.ref (); - - // Give the instance to Wren - unowned Object **ppobject = (Object **)vm.SetSlotNewForeign(0, 0, sizeof(Object)); - *ppobject = instance; - - debug("index %u: %p", index, instance); - } - - /** Free a GObject instance */ - private static void deallocateObject(void *data) - { - unowned Object **ppobject = (Object **)data; - debug("deallocateObject %p\n", ppobject); - if(ppobject != null) { - unowned Object *obj = *ppobject; - obj->unref(); - } - } - - // This is incredibly tedious. We hold 100 functions and pointers to them. - private static void i0(VM vm) { - instantiate(vm, 0); - } - private static void i1(VM vm) { - instantiate(vm, 1); - } - private static void i2(VM vm) { - instantiate(vm, 2); - } - private static void i3(VM vm) { - instantiate(vm, 3); - } - private static void i4(VM vm) { - instantiate(vm, 4); - } - private static void i5(VM vm) { - instantiate(vm, 5); - } - private static void i6(VM vm) { - instantiate(vm, 6); - } - private static void i7(VM vm) { - instantiate(vm, 7); - } - private static void i8(VM vm) { - instantiate(vm, 8); - } - private static void i9(VM vm) { - instantiate(vm, 9); - } - private static void i10(VM vm) { - instantiate(vm, 10); - } - private static void i11(VM vm) { - instantiate(vm, 11); - } - private static void i12(VM vm) { - instantiate(vm, 12); - } - private static void i13(VM vm) { - instantiate(vm, 13); - } - private static void i14(VM vm) { - instantiate(vm, 14); - } - private static void i15(VM vm) { - instantiate(vm, 15); - } - private static void i16(VM vm) { - instantiate(vm, 16); - } - private static void i17(VM vm) { - instantiate(vm, 17); - } - private static void i18(VM vm) { - instantiate(vm, 18); - } - private static void i19(VM vm) { - instantiate(vm, 19); - } - private static void i20(VM vm) { - instantiate(vm, 20); - } - private static void i21(VM vm) { - instantiate(vm, 21); - } - private static void i22(VM vm) { - instantiate(vm, 22); - } - private static void i23(VM vm) { - instantiate(vm, 23); - } - private static void i24(VM vm) { - instantiate(vm, 24); - } - private static void i25(VM vm) { - instantiate(vm, 25); - } - private static void i26(VM vm) { - instantiate(vm, 26); - } - private static void i27(VM vm) { - instantiate(vm, 27); - } - private static void i28(VM vm) { - instantiate(vm, 28); - } - private static void i29(VM vm) { - instantiate(vm, 29); - } - private static void i30(VM vm) { - instantiate(vm, 30); - } - private static void i31(VM vm) { - instantiate(vm, 31); - } - private static void i32(VM vm) { - instantiate(vm, 32); - } - private static void i33(VM vm) { - instantiate(vm, 33); - } - private static void i34(VM vm) { - instantiate(vm, 34); - } - private static void i35(VM vm) { - instantiate(vm, 35); - } - private static void i36(VM vm) { - instantiate(vm, 36); - } - private static void i37(VM vm) { - instantiate(vm, 37); - } - private static void i38(VM vm) { - instantiate(vm, 38); - } - private static void i39(VM vm) { - instantiate(vm, 39); - } - private static void i40(VM vm) { - instantiate(vm, 40); - } - private static void i41(VM vm) { - instantiate(vm, 41); - } - private static void i42(VM vm) { - instantiate(vm, 42); - } - private static void i43(VM vm) { - instantiate(vm, 43); - } - private static void i44(VM vm) { - instantiate(vm, 44); - } - private static void i45(VM vm) { - instantiate(vm, 45); - } - private static void i46(VM vm) { - instantiate(vm, 46); - } - private static void i47(VM vm) { - instantiate(vm, 47); - } - private static void i48(VM vm) { - instantiate(vm, 48); - } - private static void i49(VM vm) { - instantiate(vm, 49); - } - private static void i50(VM vm) { - instantiate(vm, 50); - } - private static void i51(VM vm) { - instantiate(vm, 51); - } - private static void i52(VM vm) { - instantiate(vm, 52); - } - private static void i53(VM vm) { - instantiate(vm, 53); - } - private static void i54(VM vm) { - instantiate(vm, 54); - } - private static void i55(VM vm) { - instantiate(vm, 55); - } - private static void i56(VM vm) { - instantiate(vm, 56); - } - private static void i57(VM vm) { - instantiate(vm, 57); - } - private static void i58(VM vm) { - instantiate(vm, 58); - } - private static void i59(VM vm) { - instantiate(vm, 59); - } - private static void i60(VM vm) { - instantiate(vm, 60); - } - private static void i61(VM vm) { - instantiate(vm, 61); - } - private static void i62(VM vm) { - instantiate(vm, 62); - } - private static void i63(VM vm) { - instantiate(vm, 63); - } - private static void i64(VM vm) { - instantiate(vm, 64); - } - private static void i65(VM vm) { - instantiate(vm, 65); - } - private static void i66(VM vm) { - instantiate(vm, 66); - } - private static void i67(VM vm) { - instantiate(vm, 67); - } - private static void i68(VM vm) { - instantiate(vm, 68); - } - private static void i69(VM vm) { - instantiate(vm, 69); - } - private static void i70(VM vm) { - instantiate(vm, 70); - } - private static void i71(VM vm) { - instantiate(vm, 71); - } - private static void i72(VM vm) { - instantiate(vm, 72); - } - private static void i73(VM vm) { - instantiate(vm, 73); - } - private static void i74(VM vm) { - instantiate(vm, 74); - } - private static void i75(VM vm) { - instantiate(vm, 75); - } - private static void i76(VM vm) { - instantiate(vm, 76); - } - private static void i77(VM vm) { - instantiate(vm, 77); - } - private static void i78(VM vm) { - instantiate(vm, 78); - } - private static void i79(VM vm) { - instantiate(vm, 79); - } - private static void i80(VM vm) { - instantiate(vm, 80); - } - private static void i81(VM vm) { - instantiate(vm, 81); - } - private static void i82(VM vm) { - instantiate(vm, 82); - } - private static void i83(VM vm) { - instantiate(vm, 83); - } - private static void i84(VM vm) { - instantiate(vm, 84); - } - private static void i85(VM vm) { - instantiate(vm, 85); - } - private static void i86(VM vm) { - instantiate(vm, 86); - } - private static void i87(VM vm) { - instantiate(vm, 87); - } - private static void i88(VM vm) { - instantiate(vm, 88); - } - private static void i89(VM vm) { - instantiate(vm, 89); - } - private static void i90(VM vm) { - instantiate(vm, 90); - } - private static void i91(VM vm) { - instantiate(vm, 91); - } - private static void i92(VM vm) { - instantiate(vm, 92); - } - private static void i93(VM vm) { - instantiate(vm, 93); - } - private static void i94(VM vm) { - instantiate(vm, 94); - } - private static void i95(VM vm) { - instantiate(vm, 95); - } - private static void i96(VM vm) { - instantiate(vm, 96); - } - private static void i97(VM vm) { - instantiate(vm, 97); - } - private static void i98(VM vm) { - instantiate(vm, 98); - } - private static void i99(VM vm) { - instantiate(vm, 99); - } - - // --- Calling methods ---------------------------------------- - - /** - * Call a method on a class instance - * - * @param vm The Wren VM to use - * @param index The index in {@link methodImpls} to use. - */ - private static void call_method(VM vm, uint index) - { - debug("call_method: vm %p, index %u", vm, index); - - var self = tramp_from_vm(vm); - assert(index < self.methodImpls.length); - - unowned Object **ppobject = (Object **)vm.GetSlotForeign(0); - var vmv = vm.GetUserData() as Wren.VMV; - self.methodImpls.data[index](*ppobject, vmv); - } - - // Also tedious. 100 of these. `cN` = call N - private static void c0(VM vm) { - call_method(vm, 0); - } - private static void c1(VM vm) { - call_method(vm, 1); - } - private static void c2(VM vm) { - call_method(vm, 2); - } - private static void c3(VM vm) { - call_method(vm, 3); - } - private static void c4(VM vm) { - call_method(vm, 4); - } - private static void c5(VM vm) { - call_method(vm, 5); - } - private static void c6(VM vm) { - call_method(vm, 6); - } - private static void c7(VM vm) { - call_method(vm, 7); - } - private static void c8(VM vm) { - call_method(vm, 8); - } - private static void c9(VM vm) { - call_method(vm, 9); - } - private static void c10(VM vm) { - call_method(vm, 10); - } - private static void c11(VM vm) { - call_method(vm, 11); - } - private static void c12(VM vm) { - call_method(vm, 12); - } - private static void c13(VM vm) { - call_method(vm, 13); - } - private static void c14(VM vm) { - call_method(vm, 14); - } - private static void c15(VM vm) { - call_method(vm, 15); - } - private static void c16(VM vm) { - call_method(vm, 16); - } - private static void c17(VM vm) { - call_method(vm, 17); - } - private static void c18(VM vm) { - call_method(vm, 18); - } - private static void c19(VM vm) { - call_method(vm, 19); - } - private static void c20(VM vm) { - call_method(vm, 20); - } - private static void c21(VM vm) { - call_method(vm, 21); - } - private static void c22(VM vm) { - call_method(vm, 22); - } - private static void c23(VM vm) { - call_method(vm, 23); - } - private static void c24(VM vm) { - call_method(vm, 24); - } - private static void c25(VM vm) { - call_method(vm, 25); - } - private static void c26(VM vm) { - call_method(vm, 26); - } - private static void c27(VM vm) { - call_method(vm, 27); - } - private static void c28(VM vm) { - call_method(vm, 28); - } - private static void c29(VM vm) { - call_method(vm, 29); - } - private static void c30(VM vm) { - call_method(vm, 30); - } - private static void c31(VM vm) { - call_method(vm, 31); - } - private static void c32(VM vm) { - call_method(vm, 32); - } - private static void c33(VM vm) { - call_method(vm, 33); - } - private static void c34(VM vm) { - call_method(vm, 34); - } - private static void c35(VM vm) { - call_method(vm, 35); - } - private static void c36(VM vm) { - call_method(vm, 36); - } - private static void c37(VM vm) { - call_method(vm, 37); - } - private static void c38(VM vm) { - call_method(vm, 38); - } - private static void c39(VM vm) { - call_method(vm, 39); - } - private static void c40(VM vm) { - call_method(vm, 40); - } - private static void c41(VM vm) { - call_method(vm, 41); - } - private static void c42(VM vm) { - call_method(vm, 42); - } - private static void c43(VM vm) { - call_method(vm, 43); - } - private static void c44(VM vm) { - call_method(vm, 44); - } - private static void c45(VM vm) { - call_method(vm, 45); - } - private static void c46(VM vm) { - call_method(vm, 46); - } - private static void c47(VM vm) { - call_method(vm, 47); - } - private static void c48(VM vm) { - call_method(vm, 48); - } - private static void c49(VM vm) { - call_method(vm, 49); - } - private static void c50(VM vm) { - call_method(vm, 50); - } - private static void c51(VM vm) { - call_method(vm, 51); - } - private static void c52(VM vm) { - call_method(vm, 52); - } - private static void c53(VM vm) { - call_method(vm, 53); - } - private static void c54(VM vm) { - call_method(vm, 54); - } - private static void c55(VM vm) { - call_method(vm, 55); - } - private static void c56(VM vm) { - call_method(vm, 56); - } - private static void c57(VM vm) { - call_method(vm, 57); - } - private static void c58(VM vm) { - call_method(vm, 58); - } - private static void c59(VM vm) { - call_method(vm, 59); - } - private static void c60(VM vm) { - call_method(vm, 60); - } - private static void c61(VM vm) { - call_method(vm, 61); - } - private static void c62(VM vm) { - call_method(vm, 62); - } - private static void c63(VM vm) { - call_method(vm, 63); - } - private static void c64(VM vm) { - call_method(vm, 64); - } - private static void c65(VM vm) { - call_method(vm, 65); - } - private static void c66(VM vm) { - call_method(vm, 66); - } - private static void c67(VM vm) { - call_method(vm, 67); - } - private static void c68(VM vm) { - call_method(vm, 68); - } - private static void c69(VM vm) { - call_method(vm, 69); - } - private static void c70(VM vm) { - call_method(vm, 70); - } - private static void c71(VM vm) { - call_method(vm, 71); - } - private static void c72(VM vm) { - call_method(vm, 72); - } - private static void c73(VM vm) { - call_method(vm, 73); - } - private static void c74(VM vm) { - call_method(vm, 74); - } - private static void c75(VM vm) { - call_method(vm, 75); - } - private static void c76(VM vm) { - call_method(vm, 76); - } - private static void c77(VM vm) { - call_method(vm, 77); - } - private static void c78(VM vm) { - call_method(vm, 78); - } - private static void c79(VM vm) { - call_method(vm, 79); - } - private static void c80(VM vm) { - call_method(vm, 80); - } - private static void c81(VM vm) { - call_method(vm, 81); - } - private static void c82(VM vm) { - call_method(vm, 82); - } - private static void c83(VM vm) { - call_method(vm, 83); - } - private static void c84(VM vm) { - call_method(vm, 84); - } - private static void c85(VM vm) { - call_method(vm, 85); - } - private static void c86(VM vm) { - call_method(vm, 86); - } - private static void c87(VM vm) { - call_method(vm, 87); - } - private static void c88(VM vm) { - call_method(vm, 88); - } - private static void c89(VM vm) { - call_method(vm, 89); - } - private static void c90(VM vm) { - call_method(vm, 90); - } - private static void c91(VM vm) { - call_method(vm, 91); - } - private static void c92(VM vm) { - call_method(vm, 92); - } - private static void c93(VM vm) { - call_method(vm, 93); - } - private static void c94(VM vm) { - call_method(vm, 94); - } - private static void c95(VM vm) { - call_method(vm, 95); - } - private static void c96(VM vm) { - call_method(vm, 96); - } - private static void c97(VM vm) { - call_method(vm, 97); - } - private static void c98(VM vm) { - call_method(vm, 98); - } - private static void c99(VM vm) { - call_method(vm, 99); - } - - // --- Constructor -------------------------------------------- - - public Tramp() { - - allocators = new Array(); - allocators.set_size(100); - allocators.data[0] = i0; - allocators.data[1] = i1; - allocators.data[2] = i2; - allocators.data[3] = i3; - allocators.data[4] = i4; - allocators.data[5] = i5; - allocators.data[6] = i6; - allocators.data[7] = i7; - allocators.data[8] = i8; - allocators.data[9] = i9; - allocators.data[10] = i10; - allocators.data[11] = i11; - allocators.data[12] = i12; - allocators.data[13] = i13; - allocators.data[14] = i14; - allocators.data[15] = i15; - allocators.data[16] = i16; - allocators.data[17] = i17; - allocators.data[18] = i18; - allocators.data[19] = i19; - allocators.data[20] = i20; - allocators.data[21] = i21; - allocators.data[22] = i22; - allocators.data[23] = i23; - allocators.data[24] = i24; - allocators.data[25] = i25; - allocators.data[26] = i26; - allocators.data[27] = i27; - allocators.data[28] = i28; - allocators.data[29] = i29; - allocators.data[30] = i30; - allocators.data[31] = i31; - allocators.data[32] = i32; - allocators.data[33] = i33; - allocators.data[34] = i34; - allocators.data[35] = i35; - allocators.data[36] = i36; - allocators.data[37] = i37; - allocators.data[38] = i38; - allocators.data[39] = i39; - allocators.data[40] = i40; - allocators.data[41] = i41; - allocators.data[42] = i42; - allocators.data[43] = i43; - allocators.data[44] = i44; - allocators.data[45] = i45; - allocators.data[46] = i46; - allocators.data[47] = i47; - allocators.data[48] = i48; - allocators.data[49] = i49; - allocators.data[50] = i50; - allocators.data[51] = i51; - allocators.data[52] = i52; - allocators.data[53] = i53; - allocators.data[54] = i54; - allocators.data[55] = i55; - allocators.data[56] = i56; - allocators.data[57] = i57; - allocators.data[58] = i58; - allocators.data[59] = i59; - allocators.data[60] = i60; - allocators.data[61] = i61; - allocators.data[62] = i62; - allocators.data[63] = i63; - allocators.data[64] = i64; - allocators.data[65] = i65; - allocators.data[66] = i66; - allocators.data[67] = i67; - allocators.data[68] = i68; - allocators.data[69] = i69; - allocators.data[70] = i70; - allocators.data[71] = i71; - allocators.data[72] = i72; - allocators.data[73] = i73; - allocators.data[74] = i74; - allocators.data[75] = i75; - allocators.data[76] = i76; - allocators.data[77] = i77; - allocators.data[78] = i78; - allocators.data[79] = i79; - allocators.data[80] = i80; - allocators.data[81] = i81; - allocators.data[82] = i82; - allocators.data[83] = i83; - allocators.data[84] = i84; - allocators.data[85] = i85; - allocators.data[86] = i86; - allocators.data[87] = i87; - allocators.data[88] = i88; - allocators.data[89] = i89; - allocators.data[90] = i90; - allocators.data[91] = i91; - allocators.data[92] = i92; - allocators.data[93] = i93; - allocators.data[94] = i94; - allocators.data[95] = i95; - allocators.data[96] = i96; - allocators.data[97] = i97; - allocators.data[98] = i98; - allocators.data[99] = i99; - - methodThunks = new Array(); - methodThunks.set_size(100); - methodThunks.data[0] = c0; - methodThunks.data[1] = c1; - methodThunks.data[2] = c2; - methodThunks.data[3] = c3; - methodThunks.data[4] = c4; - methodThunks.data[5] = c5; - methodThunks.data[6] = c6; - methodThunks.data[7] = c7; - methodThunks.data[8] = c8; - methodThunks.data[9] = c9; - methodThunks.data[10] = c10; - methodThunks.data[11] = c11; - methodThunks.data[12] = c12; - methodThunks.data[13] = c13; - methodThunks.data[14] = c14; - methodThunks.data[15] = c15; - methodThunks.data[16] = c16; - methodThunks.data[17] = c17; - methodThunks.data[18] = c18; - methodThunks.data[19] = c19; - methodThunks.data[20] = c20; - methodThunks.data[21] = c21; - methodThunks.data[22] = c22; - methodThunks.data[23] = c23; - methodThunks.data[24] = c24; - methodThunks.data[25] = c25; - methodThunks.data[26] = c26; - methodThunks.data[27] = c27; - methodThunks.data[28] = c28; - methodThunks.data[29] = c29; - methodThunks.data[30] = c30; - methodThunks.data[31] = c31; - methodThunks.data[32] = c32; - methodThunks.data[33] = c33; - methodThunks.data[34] = c34; - methodThunks.data[35] = c35; - methodThunks.data[36] = c36; - methodThunks.data[37] = c37; - methodThunks.data[38] = c38; - methodThunks.data[39] = c39; - methodThunks.data[40] = c40; - methodThunks.data[41] = c41; - methodThunks.data[42] = c42; - methodThunks.data[43] = c43; - methodThunks.data[44] = c44; - methodThunks.data[45] = c45; - methodThunks.data[46] = c46; - methodThunks.data[47] = c47; - methodThunks.data[48] = c48; - methodThunks.data[49] = c49; - methodThunks.data[50] = c50; - methodThunks.data[51] = c51; - methodThunks.data[52] = c52; - methodThunks.data[53] = c53; - methodThunks.data[54] = c54; - methodThunks.data[55] = c55; - methodThunks.data[56] = c56; - methodThunks.data[57] = c57; - methodThunks.data[58] = c58; - methodThunks.data[59] = c59; - methodThunks.data[60] = c60; - methodThunks.data[61] = c61; - methodThunks.data[62] = c62; - methodThunks.data[63] = c63; - methodThunks.data[64] = c64; - methodThunks.data[65] = c65; - methodThunks.data[66] = c66; - methodThunks.data[67] = c67; - methodThunks.data[68] = c68; - methodThunks.data[69] = c69; - methodThunks.data[70] = c70; - methodThunks.data[71] = c71; - methodThunks.data[72] = c72; - methodThunks.data[73] = c73; - methodThunks.data[74] = c74; - methodThunks.data[75] = c75; - methodThunks.data[76] = c76; - methodThunks.data[77] = c77; - methodThunks.data[78] = c78; - methodThunks.data[79] = c79; - methodThunks.data[80] = c80; - methodThunks.data[81] = c81; - methodThunks.data[82] = c82; - methodThunks.data[83] = c83; - methodThunks.data[84] = c84; - methodThunks.data[85] = c85; - methodThunks.data[86] = c86; - methodThunks.data[87] = c87; - methodThunks.data[88] = c88; - methodThunks.data[89] = c89; - methodThunks.data[90] = c90; - methodThunks.data[91] = c91; - methodThunks.data[92] = c92; - methodThunks.data[93] = c93; - methodThunks.data[94] = c94; - methodThunks.data[95] = c95; - methodThunks.data[96] = c96; - methodThunks.data[97] = c97; - methodThunks.data[98] = c98; - methodThunks.data[99] = c99; - } // ctor - } -} diff --git a/src/util.vala b/src/util.vala new file mode 100644 index 0000000..22f7d36 --- /dev/null +++ b/src/util.vala @@ -0,0 +1,73 @@ +// util.vala: Utility routines used by the Vala bindings for WrenVM +// See wren-pkg/wren/src/include/wren.h for documentation. +// +// By Christopher White +// Copyright (c) 2021 Christopher White. All rights reserved. +// SPDX-License-Identifier: MIT + +[CCode(cprefix="wren", cheader_filename="wren-vala-merged.h")] +namespace Wren { + + // === From myconfig.c === + /** The Wren version */ + public extern string APIVER(); + /** The wren-vala version */ + public extern string VERSION(); + + // === Defined in this file === + + /** Regex for foreign_decl_for */ + private static Regex fdf_re_ = null; + + /** Make a foreign declaration for a particular signature */ + public string foreign_decl_for(string sig) + { + if(fdf_re_ == null) { + try { + var ident = "(?:[A-Za-z_][A-Za-z_0-9]*)"; + fdf_re_ = new Regex( + "^(?(?:static\\s+)?%s)(?.*)$".printf(ident)); + } catch(RegexError e) { // LCOV_EXCL_START - unreached if tests pass + error("Could not create regex for %s(): %s", GLib.Log.METHOD, e.message); + } // LCOV_EXCL_STOP + } + + MatchInfo matches; + if(!fdf_re_.match(sig, 0, out matches)) { + return "INVALID_FORMAT for >%s // Copyright (c) 2021 Christopher White. All rights reserved. @@ -9,7 +7,7 @@ // // TODO: check the ownership on Handle instances. -[CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] +[CCode(cheader_filename="wren-vala-merged.h")] namespace Wren { /** @@ -17,7 +15,7 @@ namespace Wren { * * Use this instead of a bare Handle whenever possible. */ - [CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] + [CCode(cheader_filename="wren-vala-merged.h")] public class HandleV { private VMV vm; @@ -90,7 +88,7 @@ namespace Wren { public delegate void MethodImpl(GLib.Object self, VMV vm); /** - * Fill a hash table with a map from {@link Wren.Tramp.hash_key} to + * Fill a hash table with a map from function signature to * {@link Wren.HasMethods.MethodImpl}. * * Must be callable multiple times, returning the same value every time. @@ -113,7 +111,7 @@ namespace Wren { * functions, e.g., call(), use Wren.HandleV arguments. This is so the * default use of Handles is consistent with Vala memory management. */ - [CCode(cheader_filename="libwren-vala-@APIVER@.h,@WREN_HEADER@")] + [CCode(cheader_filename="wren-vala-merged.h")] public class VMV : Object { // --- Instance data ------------------------------------------ @@ -122,12 +120,16 @@ namespace Wren { private VM vm = null; /** - * Trampolines + * Public getter for the wrapped VM * - * Has to be public, but if you use it, your bindings will break! + * Use with caution! */ - [CCode(notify = false)] - public Tramp tramp_ { get; private set; default = new Tramp(); } + public unowned VM raw_vm() { + return vm; + } + + /** Trampolines */ + private Tramp tramp_ = new Tramp(); // --- VM functions ------------------------------------------- @@ -137,16 +139,16 @@ namespace Wren { var self = vm.GetUserData() as VMV; debug("bindForeignClass %s in %p\n", Tramp.hash_key(module, className), self); - return self.tramp_.get_functions(module, className); + return self.tramp_.get_class_methods(module, className); } /** Bind a foreign method */ - private static ForeignMethodFn bindForeignMethod(VM vm, string module, + private static BindForeignMethodResult bindForeignMethod(VM vm, string module, string className, bool isStatic, string signature) { var self = vm.GetUserData() as VMV; debug("bindForeignMethod %s in %p\n", - Tramp.hash_key(module, className, isStatic, signature), self); + Tramp.hash_key(module, className, make_sig(isStatic, signature)), self); return self.tramp_.get_method(module, className, isStatic, signature); } @@ -381,52 +383,62 @@ namespace Wren { // --- High-level interface ----------------------------------- // This section binds classes as a whole between Vala and Wren -#if 0 /** * Get a slot as a GValue */ public Value get_slot(int slot) + throws Marshal.Error { - // TODO - return Value(GLib.Type.INVALID); + return Marshal.value_from_slot(vm, slot); } -#endif /** - * Expose class T to Wren, as part of module `mod` + * Wrap Wren slots in a GValue array. * - * @param mod The module to add the class to (default "main"). + * @param first_slot The first slot to grab (default 0) + * @param num_slots How many slots to grab (default -1 == the whole + * slot array) + * @return An array of freshly-created GValues. */ - public InterpretResult expose_class(string mod = "main") + public Value[] get_slots(int first_slot = 0, int num_slots = -1) + throws Marshal.Error { - StringBuilder wren = new StringBuilder(); // source to be interpreted - var type = typeof(T); - assert(!type.is_abstract()); // TODO more sophisticated error handling - assert(type.is_instantiatable()); - - // ObjectClass ocl = (ObjectClass)type.class_ref(); - - wren.append_printf("foreign class %s {\n", type.name()); - - // Properties - // foreach (ParamSpec spec in ocl.list_properties()) { - // TODO print ("%s\n", spec.get_name ()); - // } + return Marshal.values_from_slots(vm, first_slot, num_slots); + } - wren.append("construct new() {}\n"); + /** + * Set a slot to a GValue + * + * @param slot Slot to set + * @param val New value + */ + public void set_slot(int slot, Value val) + throws Marshal.Error + { + Marshal.slot_from_value(vm, slot, val); + } - // Functions - Tramp.foreach_method_of_type(type, (k,v) => { - wren.append(Tramp.foreign_decl_for_key(k) + "\n"); - }); + /** + * Expose class `type` to Wren, as part of module `mod` + * + * @param type The type of the class to add + * @param mod The module to add the class to (default "main"). + */ + public InterpretResult expose_class(GLib.Type type, string mod = "main") + { + assert(!type.is_abstract()); // TODO more sophisticated error handling + assert(type.is_instantiatable()); - wren.append("}\n"); - debug("Wren source: >>%s<<", wren.str); + var wren_source = tramp_.add_type(mod, type.name(), type); + if(wren_source == "") { // the second time we were called for the same class + debug("Already implemented"); + return SUCCESS; + } - tramp_.add_type(mod, type.name(), type); + debug("Wren source: >>%s<<", wren_source); - return interpret(mod, wren.str); - } + return interpret(mod, wren_source); + } // expose_class() } // class VMV } // namespace Wren diff --git a/src/wren-vala-merged.h b/src/wren-vala-merged.h new file mode 100644 index 0000000..4f6b743 --- /dev/null +++ b/src/wren-vala-merged.h @@ -0,0 +1,7 @@ +// wren-vala-merged.h: Header used while compiling wren-vala +// By Christopher White +// Copyright (c) 2021 Christopher White. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "wren.h" +#include "wren-vala-generated.h" diff --git a/t/000-sanity-t.vala b/t/000-sanity-t.vala index 0165cf3..609f01c 100644 --- a/t/000-sanity-t.vala +++ b/t/000-sanity-t.vala @@ -1,7 +1,11 @@ // t/000-sanity-t.vala +using Wren; + void test_sanity() { + debug("Wren API version %s, wren-vala version %s", + Wren.APIVER(), Wren.VERSION()); assert_true(true); } diff --git a/t/090-util-t.vala b/t/090-util-t.vala new file mode 100644 index 0000000..cdf3f42 --- /dev/null +++ b/t/090-util-t.vala @@ -0,0 +1,40 @@ +// t/090-util-t.vala + +using Wren; // required to pull in the headers on valac 0.50.1.79-3f2a6 + +void test_foreign_decl_for() +{ + // Methods + assert_cmpstr(foreign_decl_for("static sig()"), EQ, "foreign static sig()"); + assert_cmpstr(foreign_decl_for("sig()"), EQ, "foreign sig()"); + + assert_cmpstr(foreign_decl_for("static sig(_)"), EQ, "foreign static sig(p0)"); + assert_cmpstr(foreign_decl_for("sig(_)"), EQ, "foreign sig(p0)"); + + assert_cmpstr(foreign_decl_for("static sig(_,_)"), EQ, "foreign static sig(p0,p1)"); + assert_cmpstr(foreign_decl_for("sig(_,_)"), EQ, "foreign sig(p0,p1)"); + + // Accessors + assert_cmpstr(foreign_decl_for("static sig"), EQ, "foreign static sig"); + assert_cmpstr(foreign_decl_for("sig"), EQ, "foreign sig"); + + assert_cmpstr(foreign_decl_for("static sig=(_)"), EQ, "foreign static sig=(p0)"); + assert_cmpstr(foreign_decl_for("sig=(_)"), EQ, "foreign sig=(p0)"); +} + +void test_make_sig() +{ + assert_cmpstr(make_sig(false, "foo"), EQ, "foo"); + assert_cmpstr(make_sig(true, "foo"), EQ, "static foo"); + assert_cmpstr(make_sig(false, "foo(_)"), EQ, "foo(_)"); + assert_cmpstr(make_sig(true, "foo(_)"), EQ, "static foo(_)"); +} + +public static int main(string[] args) +{ + Test.init(ref args); + Test.set_nonfatal_assertions(); + Test.add_func("/090-util/foreign_decl_for", test_foreign_decl_for); + Test.add_func("/090-util/make_sig", test_make_sig); + return Test.run(); +} diff --git a/t/105-tramp-t.vala b/t/105-tramp-t.vala index d2515e2..bc9d41e 100644 --- a/t/105-tramp-t.vala +++ b/t/105-tramp-t.vala @@ -5,21 +5,8 @@ using Wren; // required to pull in the headers on valac 0.50.1.79-3f2a6 void test_hash_key() { assert_cmpstr(Tramp.hash_key("m","c"), EQ, "m!c"); - assert_cmpstr(Tramp.hash_key("m","c",false,"sig()"), EQ, "m!c.sig()"); - assert_cmpstr(Tramp.hash_key("m","c",true,"sig()"), EQ, "m!c.$sig()"); -} - -void test_foreign_decl_for_key() -{ - assert_cmpstr(Tramp.foreign_decl_for_key("m!c"), EQ, "INVALID_FORMAT!"); - assert_cmpstr(Tramp.foreign_decl_for_key("m!c.$sig()"), EQ, "foreign static sig()"); - assert_cmpstr(Tramp.foreign_decl_for_key("m!c.sig()"), EQ, "foreign sig()"); - - assert_cmpstr(Tramp.foreign_decl_for_key("m!c.$sig(_)"), EQ, "foreign static sig(p0)"); - assert_cmpstr(Tramp.foreign_decl_for_key("m!c.sig(_)"), EQ, "foreign sig(p0)"); - - assert_cmpstr(Tramp.foreign_decl_for_key("m!c.$sig(_,_)"), EQ, "foreign static sig(p0,p1)"); - assert_cmpstr(Tramp.foreign_decl_for_key("m!c.sig(_,_)"), EQ, "foreign sig(p0,p1)"); + assert_cmpstr(Tramp.hash_key("m","c","sig()"), EQ, "m!c:sig()"); + assert_cmpstr(Tramp.hash_key("m","c","static sig()"), EQ, "m!c:static sig()"); } public static int main(string[] args) @@ -27,6 +14,5 @@ public static int main(string[] args) Test.init(ref args); Test.set_nonfatal_assertions(); Test.add_func("/105-tramp/hash_key", test_hash_key); - Test.add_func("/105-tramp/foreign_decl_for_key", test_foreign_decl_for_key); return Test.run(); } diff --git a/t/150-marshal-t.vala b/t/150-marshal-t.vala index 60fd051..f09ee6b 100644 --- a/t/150-marshal-t.vala +++ b/t/150-marshal-t.vala @@ -2,38 +2,182 @@ using Wren; // required to pull in the headers on valac 0.50.1.79-3f2a6 -private Wren.VM g_vm; +private Wren.Configuration g_conf; + +/** Make a new vm using g_conf */ +private Wren.VM new_vm() +{ + var retval = new Wren.VM(g_conf); + assert_nonnull(retval); + if(retval == null) { // LCOV_EXCL_START + print("# Could not create VM\n"); + assert_not_reached(); + } // LCOV_EXCL_STOP + return retval; +} // Test reading Wren data into a GValue void test_from_wren() { - Value v; - g_vm.EnsureSlots(1); + try { + var vm = new_vm(); + Value v; + + vm.EnsureSlots(1); + + vm.SetSlotBool(0, true); + v = Marshal.value_from_slot(vm, 0); + assert_true(v.type() == GLib.Type.BOOLEAN); + assert_true(v.get_boolean()); + + vm.SetSlotDouble(0, 42); + v = Marshal.value_from_slot(vm, 0); + assert_true(v.type() == GLib.Type.DOUBLE); + assert_cmpfloat(v.get_double(), EQ, 42); + + // FOREIGN is tested in t/210 + + var interpok = vm.Interpret("main", """var listfromwren = [1, "yes", true]"""); + assert_true(interpok == SUCCESS); + vm.EnsureSlots(2); + vm.GetVariable("main", "listfromwren", 0); + + // Check Wren's view of the list as well as Marshal's view + { + Wren.Type types[] = { Wren.Type.NUM, Wren.Type.STRING, Wren.Type.BOOL }; + Value values[] = { 1.0, "yes", true }; + assert_cmpuint(vm.GetSlotType(0), EQ, Wren.Type.LIST); + assert_cmpint(vm.GetListCount(0), EQ, 3); + for(int i = 0; i < vm.GetListCount(0); ++i) { + vm.GetListElement(0, i, 1); // slot 1 := (slot 0)[i] + assert_cmpuint(vm.GetSlotType(1), EQ, types[i]); + var val = Marshal.value_from_slot(vm, 1); + +#if 0 + // XXX: the json-glib is just for serializing values when debugging + var node = new Json.Node(VALUE); + node.set_value(values[i]); + debug("%d: expected %s", i, Json.to_string(node, true)); + node.set_value(val); + debug("%d: got %s", i, Json.to_string(node, true)); +#endif + + // Note: == just checks for pointer equality, not equality + // of the contained values. Do it brute-force. + switch(i) { + case 0: + assert_true(val.type() == GLib.Type.DOUBLE); + assert_cmpfloat(val.get_double(), EQ, values[0].get_double()); + break; + case 1: + assert_true(val.type() == GLib.Type.STRING); + assert_cmpstr(val.get_string(), EQ, values[1].get_string()); + break; + case 2: + assert_true(val.type() == GLib.Type.BOOLEAN); + assert_true(val.get_boolean() == values[2].get_boolean()); + break; + default: + assert_not_reached(); // LCOV_EXCL_LINE + } + } + } // lists + + // TODO MAP + + vm.SetSlotNull(0); + v = Marshal.value_from_slot(vm, 0); + assert_true(v.type() == Wren.get_null_type()); - g_vm.SetSlotBool(0, true); - v = Marshal.to_value_raw(g_vm, 0); - assert_true(v.type() == GLib.Type.BOOLEAN); - assert_true(v.get_boolean()); + vm.SetSlotString(0, "hello"); + v = Marshal.value_from_slot(vm, 0); + assert_true(v.type() == GLib.Type.STRING); + assert_true(v.get_string() == "hello"); - g_vm.SetSlotDouble(0, 42); - v = Marshal.to_value_raw(g_vm, 0); - assert_true(v.type() == GLib.Type.DOUBLE); - assert_cmpfloat(v.get_double(), EQ, 42); + // Marshal.values_from_slots() + vm.EnsureSlots(3); - // TODO FOREIGN, LIST, MAP + vm.SetSlotBool(0, true); + vm.SetSlotDouble(1, 42.0); + vm.SetSlotString(2, "Yep!"); + var values = Marshal.values_from_slots(vm, 0, 3); - g_vm.SetSlotNull(0); - v = Marshal.to_value_raw(g_vm, 0); - assert_true(v.type() == Wren.get_null_type()); + assert_cmpuint(values.length, EQ, 3); + assert_true(values[0].type() == GLib.Type.BOOLEAN); + assert_true(values[0].get_boolean()); + assert_true(values[1].type() == GLib.Type.DOUBLE); + assert_cmpfloat(values[1].get_double(), EQ, 42.0); + assert_true(values[2].type() == GLib.Type.STRING); + assert_cmpstr(values[2].get_string(), EQ, "Yep!"); - g_vm.SetSlotString(0, "hello"); - v = Marshal.to_value_raw(g_vm, 0); - assert_true(v.type() == GLib.Type.STRING); - assert_true(v.get_string() == "hello"); + } catch(Marshal.Error e) { // LCOV_EXCL_START - unreached if tests pass + warning("marshalling error: %s", e.message); + assert_not_reached(); + } // LCOV_EXCL_STOP // TODO UNKNOWN -} // test_from_wren +} // test_from_wren() + +void test_to_wren() +{ + try { + var vm = new_vm(); + Value val; + vm.EnsureSlots(1); + + val = true; + Marshal.slot_from_value(vm, 0, val); + assert_true(vm.GetSlotBool(0)); + val = false; + Marshal.slot_from_value(vm, 0, val); + assert_true(!vm.GetSlotBool(0)); + + val = 42; + Marshal.slot_from_value(vm, 0, val); + assert_cmpfloat(vm.GetSlotDouble(0), EQ, 42.0); + // TODO other types of number + + // TODO FOREIGN, LIST, MAP + + val = Value(Wren.get_null_type()); + Marshal.slot_from_value(vm, 0, val); + assert_cmpuint(vm.GetSlotType(0), EQ, Wren.Type.NULL); + + val = "Hello, world!"; + Marshal.slot_from_value(vm, 0, val); + assert_cmpstr(vm.GetSlotString(0), EQ, "Hello, world!"); + + // TODO UNKNOWN + } catch(Marshal.Error e) { // LCOV_EXCL_START - unreached if tests pass + warning("marshalling error: %s", e.message); + assert_not_reached(); + } // LCOV_EXCL_STOP +} // test_to_wren() + +void test_vmv_marshal() +{ + try { + var vm = new VMV(); + vm.ensure_slots(1); + + Value val = "message"; + vm.set_slot(0, val); + Value val2 = vm.get_slot(0); + assert_true(val2.type() == GLib.Type.STRING); + assert_cmpstr(val2.get_string(), EQ, val.get_string()); + assert_true(&val != &val2); // didn't just get the same one back + + var vals = vm.get_slots(); + assert_cmpuint(vals.length, EQ, 1); + assert_true(vals[0].type() == GLib.Type.STRING); + assert_cmpstr(vals[0].get_string(), EQ, val.get_string()); + + } catch(Marshal.Error e) { // LCOV_EXCL_START - unreached if tests pass + warning("marshalling error: %s", e.message); + assert_not_reached(); + } // LCOV_EXCL_STOP +} // test_vmv_marshal() public static int main(string[] args) { @@ -41,19 +185,14 @@ public static int main(string[] args) Test.init(ref args); Test.set_nonfatal_assertions(); Test.add_func("/150-marshal/from-wren", test_from_wren); + Test.add_func("/150-marshal/to-wren", test_to_wren); + Test.add_func("/150-marshal/vmv_marshal", test_vmv_marshal); // Make the VM - var conf = Wren.Configuration(); - Wren.InitConfiguration(ref conf); - conf.writeFn = Wren.defaultWriteFn; - conf.errorFn = Wren.defaultErrorFn; - - g_vm = new Wren.VM(conf); - assert_nonnull(g_vm); - if(g_vm == null) { - print("# Could not create VM\n"); - return 1; - } + g_conf = Wren.Configuration(); + Wren.InitConfiguration(ref g_conf); + g_conf.writeFn = Wren.defaultWriteFn; // LCOV_EXCL_LINE - GNOME/vala#1165 + g_conf.errorFn = Wren.defaultErrorFn; // LCOV_EXCL_LINE - GNOME/vala#1165 // Run the tests return Test.run(); diff --git a/t/160-error-s.vala b/t/160-error-s.vala new file mode 100644 index 0000000..5f7ab41 --- /dev/null +++ b/t/160-error-s.vala @@ -0,0 +1,24 @@ +// t/160-error-s.vala - support for test 160 + +using Wren; // required to pull in the headers on valac 0.50.1.79-3f2a6 + +public static int main(string[] args) +{ + if(args.length != 2) { + error("Usage: %s WHICHTEST", args[0]); + } + + if(args[1] == "noSuchMethod") { // runtime error + var vmv = new Wren.VMV(); + var ok = vmv.interpret("main", """ 123.noSuchMethod """); + return (ok == SUCCESS) ? 1 : 0; + + } else if(args[1] == "compiletime") { // compile-time error + var vmv = new Wren.VMV(); + var ok = vmv.interpret("main", """ ! """); + return (ok == SUCCESS) ? 1 : 0; + + } else { + error("Unknown test type %s", args[1]); + } +} diff --git a/t/160-error-t.sh b/t/160-error-t.sh new file mode 100755 index 0000000..fe5a5b4 --- /dev/null +++ b/t/160-error-t.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +. common.sh + +grep -q "Num does not implement 'noSuchMethod'" <(./160-error-s noSuchMethod 2>&1) +ok $? "runtime error" + +grep -q "Expected expression" <(./160-error-s compiletime 2>&1) +ok $? "compile-time error" + +done_testing diff --git a/t/200-expose-class-t.vala b/t/200-expose-class-t.vala index 41b126e..6270ea5 100644 --- a/t/200-expose-class-t.vala +++ b/t/200-expose-class-t.vala @@ -2,7 +2,6 @@ using Wren; // required to pull in the headers on valac 0.50.1.79-3f2a6 - class Sample : Object, Wren.HasMethods { // --- How we observe Wren effects ----- @@ -10,10 +9,14 @@ class Sample : Object, Wren.HasMethods { public static bool wasInitialized = false; public static string gMsg; public static unowned Object gObj; + public static int gPropValue = 42; // --- Properties ---------------------- - public int prop { get; set; default = 42; } + public int prop { + get { return gPropValue; } + set { gPropValue = value; } + } // --- Ctor/dtor ----------------------- construct { @@ -53,15 +56,17 @@ class Sample : Object, Wren.HasMethods { } /** Wren-visible function of two args */ - void join(string part2, string part1) + string join(string part2, string part1) { gMsg = part1 + part2; // opposite order of the args on purpose + return gMsg + "joined"; } /** Wren invoker for join() */ static void wren_join(GLib.Object self, VMV vm) { - ((!)(self as Sample)).join(vm.get_slot_string(1), vm.get_slot_string(2)); + var retval = ((!)(self as Sample)).join(vm.get_slot_string(1), vm.get_slot_string(2)); + vm.set_slot_string(0, retval); } /** Wren-visible STATIC function of two args */ @@ -79,10 +84,10 @@ class Sample : Object, Wren.HasMethods { /** Tell Wren about our methods */ public void get_methods(ref HashTable methods) { - methods["main!Sample.hello()"] = wren_sayHello; - methods["main!Sample.stashMessage(_)"] = wren_stashMessage; - methods["main!Sample.join(_,_)"] = wren_join; - methods["main!Sample.$sjoin(_,_)"] = wren_sjoin; + methods["hello()"] = wren_sayHello; + methods["stashMessage(_)"] = wren_stashMessage; + methods["join(_,_)"] = wren_join; + methods["static sjoin(_,_)"] = wren_sjoin; } } @@ -90,7 +95,7 @@ void test_instantiate() { var vm = new Wren.VMV(); - var ok = vm.expose_class("main"); + var ok = vm.expose_class(typeof(Sample), "main"); assert_true(ok == SUCCESS); Sample.wasInitialized = false; @@ -109,7 +114,7 @@ void test_methods() Sample.gMsg = null; Sample.gObj = null; - var ok = vm.expose_class("main"); + var ok = vm.expose_class(typeof(Sample), "main"); assert_true(ok == SUCCESS); // No args @@ -145,7 +150,7 @@ void test_static_methods() Sample.gMsg = null; Sample.gObj = null; - var ok = vm.expose_class("main"); + var ok = vm.expose_class(typeof(Sample), "main"); assert_true(ok == SUCCESS); // Instance function @@ -164,12 +169,79 @@ void test_static_methods() assert_cmpstr(Sample.gMsg, EQ, "staticDC"); } +void test_retvals() +{ + var vm = new Wren.VMV(); + + Sample.gMsg = null; + Sample.gObj = null; + + var ok = vm.expose_class(typeof(Sample), "main"); + assert_true(ok == SUCCESS); + + // Instance function + ok = vm.interpret("main", """ + var Obj = Sample.new() + var Ret = Obj.join("A","B") + """); + assert_true(ok == SUCCESS); + vm.ensure_slots(1); + vm.get_variable("main", "Ret", 0); + assert_true(vm.get_slot_type(0) == Wren.Type.STRING); + assert_cmpstr(vm.get_slot_string(0), EQ, "BAjoined"); +} + +void test_properties() +{ + var vm = new Wren.VMV(); + + var ok = vm.expose_class(typeof(Sample), "main"); + assert_true(ok == SUCCESS); + + assert_cmpint(Sample.gPropValue, EQ, 42); + + // Set + ok = vm.interpret("main", """ + var Obj = Sample.new() + Obj.prop = 128 + """); + assert_true(ok == SUCCESS); + assert_cmpint(Sample.gPropValue, EQ, 128); + + // Get + Sample.gPropValue = 1337; + ok = vm.interpret("main", """ + var Got = Obj.prop + """); + assert_true(ok == SUCCESS); + + vm.ensure_slots(1); + vm.get_variable("main", "Got", 0); + assert_true(vm.get_slot_type(0) == Wren.Type.NUM); + assert_cmpfloat(vm.get_slot_double(0), EQ, 1337); +} + +void test_add_type_succeeds_twice() +{ + var vm = new Wren.VMV(); + + var ok = vm.expose_class(typeof(Sample), "main"); + assert_true(ok == SUCCESS); + ok = vm.expose_class(typeof(Sample), "main"); + assert_true(ok == SUCCESS); +} + public static int main(string[] args) { Test.init(ref args); Test.set_nonfatal_assertions(); + Test.add_func("/200-expose-class/instantiate", test_instantiate); Test.add_func("/200-expose-class/methods", test_methods); Test.add_func("/200-expose-class/static_methods", test_static_methods); + Test.add_func("/200-expose-class/retvals", test_retvals); + Test.add_func("/200-expose-class/properties", test_properties); + Test.add_func("/200-expose-class/add_type_succeeds_twice", test_add_type_succeeds_twice); + return Test.run(); } diff --git a/t/210-marshal-class-t.vala b/t/210-marshal-class-t.vala new file mode 100644 index 0000000..f7f0ab6 --- /dev/null +++ b/t/210-marshal-class-t.vala @@ -0,0 +1,114 @@ +// t/200-marshal-class-t.vala + +using Wren; // required to pull in the headers on valac 0.50.1.79-3f2a6 + +class Sample : Object, Wren.HasMethods { + + // --- How we observe Wren effects ----- + + public static bool wasInitialized = false; + public string instanceMessage; + + // --- Ctor/dtor ----------------------- + construct { + wasInitialized = true; + debug("Initialized!"); + } + + ~Sample() { + debug("Dtor"); + } + + // --- Methods ------------------------- + + /** Wren accessor for instanceMessage */ + static void wren_get_message(GLib.Object self, VMV vm) + { + var instance = (!)(self as Sample); + vm.ensure_slots(1); + vm.set_slot_string(0,instance.instanceMessage); + } + + public void stash_message(string msg) + { + instanceMessage = msg; + } + + /** Tell Wren about our methods */ + public void get_methods(ref HashTable methods) + { + methods["getMessage"] = wren_get_message; + } +} + +void test_from_wren() +{ + try { + var vmv = new Wren.VMV(); + + var ok = vmv.expose_class(typeof(Sample), "main"); + assert_true(ok == SUCCESS); + + Sample.wasInitialized = false; + ok = vmv.interpret("main", """ + var Obj = Sample.new() + """); + assert_true(ok == SUCCESS); + assert_true(Sample.wasInitialized); + + vmv.ensure_slots(1); + vmv.get_variable("main", "Obj", 0); + assert_true(vmv.get_slot_type(0) == Wren.Type.FOREIGN); + + unowned var vm = vmv.raw_vm(); + assert_nonnull(vm); + var obj_val = Marshal.value_from_slot(vm, 0); + assert_true(obj_val.type() == GLib.Type.OBJECT); + var sample = obj_val.get_object() as Sample; + assert_nonnull(sample); + + // TODO make sure modifications to `sample` are visible in Wren + sample.stash_message("from Vala"); + ok = vmv.interpret("main", """ + if(Obj.getMessage != "from Vala") { + Fiber.abort("mismatch: got '%(Obj.getMessage)'") + } + """); + assert_true(ok == SUCCESS); + + } catch(Marshal.Error e) { // LCOV_EXCL_START - unreached if tests pass + warning("marshalling error: %s", e.message); + assert_not_reached(); + } // LCOV_EXCL_STOP + +} // test_from_wren() + +void test_to_wren() +{ + var vmv = new Wren.VMV(); + + var ok = vmv.expose_class(typeof(Sample), "main"); + assert_true(ok == SUCCESS); + + var s = new Sample(); + vmv.ensure_slots(1); + try { + Marshal.slot_from_value(vmv.raw_vm(), 0, s); + assert_not_reached(); // LCOV_EXCL_LINE - unreachable on a successful test + } catch(Marshal.Error e) { + debug("got error: %s\n", e.message); + assert_true(e is Marshal.Error.EINVAL); + } + +} // test_to_wren() + +public static int main(string[] args) +{ + Test.init(ref args); + Test.set_nonfatal_assertions(); + + Test.add_func("/210-marshal-class/from_wren", test_from_wren); + Test.add_func("/210-marshal-class/to_wren", test_to_wren); + + return Test.run(); +} diff --git a/t/Makefile.am b/t/Makefile.am index 9541633..db0d4d9 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -30,22 +30,31 @@ LOCAL_VALA_FLAGS = \ --pkg libwren-vala-@APIVER@ \ $(EOL) +# XXX: the json-glib is just for serializing values when debugging +# --pkg json-glib-1.0 + LOCAL_CFLAGS = \ -I $(top_srcdir)/src \ -I $(top_builddir)/src \ $(EOL) +# XXX $(shell pkg-config --cflags json-glib-1.0) + +AM_LDFLAGS = \ + $(EOL) +# XXX $(shell pkg-config --libs json-glib-1.0) # Libs: -lwren is in LIBS, which is included by default. LDADD = \ -L$(top_builddir)/src \ - -lwren-vala \ + -lwren-vala-@APIVER@ \ $(EOL) # --- Test definitions --- -if !WREN_INSTALL +# When you add files here, also add them to the list in the `prettyprint` target test_programs += \ 000-sanity-t \ + 090-util-t \ 100-vmv-t \ 105-tramp-t \ 120-read-var-from-wren-t \ @@ -53,14 +62,17 @@ test_programs += \ 140-roundtrip-data-t \ 150-marshal-t \ 200-expose-class-t \ + 210-marshal-class-t \ $(EOL) dist_test_scripts += \ 110-hello-world-t.sh \ + 160-error-t.sh $(EOL) test_extra_programs += \ 110-hello-world-s \ + 160-error-s \ $(EOL) dist_test_extra_scripts += \ @@ -69,5 +81,3 @@ dist_test_extra_scripts += \ # Make sure common.sh gets remade if necessary before running tests all-local: common.sh - -endif diff --git a/wren-pkg/Makefile.am b/wren-pkg/Makefile.am index cf4cfa4..10bf4f3 100644 --- a/wren-pkg/Makefile.am +++ b/wren-pkg/Makefile.am @@ -8,44 +8,15 @@ include $(top_srcdir)/rules.mk EXTRA_DIST = .gitignore -pkgconfig_DATA = -include_HEADERS = -lib_LTLIBRARIES = - -# === Headers =========================================================== - -if WREN_INSTALL - -pkgconfig_DATA += \ - libwren-${APIVER}.pc \ - $(EOL) - -include_HEADERS += \ - wren-${APIVER}.h \ - wren-${APIVER}.hpp \ - $(EOL) - -endif - -wren-${APIVER}.h: wren/src/include/wren.h - cp "$<" "$@" - -wren-${APIVER}.hpp: wren/src/include/wren.hpp - cp "$<" "$@" - -CLEANFILES = \ - wren-${APIVER}.h \ - wren-${APIVER}.hpp \ - $(EOL) +noinst_LTLIBRARIES = # === Libraries ========================================================= # Do include all the optional features. +# TODO make the optional features configure-time options -if WREN_INSTALL -lib_LTLIBRARIES += \ +noinst_LTLIBRARIES += \ libwren-@APIVER@.la \ $(EOL) -endif libwren_@APIVER@_la_CFLAGS = \ -I$(srcdir)/wren/src/vm -I$(srcdir)/wren/src/optional \ @@ -53,6 +24,9 @@ libwren_@APIVER@_la_CFLAGS = \ $(AM_CFLAGS) \ $(EOL) +# Sources. Note that we list both our local copies of the headers and the +# Wren headers (wren/src/include/*). The local copies depend on the Wren +# headers above. This way everything gets disted and depped. libwren_@APIVER@_la_SOURCES = \ wren/src/include/wren.h \ wren/src/include/wren.hpp \ @@ -82,11 +56,11 @@ libwren_@APIVER@_la_SOURCES = \ wren/src/optional/wren_opt_random.wren.inc \ $(EOL) -# === Cleanup =========================================================== -# When not installing Wren, don't leave deps files lying around --- -# we don't need them, and they dirty the repo. +# === Other Wren files ================================================== -if !WREN_INSTALL -all-local: - -find $(builddir) -name .deps -exec rm -rf {} + -endif +EXTRA_DIST += \ + wren/AUTHORS \ + wren/CHANGELOG.md \ + wren/LICENSE \ + wren/README.md \ + $(EOL) diff --git a/wren-pkg/wren b/wren-pkg/wren index 2bc895c..d0ac43b 160000 --- a/wren-pkg/wren +++ b/wren-pkg/wren @@ -1 +1 @@ -Subproject commit 2bc895c26d8b0cc7ff080c05e339d6569f86367f +Subproject commit d0ac43b3bc6dee3bc9c31c6b5b57015fb3673d66