Moved to modular java build system.
authorNot Zed <notzed@gmail.com>
Sun, 6 Feb 2022 00:19:13 +0000 (10:49 +1030)
committerNot Zed <notzed@gmail.com>
Sun, 6 Feb 2022 00:19:13 +0000 (10:49 +1030)
96 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
README
api/Makefile [deleted file]
config.make.in [new file with mode: 0644]
java.make [new file with mode: 0644]
src/Makefile [deleted file]
src/apigen.pm [deleted file]
src/generate-native [deleted file]
src/notzed.api/native/api.c [moved from api/api.c with 97% similarity]
src/notzed.api/native/libapi/api.h [moved from api/api.h with 100% similarity]
src/notzed.api/native/native.make [new file with mode: 0644]
src/notzed.apiobject/classes/api/test/TestAPI.java [moved from test-api-object/src/api/test/TestAPI.java with 78% similarity]
src/notzed.apiobject/classes/module-info.java [new file with mode: 0644]
src/notzed.apiobject/gen/apiobject.api [moved from test-api-object/api-object.api with 93% similarity]
src/notzed.apiobject/gen/apiobject.h [new file with mode: 0644]
src/notzed.apiobject/gen/gen.make [new file with mode: 0644]
src/notzed.apistatic/classes/api/test/TestAPI.java [moved from test-api-static/src/api/test/TestAPI.java with 83% similarity]
src/notzed.apistatic/classes/module-info.java [new file with mode: 0644]
src/notzed.apistatic/gen/apistatic.api [moved from test-api-static/api-static.api with 95% similarity]
src/notzed.apistatic/gen/apistatic.h [new file with mode: 0644]
src/notzed.apistatic/gen/gen.make [new file with mode: 0644]
src/notzed.clstatic/classes/module-info.java [new file with mode: 0644]
src/notzed.clstatic/classes/opencl/cl_event_list.java [new file with mode: 0755]
src/notzed.clstatic/classes/opencl/test/clinfo.java [moved from test-opencl-basic/src/opencl/test/TestOpenCL.java with 90% similarity]
src/notzed.clstatic/gen/gen.make [new file with mode: 0644]
src/notzed.clstatic/gen/opencl.api [new file with mode: 0644]
src/notzed.clstatic/gen/opencl.h [moved from test-opencl-basic/opencl.h with 100% similarity]
src/notzed.ffmpeg/classes/ffmpeg/AVPixelReader.java [moved from test-ffmpeg/src/proto/ffmpeg/AVPixelReader.java with 98% similarity]
src/notzed.ffmpeg/classes/ffmpeg/FramePixelReader.java [moved from test-ffmpeg/src/proto/ffmpeg/FramePixelReader.java with 73% similarity]
src/notzed.ffmpeg/classes/ffmpeg/test/TestFFMPEG.java [moved from test-ffmpeg/src/ffmpeg/test/TestFFMPEG.java with 54% similarity]
src/notzed.ffmpeg/classes/module-info.java [new file with mode: 0644]
src/notzed.ffmpeg/gen/ffmpeg.api [new file with mode: 0644]
src/notzed.ffmpeg/gen/ffmpeg.h [new file with mode: 0644]
src/notzed.ffmpeg/gen/gen.make [new file with mode: 0644]
src/notzed.nativez/bin/export-api [new file with mode: 0755]
src/notzed.nativez/bin/export-defines [moved from src/export-defines with 94% similarity]
src/notzed.nativez/bin/generate-api [moved from src/generate-api-2 with 71% similarity]
src/notzed.nativez/classes/au/notzed/nativez/Array.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/ByteArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/DoubleArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/FloatArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/Frame.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/FunctionPointer.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/IntArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/LongArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/Memory.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/Native.java [moved from src/template/Native.java with 96% similarity]
src/notzed.nativez/classes/au/notzed/nativez/Pointer.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/PointerArray.java [new file with mode: 0644]
src/notzed.nativez/classes/au/notzed/nativez/ShortArray.java [new file with mode: 0644]
src/notzed.nativez/classes/module-info.java [new file with mode: 0644]
src/notzed.nativez/lib/api.pm [new file with mode: 0644]
src/notzed.nativez/lib/code.api [moved from src/code.api with 79% similarity]
src/notzed.nativez/lib/code.pm [moved from src/code.pm with 98% similarity]
src/notzed.nativez/lib/config.pm [moved from src/genconfig2.pm with 99% similarity]
src/notzed.nativez/lib/method.pm [moved from src/method.pm with 90% similarity]
src/notzed.nativez/lib/tokenise.pm [moved from src/tokenise.pm with 100% similarity]
src/notzed.nativez/lib/types.api [moved from src/types.api with 87% similarity]
src/notzed.nativez/native/export.cc [moved from src/export.cc with 100% similarity]
src/notzed.nativez/native/list.h [moved from src/list.h with 100% similarity]
src/notzed.nativez/native/native.make [new file with mode: 0644]
src/notzed.nativez/native/tree-codes.c [moved from src/tree-codes.c with 100% similarity]
src/notzed.vkheader/classes/module-info.java [new file with mode: 0644]
src/notzed.vkheader/gen/gen.make [new file with mode: 0644]
src/notzed.vkheader/gen/vkheader.api [new file with mode: 0644]
src/notzed.vkheader/gen/vkheader.h [new file with mode: 0644]
src/notzed.vkregistry/classes/module-info.java [new file with mode: 0644]
src/notzed.vkregistry/classes/vulkan/PFN_vkDebugUtilsMessengerCallbackEXT.java [moved from test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java with 76% similarity]
src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java [moved from test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java with 86% similarity]
src/notzed.vkregistry/classes/vulkan/test/TestVulkan.java [moved from test-vulkan/src/zvk/test/TestVulkan.java with 76% similarity]
src/notzed.vkregistry/gen/VkDevice-part.java [moved from test-vulkan/template/VkDevice-part.java with 82% similarity]
src/notzed.vkregistry/gen/VkInstance-part.java [moved from test-vulkan/template/VkInstance-part.java with 88% similarity]
src/notzed.vkregistry/gen/export-registry [new file with mode: 0755]
src/notzed.vkregistry/gen/export-vulkan [moved from test-vulkan/generate-vulkan with 96% similarity]
src/notzed.vkregistry/gen/gen.make [new file with mode: 0644]
src/notzed.vkregistry/gen/mandelbrot.comp [moved from test-vulkan/mandelbrot.comp with 88% similarity]
src/template/Frame.java [deleted file]
src/template/Memory.java [deleted file]
test-api-object/Makefile [deleted file]
test-api-object/README [deleted file]
test-api-static/Makefile [deleted file]
test-api-static/README [deleted file]
test-ffmpeg/Makefile [deleted file]
test-ffmpeg/README [deleted file]
test-ffmpeg/ffmpeg.api [deleted file]
test-ffmpeg/ffmpeg.h [deleted file]
test-opencl-basic/Makefile [deleted file]
test-opencl-basic/opencl.api [deleted file]
test-vulkan/Makefile [deleted file]
test-vulkan/src/zvk/Frame.java [deleted file]
test-vulkan/src/zvk/Memory.java [deleted file]
test-vulkan/src/zvk/PFN_Test.java [deleted file]
test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java [deleted file]
test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java [deleted file]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..58686c2
--- /dev/null
@@ -0,0 +1,4 @@
+bin/
+config.make
+mandelbrot.pam
+movie.avi
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..9ab70aa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,41 @@
+
+dist_VERSION=0.0.99
+dist_NAME=panamaz
+dist_EXTRA=README COPYING
+
+include config.make
+
+java_MODULES = notzed.nativez notzed.apistatic notzed.apiobject notzed.ffmpeg notzed.clstatic notzed.vkregistry notzed.vkheader
+native_MODULES = notzed.api
+
+notzed.apistatic_JDEPMOD = notzed.nativez notzed.api
+notzed.apiobject_JDEPMOD = notzed.nativez notzed.api
+notzed.ffmpeg_JDEPMOD = notzed.nativez
+notzed.clstatic_JDEPMOD = notzed.nativez
+notzed.vkregistry_JDEPMOD = notzed.nativez
+notzed.vkheader_JDEPMOD = notzed.nativez
+
+notzed.apistatic_JMAIN = api.test.TestAPI
+notzed.apiobject_JMAIN = api.test.TestAPI
+notzed.ffmpeg_JMAIN = ffmpeg.test.TestFFMPEG
+notzed.clstatic_JMAIN = opencl.test.clinfo
+notzed.vkregistry_JMAIN = vulkan.test.TestVulkan
+
+$(foreach module,$(java_MODULES),$(eval $(module)_JMAINFLAGS=--enable-native-access=notzed.nativez,$(module)))
+
+include java.make
+
+#notzed.nativez_LIBRARIES = export
+#shared_LIBRARIES = libapi export
+
+#libapi_SOURCES = libapi/api.c
+
+#export_CXXSOURCES = export/export.cc
+#export_SOURCES = export/tree-codes.c
+#export_CXXFLAGS = -Wno-switch -g
+#export_CPPFLAGS=-I. -I$(PLUGINDIR)/include
+
+#bin/status/notzed.nativez.scripts: bin/$(TARGET)/lib/export.so
+$(java_bindir)/export-api: notzed.nativez
+
+#include shared.make
diff --git a/README b/README
index cdb34e6..135b477 100644 (file)
--- a/README
+++ b/README
 Introduction
 ------------
 
-This is some experiments with the panama-foreign abi JEP for invoking
-native C functions from Java directly and without JNI.
+This is various experiments with the panama-foreign abi JEP for
+invoking native C functions from Java directly and without JNI.
 
 The main goal is to experiment with creating a "java friendly" and
 mostly type-safe api directly in one step, without requiring
-additional wrapping or inconvenient use.
+additional wrapping.
 
-It's roughly tracking the openjdk repository of the same date.
-
-It uses a gcc plugin to compile the headers to obtain most of the api
+It uses a gcc plugin to compile c headers to obtain most of the api
 information, but requires cpp and perl to extract the values of
-#define constants.
-
-This api information is then converted to Java wrappers using a
-config-directed perl script `generate-native'.  The config is flexible
-enough to either generate c-like libraries of static functions or
-object-oriented layouts with data, static and object functions grouped
-by class.
-
-test-vulkan uses a different approach as the vulkan api is officially
-defined by an xml registry and not the generated C headers.  This is
-directly converted to about-as-java-friendly a vulkan api as one can
-hope for (all those structs you need to initialise get messy in any
-language).  Most of the script is converting the registry vk.xml file
-into a usable structure as it's really only designed for outputting c
-headers.
+#define constants as they are not available to the gcc plugin.
+
+This api information is then converted to Java code using a
+config-directed perl script `export-api'.  The config file can be used
+to create static (all-in-one) or object oriented output.  It includes
+templated strings as well as perl fragments to generate various source
+parts.
+
+The vulkan api is officially defined by a registry in xml, so there's
+a separate generator for that.
 
 Compile
 -------
 
-Requirements are gcc and cpp, perl, GNU make.
+Requirements are gcc and GNU cpp, perl, GNU make, and of course a
+compatible jdk-foreign jdk.  The various modules require headers or
+sdks for their corresponding libraries, e.g. ffmpeg-5.x,
+vulkan-tools-1.2.x.
+
+Copy config.make.in to config.make and modify any variables required.
+
+Parallel make should work.
+
+Build all:
+
+$ make -j
+
+Build one module:
+
+$ make notzed.nativez
+
+Run a demo (see next section):
+
+$ make run-notzed.vkregistry/vulkan.test.TestVulkan
+
+A non-recursive make setup is used although make file fragments are
+included from various locations across the modules.  All java is
+compiled as modules.
+
+JAVA_HOME must point to a compatible panama-enabled jdk.
+
+The latest at the time of writing was:
+
+ branch: foreign-jextract
+ commit: 2e1291680024f12fbf2f0f02b0f79d7b4348b369
+ date: Fri Feb 4 11:01:21 2022 +0000
+
+All output and intermediate files are in bin/
+
+bin/modules/<module>/classes - compiled java modules
+bin/gen/<module>/classes - generated java
+bin/gen/<module>/gen - generated intermediate non-java
+
+These are more or less an exploded view of all jmod files:
+
+bin/<target>/bin - commands for all modules
+bin/<target>/lib - libraries/config and modular jar files for all modules
+bin/<target>/include - header files for all modules
+
+Finally:
+
+bin/<target>/jmods - .jmod modules
+
+
+Demos
+-----
+
+Most examples have a demo, see the <module>_JMAIN variables in the
+Makefile for the targets.  They are executed using:
+
+$ make run-<module>/<main-class>
+
+Modules
+--------
+
+notzed.nativez contains some support classes and the code generator.
+The gcc plugin source is in src/notzed.nativez/native/ the code
+generator is in src/notzed.nativez/{bin,lib}.
+
+notzed.api is a pseudo-module containing a simple test c api for
+experiments, it just builds into a library.
+
+notzed.apistatic is a 'all in one class' static wrapper for
+notzed.api.
+
+notzed.apiobject is an object-oriented wrapper for notzed.api.
+
+notzed.clstatic is an 'all in one class' static wrapper for OpenCL
+(2.1).  The api closesly matches the C api except it converts error
+codes into exceptins and can infer certain arguments from others such
+as length parameters.  This is probably the most complete api.
+
+notzed.ffmpeg is a partial object-oriented mapping for ffmpeg-5.0 that
+closely follows the jjmpeg design.  It's enough to read video frames.
+The demo requires a file 'movie.api' in the root directory to run.
+
+notzed.vkheader uses the header-based generator on the vulkan
+installed headers.  This is still very incomplete work in progress.
+Much meta-data is lost such as which functions are extensions during
+the generation of the headers.
+
+notzed.vkregistry uses a completely different generator which directly
+parses the official xml registry specification for vulkan
+(/usr/share/vulkan/registry/vk.xml).  This is directly converted to
+about-as-java-friendly a vulkan api as one can hope for, particularly
+the constructors for all the config objects.
+
+Export process
+--------------
+
+The main generator is written in perl and lives in
+src/notzed.nativez/{bin,lib}.
+
+The process:
+
+* run the gcc plugin to extract all of the available c structures from
+  gcc and save them to a perl hash.
+
+  gcc plugins aren't very well documented so it was a lot of trial and
+  error to get it to output all the type information even starting
+  with a partial example.  The names of parameters for function calls
+  were particularly problematic.
+
+* optionally run export-defines to extract #define constants.  They
+  can be grouped/excluded by name or by the filename they belong to.
+  The the first rule to match will complete the processing for a given
+  type.
+
+  This is also a surprisingly difficult process because the c
+  pre-processor can just generate arbitrary c expressions so the only
+  way to find their correct value is to execute the c.  So the export
+  script generates a c file which is compiled and executed to generate
+  the perl definitions.
+
+  Currently the types are mapped to a 'compatible' native type by
+  using the gcc operators __builtin_choose_expr,
+  __builtin_types_compatible_p, and typeof to implement a
+  pseudo-function overloading in c - possiblly using c++ is a better
+  choice here.  For now inclusions or exclusions are required to
+  avoid problematic definitions that confuse these operators.
+
+These files are then fed to generate-api.  It proceeds in multiple
+stages.
+
+First stages are handled by lib/api.pm.
+
+* Load and preprocess the api definition.
+
+ - Perform a bunch of 'fix up' processing on the data structures such
+   as inserting anonymous types which are not generated by the plugin.
+
+ - Create a full dependency tree for the objects
+   specificallyreferenced by the api definition so only objects and
+   functions of interest are included.
+
+ - Resolve all the various export options using some rules and store
+   the information about the selected options onto target objects.
+
+Then generate-api produces the java files from a combination of the
+processed data definitions and the api definition.
+
+* Export 'libraries'.  These are static classes of functions and/or
+  constants.
+
+ - Can include static methods.  Methods can have per-method template
+   overrides.
+
+ - Can include constants.
+
+* Export 'structures'.  These represent C pointers.
+
+ - struct/union have a MemorySegment, a layout, and potentially
+   accessors for fields.
+
+ - anonymous structures just have a MemoryAddress and no accessors.
+
+ - Can include static and member methods.  Methods can have per-method
+   template overrides.
+
+ - Can include constants.
+
+* Export 'calls'.  These are function interfaces for function pointers.
+
+ - The interface is the java form of the call.
+
+ - If available a typedef name is used, otherwise the names are mapped
+   to a mangled name of the form Call_<args>_<return>.
+
+ - An upcall factory creates an upcall to an instance of a hidden
+   trampline interface which performs the mapping of arguments to java
+   and returns to c.
+
+ - A downcall factory creates a downcall which maps the java call to
+   native and back.
+
+ - Both factories return a record of type FunctionPointer<type> which
+   contains both a NativeSymbol and the java interface instance so they
+   can be used from either environment.
+
+* Export 'constants'.  These are referenced enums or any defines which
+  haven't been included in any other library or struct.
+
+ - enums are mapped to an interface with fields of:
+     'static final int FOO = xx'
+   of
+     'static final long FOO = xx'
+
+ - defines are mapped to a matching native type which includes
+   floating point types and strings in addition to all the integral
+   types.
 
-First run make in src.
+lib/code.pm is used to generate some of the code and handle
+templating. Common code templates are defined in lib/code.api but can
+be extended or replaced by a given api file.
 
-See test-*/* for examples.  JAVA_HOME must point to a compatible
-panama-enabled jdk.
+lib/method.pm is used to generate per-field (struct) and per-argument
+(function) sub-templates for mapping c to and from java.  The
+sub-templates it uses are defined in lib/types.api but again they can
+be extended or replaced by a given api file.
 
 Status
 ------
 
-It's all work in progress of course.
+It's all very much work in progress and due to the constant changes in
+panama will be in flux for some time.
 
-* bitfields not implemented yet.
+* bitfields are implemented.
 * varargs is not implemented.
-* the support library in Memory.java is copied to each output api, but should
-  be global.
+* the generator for notzed.vkregistry uses a lot of miserable
+  write-once perl.
 * the scope and object lifecycle stuff is not really sorted out yet.
+* the config format and features are still being fiddled with.
+* the config file isn't really documented.
+* the build system is still being fiddled with, some of the output
+  directories are mixed up.
+* linux-amd64 only at this point in time.
 
 License
 -------
diff --git a/api/Makefile b/api/Makefile
deleted file mode 100644 (file)
index fbe6563..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-
-CFLAGS=-g -fPIC
-
-all::
-       mkdir -p bin
-
-all:: bin/libapi.so
-
-bin/api.o: api.c api.h
-       $(CC) $(CFLAGS) -c -o $@ $<
-
-bin/libapi.so: bin/api.o
-       $(CC) -o $@ -shared $^
-
-clean:
-       rm -rf bin
-
-.PHONY: clean all
diff --git a/config.make.in b/config.make.in
new file mode 100644 (file)
index 0000000..d682dd1
--- /dev/null
@@ -0,0 +1,49 @@
+
+TARGET ?= linux-amd64
+
+JAVA_HOME ?= /usr/local/jdk
+JAVAFX_HOME ?= /usr/local/javafx-sdk
+FFMPEG_HOME ?= /opt/ffmpeg-5.0
+NATIVEZ_HOME=bin/$(TARGET)
+GCCPLUGINDIR:=$(shell gcc -print-file-name=plugin)
+
+JAVAMODPATH = bin/$(TARGET)/lib
+JAVACFLAGS =
+JMAINFLAGS = -Djava.library.path=bin/linux-amd64/lib:$(FFMPEG_HOME)/lib:/usr/lib64
+
+JAVA ?= $(JAVA_HOME)/bin/java
+JAVAC ?= $(JAVA_HOME)/bin/javac
+JAR ?= $(JAVA_HOME)/bin/jar
+JMOD ?= $(JAVA_HOME)/bin/jmod
+
+CFLAGS = -fPIC -Os -Wall
+CXXFLAGS =-fPIC -Os -Wall
+
+# Linux options
+# USE_SO_VERSION adds the major version to the library open name for ffmpeg libs on linux.
+linux-amd64_CPPFLAGS = \
+       -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \
+       -DUSE_SO_VERSION=1
+linux-amd64_CFLAGS = -fPIC -Os -Wall
+linux-amd64_CC = cc
+linux-amd64_CXXFLAGS = -fPIC -Os -Wall
+linux-amd64_CXX = g++
+linux-amd64_LD = ld
+
+linux-amd64_SO = .so
+linux-amd64_LIB = lib
+
+# Windows options
+windows-amd64_CPPFLAGS = \
+       -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \
+       -DHAVE_ALIGNED_MALLOC \
+       -DWIN32
+windows-amd64_CFLAGS = -Os -Wall
+windows-amd64_CC = x86_64-w64-mingw32-gcc
+windows-amd64_CXXFLAGS = -Os -Wall
+windows-amd64_CXX = x86_64-w64-mingw32-g++
+windows-amd64_LD = x86_64-w64-mingw32-ld
+windows-amd64_LDFLAGS = -Wl,--subsystem,windows
+
+windows-amd64_SO = .dll
+windows-amd64_LIB =
diff --git a/java.make b/java.make
new file mode 100644 (file)
index 0000000..1986121
--- /dev/null
+++ b/java.make
@@ -0,0 +1,416 @@
+#
+# Copyright (C) 2019,2022 Michael Zucchi
+#
+# This is the copyright for java.make
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# General purpose modular java makefile that supports native library
+# compilation directly.  Non-recrusve implementation.
+#
+# Uses metamake programming with some file conventions to implement
+# auto-make-like features.
+
+# Define modules
+# --------------
+# java_MODULES list of java modules to compile.  The sources must
+#              exist in src/<module>/classes.  Resource files are
+#              stored in src/<module>/classes.  Source-code
+#              generators must exist in src/<module>/gen.  Native
+#              libraries must exist in src/<module>/jni.
+
+# native_MODULES list of native-only "modules".
+
+
+# Global variables
+
+# JAVA_HOME            location of jdk.
+# JAVAC                        java compiler to use.  Default is 'javac' on the path.
+# JAVACFLAGS           javac flags applied to all invocations.
+# JAR                  jar command.
+# JARFLAGS             jar flags
+# JMOD                 jmod command.
+# JMODFLAGS            jmod flags.
+# JAVAFLAGS            java flags for run targets
+
+# Module specific variables
+
+# <module>_JDEPMOD     Lists modules which this one depends on.
+
+# <module>_JAVACFLAGS  Extra module-specific flags for each command.
+# <module>_JARFLAGS
+# <module>_JMODFLAGS
+
+# <module>_JAVA                        Java sources.  If not set it is found from src/<module>/classes/(*.java)
+# <module>_RESOURCES           .jar resources.  If not set it is found from src/<module>/classes/(not *.java)
+# <module>_JAVA_GENERATED      Java generated sources.  These must be relative to the package name.
+
+# Variables for use in fragments
+
+# gen.make and jni.make can additionally make use of these variables
+
+# <module>_gendir      Location for files used in Java generation process (per project).
+# <module>_genjavadir  Location where _JAVA_GENERATED .java files will be created (per project).
+# <module>_objdir      Location for c objects (per target).
+# <module>_incdir      Location for output includes, .jmod staging.
+# <module>_libdir      Location for output libraries, .jmod staging.  May point to _bindir.
+# <module>_bindir      Location for output commands, .jmod staging.
+
+# Define libraries
+# ----------------
+
+# Each module can define one or more native libraries.
+
+# These are compiled after the java sources have been compiled as that
+# process also generates any native binding headers.
+
+# <module>_NATIVE_LIBRARIES    list of libraries to build.
+# library names match System.loadLibrary().
+
+# Global variables
+
+# <target>_LDFLAGS
+# <target>_LDLIBS
+# <target>_CPPFLAGS
+# <target>_CFLAGS
+# <target>_CC
+# <target>_CXXFLAGS
+# <target>_CXX
+# SO           shared library suffix
+# LIB          shared library prefix
+
+# Utility functions
+#
+# $(call library-path,<module>,<libname>) will resolve to the library file name.
+
+# Per library variables.
+
+# <library>_SOURCES    .c source files for library.  Paths are relative to src/<module>/native.
+# <library>_CXXSOURCES .c source files for library.  Paths are relative to src/<module>/native.
+# <library>_HEADERS    header files for install/jmod
+# <library>_COMMANDS   commands/bin/scripts for install/jmod
+
+# <library>_LDFLAGS    link flags
+# <library>_LIBADD     extra objects to add to link line
+# <library>_LDLIBS     link libraries
+# <library>_CPPFLAGS   c and c++ pre-processor flags.  "-Isrc/<module>/jni -Ibin/include/<module>" is implicit.
+# <library>_CCFLAGS    c compiler flags
+# <library>_CXXFLAGS   c++ compiler flags
+
+# <library>_DEPENDENCIES       A list of other objects on which this library depends before linking.
+
+# .c and .cc files have dependencies automatically generated
+
+# Targets
+# -------
+
+# make gen             only generate java sources
+# make clean           rm -rf bin
+# make dist            create dist tar in bin/
+# make | make jar      make all jars and jmods
+# make bin             make everything but jars and mods
+
+# Outputs
+# -------
+
+# All intermediate and output files are written to bin/
+
+# This layout is enforced by javac
+#  bin/include/<module>/        .h files from javac -h
+#  bin/modules/<module>/        .class files from javac
+
+# This layout is convenient for netbeans
+#  bin/gen/<module>/gen/       .c, exe files for generator     free use
+#  bin/gen/<module>/classes/   .java files from generator      <module>_JAVA_GENERATED
+
+# Working files
+#  bin/status/                 marker files for makefile
+
+#  bin/<module>/<target>/lib   .so librareies for jmod         <module>_LIBRARIES = libname
+#  bin/<module>/<target>/obj   .o, .d files for library        <libname>_SOURCES
+#  bin/<module>/<target>/include .h files for jmod             <libname>_HEADERS
+#  bin/<module>/<target>/<module>.jmod .jmod module
+
+# Output files
+#  bin/<target>/lib/           modular jar files and shared libraries for GNU/linux dev
+#  bin/<target>/include/       header files for exported shared libraries
+#  bin/<target>/bin/           shared libraries for microsoft dev
+#  bin/<target>/jmods/         jmod files for 'jlink' use.
+
+# ######################################################################
+
+E:=
+S:=$(E) $(E)
+
+# All modules with native code
+java_JMODS=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/jni/jni.make),$(module)))
+# Only modules with no native code
+java_JARS=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/jni/jni.make),,$(module)))
+# Modules with generated java source
+java_JGEN=$(foreach module,$(java_MODULES),$(if $(wildcard src/$(module)/gen/gen.make),$(module)))
+
+# Define some useful variables before including fragments
+define common_variables=
+$(1)_gendir:=bin/gen/$(1)/gen
+$(1)_genjavadir:=bin/gen/$(1)/classes
+$(1)_objdir:=bin/$(1)/$(TARGET)/obj
+$(1)_incdir:=bin/$(1)/$(TARGET)/include
+$(1)_libdir:=$$(if $$(filter windows-%,$(TARGET)),bin/$(1)/$(TARGET)/bin,bin/$(1)/$(TARGET)/lib)
+$(1)_bindir:=bin/$(1)/$(TARGET)/bin
+ifndef $(1)_SCRIPTS
+$(1)_SCRIPTS := $$(shell find src/$(1)/bin src/$(1)/$(TARGET)/bin -type f 2>/dev/null)
+endif
+ifndef $(1)_DATA
+$(1)_DATA := $$(shell find src/$(1)/lib src/$(1)/$(TARGET)/lib -type f 2>/dev/null)
+endif
+endef
+
+define java_variables=
+ifndef $(1)_JAVA
+$(1)_JAVA := $$(shell find src/$(1)/classes -type f -name '*.java')
+endif
+ifndef $(1)_RESOURCES
+$(1)_RESOURCES := $$(shell find src/$(1)/classes -type f \! -name '*.java')
+endif
+endef
+
+java_libdir:=$(if $(filter windows-%,$(TARGET)),bin/$(TARGET)/bin,bin/$(TARGET)/lib)
+java_bindir:=bin/$(TARGET)/bin
+java_jardir:=bin/$(TARGET)/lib
+java_incdir:=bin/$(TARGET)/include
+java_jmoddir:=bin/$(TARGET)/jmods
+
+$(foreach module,$(java_MODULES),$(eval $(call java_variables,$(module))))
+$(foreach module,$(java_MODULES) $(native_MODULES),$(eval $(call common_variables,$(module))))
+
+# ######################################################################
+
+all: jar
+bin:
+gen:
+native:
+
+.PHONY: all clean jar bin gen $(java_MODULES)
+clean:
+       rm -rf bin
+
+include $(foreach module,$(java_MODULES),$(wildcard src/$(module)/gen/gen.make))
+include $(foreach module,$(java_MODULES) $(native_MODULES),$(wildcard src/$(module)/native/native.make))
+
+# ######################################################################
+# Java
+# ######################################################################
+
+define java_targets=
+# Rules for module $(1)
+$(1)_JAVA_generated = $$(addprefix $$($(1)_genjavadir)/,$$($(1)_JAVA_GENERATED))
+
+bin/status/$(1).data: $$($(1)_RESOURCES)
+bin/status/$(1).classes: $(patsubst %,bin/status/%.classes,$(filter $($(1)_JDEPMOD), $(java_MODULES))) $$($(1)_JAVA) $$($(1)_JAVA_generated)
+bin/status/$(1).scripts: $$($(1)_DATA) $$($(1)_SCRIPTS)
+jar $(1): $(java_jardir)/$(1).jar $(java_jmoddir)/$(1).jmod bin/status/$(1).scripts $($(1)_JDEPMOD)
+bin: bin/status/$(1).classes bin/status/$(1).data bin/status/$(1).scripts
+sources: $(java_jardir)/$(1)-sources.zip
+gen: $$($(1)_JAVA_generated)
+
+# Create modular jar
+$(java_jardir)/$(1).jar: bin/status/$(1).classes bin/status/$(1).data
+       @install -d $$(@D)
+       $(JAR) cf $$@ \
+         $(JARFLAGS) $$($(1)_JARFLAGS) \
+         -C bin/modules/$(1) .
+
+# Create a jmod
+$(java_jmoddir)/$(1).jmod: bin/status/$(1).classes bin/status/$(1).data
+       rm -f $$@
+       @install -d $$(@D)
+       $$(JMOD) create \
+         $$(JMODFLAGS) $$($(1)_JMODFLAGS) \
+         --target-platform $(TARGET) \
+         --class-path bin/modules/$(1) \
+         $$(if $$(wildcard bin/$(1)/$(TARGET)/include),--header-files bin/$(1)/$(TARGET)/include) \
+         $$(if $$(wildcard src/$(1)/legal),--legal-notices src/$(1)/legal) \
+         $$(if $$(wildcard bin/$(1)/$(TARGET)/bin),--cmds bin/$(1)/$(TARGET)/bin) \
+         $$(if $$(wildcard bin/$(1)/$(TARGET)/lib),--libs bin/$(1)/$(TARGET)/lib) \
+         $$@
+
+# Create an IDE source zip, paths have to match --module-source-path
+$(java_jardir)/$(1)-sources.zip: bin/status/$(1).classes
+       @install -d $$(@D)
+       jar -c -f $$@ -M \
+               $$(patsubst src/$(1)/classes/%,-C src/$(1)/classes %,$$(filter src/$(1)/classes/%,$$($(1)_JAVA))) \
+               $$(patsubst bin/gen/$(1)/classes/%,-C bin/gen/$(1)/classes %,$$(filter bin/gen/$(1)/classes/%,$$($(1)_JAVA)))
+
+endef
+
+#$(foreach module,$(java_MODULES),$(info $(call java_targets,$(module))))
+$(foreach module,$(java_MODULES),$(eval $(call java_targets,$(module))))
+
+# ######################################################################
+# Global pattern rules
+
+# Stage resources
+bin/status/%.data:
+       @install -d $(@D)
+       for data in $(patsubst src/$*/classes/%,"%",$($*_RESOURCES)) ; do \
+               install -vDC "src/$*/classes/$$data" "bin/modules/$*/$$data" || exit 1 ; \
+       done
+       touch $@
+
+# Stage scripts and data
+bin/status/%.scripts:
+       @install -d $(@D)
+       for data in $(patsubst src/$*/lib/%,"%",$($*_DATA)) ; do \
+               install -vDC -m 0644 "src/$*/lib/$$data" "bin/$(TARGET)/lib/$$data" || exit 1 ; \
+       done
+       for data in $(patsubst src/$*/bin/%,"%",$($*_SCRIPTS)) ; do \
+               install -vDC -m 0755 "src/$*/bin/$$data" "bin/$(TARGET)/bin/$$data" || exit 1 ; \
+       done
+       touch $@
+
+# Compile one module.
+bin/status/%.classes:
+       @install -d $(@D)
+       $(JAVAC) \
+               --module-source-path "src/*/classes:bin/gen/*/classes" \
+               $(if $(JAVAMODPATH),--module-path $(subst $(S),:,$(JAVAMODPATH))) \
+               $(JAVACFLAGS) $($*_JAVACFLAGS) \
+               -d bin/modules \
+               -m $* \
+               $($*_JAVA) $($*_JAVA_generated)
+       touch $@
+
+# setup run-* targets
+define run_targets=
+run-$1/$2: $1
+       LD_LIBRARY_PATH=$(FFMPEG_HOME)/lib \
+       $(JAVA) \
+               $(if $(JAVAMODPATH) $($1_JAVAMODPATH),--module-path $(subst $(S),:,$(JAVAMODPATH) $($1_JAVAMODPATH))) \
+               $(JMAINFLAGS) $($1_JMAINFLAGS) \
+               -m $1/$2 \
+               $(ARGV)
+.PHONY: run-$1/$2
+endef
+
+#$(foreach module,$(java_MODULES),$(foreach main,$($(module)_JMAIN),$(info $(call run_targets,$(module),$(main)))))
+$(foreach module,$(java_MODULES),$(foreach main,$($(module)_JMAIN),$(eval $(call run_targets,$(module),$(main)))))
+
+# ######################################################################
+# nativez jdk.foreign export tool via _API variables
+# ######################################################################
+
+# <module>_API                 List of api's to include
+# <module>_APIFLAGS            Extra flags to pass to export-api for every api in module
+# <module>_<api>_APIFLAGS      Extra flags to pass to export-api for each api
+
+define export_targets=
+gen: bin/status/$2.export
+bin/status/$1.classes: bin/status/$2.export
+bin/status/$2.export: src/$1/gen/$2.api src/$1/gen/$2.h $(NATIVEZ_HOME)/bin/export-api
+       mkdir -p bin/gen/$1/gen bin/status
+       $(NATIVEZ_HOME)/bin/export-api -w bin/gen/$1/gen -d bin/gen/$1/classes $($1_APIFLAGS) $($1_$2_APIFLAGS) $$<
+       touch $$@
+endef
+
+#$(foreach module,$(java_MODULES),$(if $($(module)_API),$(foreach api,$($(module)_API),$(info $(call export_targets,$(module),$(api))))))
+$(foreach module,$(java_MODULES),$(foreach api,$($(module)_API),$(eval $(call export_targets,$(module),$(api)))))
+
+# ######################################################################
+# C and c++ native library support
+# ######################################################################
+
+SUFFIXES=.c .C .cc
+SO=$($(TARGET)_SO)
+LIB=$($(TARGET)_LIB)
+
+# functions to find cross-module stuff $(call library-path,modname,libname)
+library-path=$($(1)_libdir)/$(LIB)$(2)$(SO)
+library-dir=$($(1)_libdir)/
+
+define native_library=
+# Rule for library $(2) in module $(1)
+$(2)_OBJS = $(patsubst %.c, $($(1)_objdir)/%.o, $($(2)_SOURCES)) \
+       $(patsubst %.cc, $($(1)_objdir)/%.o, $($(2)_CXXSOURCES))
+$(2)_SRCS = $(addprefix src/$(1)/native/,$($(2)_SOURCES))
+$(2)_SO = $($(1)_libdir)/$(LIB)$(2)$(SO)
+
+$($(1)_libdir)/$(LIB)$(2)$(SO): $$($(2)_OBJS) $($(2)_LIBADD) $($(2)_DEPENDENCIES)
+       @install -d $$(@D)
+       $($(TARGET)_CC) -o $$@ -shared \
+               $($(TARGET)_LDFLAGS) $($(2)_LDFLAGS) $$($(2)_OBJS) $($(2)_LIBADD) $($(TARGET)_LDLIBS) $($(2)_LDLIBS)
+
+$(java_libdir)/%: $($(1)_libdir)/%
+       install -DC $$< $$@
+$(java_bindir)/%: $($(1)_bindir)/%
+       install -DC $$< $$@
+$(java_incdir)/%: $($(1)_incdir)/%
+       install -DC $$< $$@
+
+$($(1)_objdir)/%.o: src/$(1)/native/%.c
+       @install -d $$(@D)
+       $($(TARGET)_CC) -Isrc/$(1)/native -Ibin/include/$(1) \
+               $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) \
+               $($(TARGET)_CFLAGS) $($(2)_CFLAGS) -c -o $$@ $$<
+
+$($(1)_objdir)/%.o: src/$(1)/native/%.cc
+       @install -d $$(@D)
+       $($(TARGET)_CXX) -Isrc/$(1)/native -Ibin/include/$(1) \
+               $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) \
+               $($(TARGET)_CXXFLAGS) $($(2)_CXXFLAGS) -c -o $$@ $$<
+
+$($(1)_incdir)/%: src/$(1)/native/%
+       install -DC $$< $$@
+$($(1)_libdir)/%: src/$(1)/native/%
+       install -DC $$< $$@
+
+# auto-dependencies for c files
+$($(1)_objdir)/%.d: src/$(1)/native/%.c
+       @install -d $$(@D)
+       @rm -f $$@
+       @$($(TARGET)_CC) -MM -MT "bin/$(1)/$(TARGET)/obj/$$*.o" -Isrc/$(1)/jni -Ibin/include/$(1) \
+               $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) $$< -o $$@.d 2>/dev/null
+       @sed 's,\($$*\.o\) *:,\1 $$@ : ,g' $$@.d > $$@ ; rm $$@.d
+
+# auto-dependencies for c++ files
+$($(1)_objdir)/%.d: src/$(1)/native/%.cc
+       @install -d $$(@D)
+       @rm -f $$@
+       @$($(TARGET)_CXX) -MM -MT "bin/$(1)/$(TARGET)/obj/$$*.o" -Isrc/$(1)/jni -Ibin/include/$(1) \
+               $($(TARGET)_CPPFLAGS) $($(2)_CPPFLAGS) $$< -o $$@.d
+       @sed 's,\($$*\.o\) *:,\1 $$@ : ,g' $$@.d > $$@ ; rm $$@.d
+
+bin native $(1) $(java_jmoddir)/$(1).jmod: \
+       $($(1)_libdir)/$(LIB)$(2)$(SO) \
+       $(java_libdir)/$(LIB)$(2)$(SO) \
+       $(addprefix $($(1)_incdir)/,$($(2)_HEADERS)) \
+       $(addprefix $(java_incdir)/,$($(2)_HEADERS)) \
+       $(addprefix $($(1)_bindir)/,$($(2)_COMMANDS)) \
+       $(addprefix $(java_bindir)/,$($(2)_COMMANDS)) \
+       $(addprefix $($(1)_libdir)/,$($(2)_LIBRARIES))
+
+$(if $(filter clean dist gen,$(MAKECMDGOALS)),,-include $$($(2)_OBJS:.o=.d))
+endef
+
+#$(foreach module,$(java_MODULES) $(native_MODULES),$(foreach library,$($(module)_NATIVE_LIBRARIES),$(info $(call native_library,$(module),$(library)))))
+$(foreach module,$(java_MODULES) $(native_MODULES),$(foreach library,$($(module)_NATIVE_LIBRARIES),$(eval $(call native_library,$(module),$(library)))))
+
+# ######################################################################
+
+dist:
+       @install -d bin
+       tar cfz bin/$(dist_NAME)-$(dist_VERSION).tar.gz \
+        --transform=s,^,$(dist_NAME)-$(dist_VERSION)/, \
+        config.make.in java.make Makefile src          \
+        $(dist_EXTRA)
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644 (file)
index ebab9dc..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-PLUGINDIR=$(shell gcc -print-file-name=plugin)
-
-CXXFLAGS=-fPIC -Wall -Wno-switch -g
-CPPFLAGS=-I. -I$(PLUGINDIR)/include
-
-all: export.so
-
-export.o: export.cc list.h
-export.so: export.o tree-codes.o
-       $(CC) $(INCLUDE) -shared -fPIC -o $@ $^
-
-clean:
-       rm -f export.o tree-codes.o export.so
-
-.PHONY: clean
diff --git a/src/apigen.pm b/src/apigen.pm
deleted file mode 100644 (file)
index afb5cda..0000000
+++ /dev/null
@@ -1,602 +0,0 @@
-
-package apigen;
-
-use strict;
-use Data::Dumper;
-require genconfig;
-
-my %renameTable = (
-       'studly-caps' => sub { my $s = shift; $s =~ s/(?:^|_)(.)/\U$1/g; return $s; },
-       'camel-case' => sub { my $s = shift; $s =~ s/(?:_)(.)/\U$1/g; return $s; },
-       'upper-leadin' => sub { my $s = shift; $s =~ s/^([^_]+)/\U$1/; return $s; },
-       'identity' => sub {     return $_[0]; },
-       'call' => sub {
-               my $s = shift;
-
-               if ($s =~ m/\(/) {
-                       $s =~ s/u32:|u64:/p/g;
-                       $s =~ s/\$\{([^\}]+)\}/$1/g;
-                       $s =~ s/[\(\)]/_/g;
-                       $s =~ s/^/Call/;
-               }
-               $s;
-       }
-
-);
-
-my %defaultTable = (
-       'struct:<default>' => {
-               name => '<default>',
-               items => [],
-               options => [ 'default=none', 'access=rw' ],
-               regex => qr/^struct:<default>$/,
-               type => 'struct'
-       },
-       'union:<default>' => {
-               name => '<default>',
-               items => [],
-               options => [ 'default=none', 'access=rw' ],
-               regex => qr/^union:<default>$/,
-               type => 'union'
-       },
-       'call:<default>' => {
-               name => '<default>',
-               items => [],
-               options => [ 'rename=call', 'access=r' ],
-               regex => qr/^call:<default>$/,
-               type => 'call'
-       },
-);
-
-# hmm, so do i iterate 'control' and find matches in 'api'
-# or do i iterate 'api' and find matches in 'control'?
-
-sub new {
-       my $class = shift;
-       my $file = shift;
-       my $self = { };
-
-       $self->{api} = genconfig::loadControlFile($file);
-       $self->{index} = {};
-       foreach my $obj (@{$self->{api}}) {
-               $self->{index}->{"$obj->{type}:$obj->{name}"} = $obj;
-       }
-       foreach my $k (keys %defaultTable) {
-               $self->{index}->{$k} = $defaultTable{$k} if !defined($self->{index}->{$k});
-       }
-
-       $self->{data} = {};
-       while ($#_ >= 0) {
-               my $name = shift;
-               my $info = loadAPIFile($name);
-
-               $self->{data} = { %{$self->{data}}, %{$info} };
-       }
-
-       analyseAPI($self);
-       preprocess($self);
-
-       # add phantom 'api' entries for anything
-       foreach my $s (findDependencies($self)) {
-               my $n = "$s->{type}:$s->{name}";
-               my $def = $self->{index}->{"$s->{type}:<default>"};
-
-               die "no default for implicit dependency $n" if (!$def);
-
-               my $obj = {
-                       name => $s->{name},
-                       items => $def->{items},
-                       options => $def->{options},
-                       regex => qr/^$n$/,
-                       type => $def->{type}
-               };
-
-               print " implicit $n\n";
-               push @{$self->{api}}, $obj;
-               $self->{index}->{$n} = $obj;
-       }
-
-       postprocess($self);
-
-       bless $self, $class;
-
-       return $self;
-}
-
-sub renameFunction {
-       my $api = shift;
-       my $name = shift;
-       $renameTable{$name};
-}
-
-# find data objects matching top-level data objects
-# return [ (obj, def) ] pairs in a list
-# if there are multiple matches only return the first
-sub findObjects {
-       my $api = shift;
-       my $type = shift;
-       my @list;
-       my %seen = ();
-
-       #print Dumper($api->{data});
-
-       foreach my $obj (grep { $_->{type} eq $type} @{$api->{api}}) {
-               my $s = $api->{data}->{"$type:$obj->{name}"};
-               print "? $obj->{name}  regex $obj->{regex} s=$s\n";
-               if ($s) {
-                       next if $seen{$s->{name}}++;
-                       push @list, [ $obj, $s ];
-               } else {
-                       foreach my $k (grep { $_ =~ $obj->{regex} } keys(%{$api->{data}})) {
-                               $s = $api->{data}->{$k};
-                               next if $seen{$s->{name}}++;
-                               #print "+  $k\n";
-                               push @list, [ $obj, $s ];
-                       }
-               }
-       }
-
-       return @list;
-}
-
-sub findAllObjects {
-       my $api = shift;
-       my @list;
-
-       foreach my $obj (@{$api->{api}}) {
-               print "? $obj->{name}\n";
-               foreach my $dat (grep { $_->{name} =~ $obj->{regex} } values(%{$api->{data}})) {
-                       #print "+  $dat->{name}\n";
-                       push @list, [ $obj, $dat ];
-               }
-       }
-
-       return @list;
-}
-
-# TODO: check these next few against the one in generate-api,
-
-# find option(s) in an obj or inc
-sub option {
-       my $obj = shift;
-       my $name = shift;
-       my $rx = qr/^$name=/;
-
-       return grep { $_ =~ m/$rx/ } @{$obj->{options}};
-}
-
-# find value of first option of the given name
-sub optionValue {
-       my $name = shift;
-       my $or = shift;
-       my $rx = qr/$name/;
-
-       foreach my $obj (@_) {
-               foreach my $opt (@{$obj->{options}}) {
-                       return $1 if ($opt =~m/^$rx=(.*)/);
-               }
-       }
-       $or;
-}
-
-# look for all matching options of the given name
-# multiple objects are searched, the first one with
-#  the given parameter overrides the rest.
-# name, object, object *
-sub optionValues {
-       my $name = shift;
-       my $rx = qr/$name/;
-       my @list;
-
-       foreach my $obj (@_) {
-               foreach my $opt (@{$obj->{options}}) {
-                       push @list, $1 if ($opt =~m/^$rx=(.*)/);
-               }
-               last if ($#list >= 0);
-       }
-       @list;
-}
-
-# find first occurance of a flag
-sub optionFlag {
-       my $name = shift;
-
-       foreach my $obj (@_) {
-               foreach my $opt (@{$obj->{options}}) {
-                       return 1 if ($opt eq $name);
-               }
-       }
-       0;
-}
-
-# find live/desired items of a given type under a particular object
-# returns list of [ $inc, $field/argument ]
-sub findItems {
-       my $api = shift;
-       my $obj = shift;
-       my $s = shift;
-       my $mode = shift;
-       my %visited = ();
-       my @fields = ();
-
-       #print Dumper($s);
-
-       my @all = @{$s->{items}};
-       my %index;
-
-       foreach my $m (@all) {
-               $index{$m->{name}} = $m;
-       }
-
-       #print "find mode $mode in $s->{name} $s->{type}\n";
-       #print Dumper($s);
-
-       foreach my $inc (grep { $_->{mode} eq $mode } @{$obj->{items}}) {
-               my $d = $index{$inc->{match}};
-
-               if ($d) {
-                       next if $visited{$d->{type}.':'.$d->{name}}++;
-                       push @fields, [ $inc, $d ];
-               } else {
-                       foreach my $d (grep { $_->{name} =~ m/$inc->{regex}/ } @all) {
-                               next if $visited{$d->{type}.':'.$d->{name}}++;
-                               push @fields, [ $inc, $d ];
-                       }
-               }
-       }
-
-       # FIXME: rather than all, use 'struct:<default>' options for fallback
-       if (optionValue('default', 'all', $obj) eq 'all') {
-               foreach my $d (@all) {
-                       next if $visited{$d->{type}.':'.$d->{name}}++;
-                       # TODO: other things might need moving over
-                       my $inc = {};
-                       $inc->{"$mode:rename"} = $obj->{"$mode:rename"} if $obj->{"$mode:rename"};
-                       push @fields, [ $inc, $d ];
-               }
-       }
-
-       return @fields;
-}
-
-sub findAllItems {
-       my $api = shift;
-       my $obj = shift;
-       my $s = shift;
-       my %visited = ();
-       my @fields = ();
-
-       #print Dumper($obj);
-
-       my @all = @{$s->{items}};
-       my %index;
-
-       foreach my $m (@all) {
-               $index{$m->{name}} = $m;
-       }
-
-       foreach my $inc (@{$obj->{items}}) {
-               my $d = $index{$inc->{match}};
-
-               if ($d) {
-                       next if $visited{$d->{type}.':'.$d->{name}}++;
-                       push @fields, [ $inc, $d ];
-               } else {
-                       foreach my $d (grep { $_->{name} =~ m/$inc->{regex}/ } @all) {
-                               next if $visited{$d->{type}.':'.$d->{name}}++;
-                               push @fields, [ $inc, $d ];
-                       }
-               }
-       }
-
-       if (optionValue('default', 'all', $obj) eq 'all') {
-               #print "* add all items\n";
-               foreach my $d (@all) {
-                       next if $visited{$d->{type}.':'.$d->{name}}++;
-                       push @fields, [ $obj, $d ];
-               }
-       }
-
-       return @fields;
-}
-
-# ######################################################################
-
-sub addDependencies {
-       my $api = shift;
-       my $obj = shift;
-       my $s = shift;
-       my $add = shift;
-
-       #print "add deps for '$s->{name}'\n";
-       if ($s->{type} =~ m/^(struct|union)$/) {
-               # include embedded structures always
-               foreach my $d (grep { $_->{type} =~ m/^(struct|union):/ && $_->{deref} =~ m/\[\d+\$\{|^\$\{/ } @{$s->{items}}) {
-                       #print "  embedded $d->{name} $d->{deref}\n";
-                       $add->($d->{type});
-               }
-
-               # include selected fields optionally
-               if ($obj) {
-                       foreach my $i (findAllItems($api, $obj, $s)) {
-                               my ($inc, $d) = @{$i};
-                               #print "  selected $d->{name} $d->{type} $d->{deref}\n";
-                               $add->($d->{type}) if ($d->{type} =~ m/^(struct|union|func|call|enum):/);
-                       }
-               }
-       } elsif ($s->{type} =~ m/^(call|func)/) {
-               # for calls/func need all fields
-               foreach my $d (grep { $_->{type} =~ m/^(struct|union|func|call|enum):/ } @{$s->{items}}, $s->{result}) {
-                       #print "  argument $d->{name} $d->{type} $d->{deref}\n";
-                       $add->($d->{type});
-               }
-       }
-}
-
-# find all extra types used by the api requested
-sub findDependencies {
-       my $api = shift;
-       my %data = %{$api->{data}};
-       my %seen;
-       my %deps;
-       my $setdeps = sub { my $d = shift; $deps{$d} = 1; };
-
-       foreach my $obj (@{$api->{api}}) {
-               if ($obj->{type} eq 'library') {
-                       foreach my $inc (@{$obj->{items}}) {
-                               print "? $inc->{regex}\n";
-                               foreach my $n (grep { $_ =~ m/$inc->{regex}/ } keys %data) {
-                                       my $s = $data{$n};
-
-                                       print "+ $n\n";
-
-                                       $seen{$n}++;
-                                       addDependencies($api, $obj, $s, $setdeps);
-                               }
-                       }
-               } elsif ($obj->{type} =~ m/^(struct|union|call|func|enum|define)$/) {
-                       foreach my $n (grep { $_ =~ m/$obj->{regex}/ } keys %data) {
-                               my $s = $data{$n};
-
-                               $seen{$n}++;
-                               addDependencies($api, $obj, $s, $setdeps);
-                       }
-               }
-       }
-
-       # at this point 'seen' contains everything explicitly requested
-       # and deps is anything else they need but not referenced directly
-       # recursively grab anything else
-
-       my @list = ();
-       my @stack = sort keys %deps;
-       my $pushstack = sub { my $d = shift; push @stack, $d; };
-       while ($#stack >= 0) {
-               my $n = shift @stack;
-               my $s;
-
-               next if $seen{$n}++;
-
-               $s = $data{$n};
-
-               if ($s) {
-                       print "Add referent: $n\n";
-                       addDependencies($api, $api->{index}->{"$s->{type}:<default>"}, $s, $pushstack);
-               } elsif ($n =~ m/^(.*):(.*)$/) {
-                       print "Add anonymous: $n\n";
-                       # type not know, add anonymous
-                       $s = {
-                               name => $2,
-                               type => $1,
-                               size => 0,
-                               items => [],
-                       };
-                       $api->{data}->{$n} = $s;
-               }
-
-               # maybe it should have some skeleton metadata?
-               # depends on where it's used i suppose
-               push @list, $s;
-       }
-
-       print "Added ".($#list+1)." dependencies\n";
-       return @list;
-}
-
-# ######################################################################
-
-sub loadAPIFile {
-       my $file = shift;
-       my $info;
-
-       unless ($info = do $file) {
-               die "couldn't parse $file: $@"  if $@;
-               die "couldn't import $file: $!" unless defined $info;
-               die "couldn't run $file"                unless $info;
-       }
-
-       return $info;
-}
-
-sub parseRename {
-       my $how = shift;
-       my $rename = $renameTable{'identity'};
-
-       foreach my $n (split /,/,$how) {
-               my $old = $rename;
-               my $new = $renameTable{$n};
-
-               if ($n =~ m@^s/(.*)/(.*)/$@) {
-                       my $rx = qr/$1/;
-                       my $rp = $2;
-                       $rename = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
-               } elsif ($new) {
-                       $rename = sub { my $s=shift; $s = $old->($s); return $new->($s); };
-               } else {
-                       my $x = $n;
-                       $rename = sub { return $x; };
-               }
-       }
-       $rename;
-}
-
-# take a signature-name and convert it to mangled java name
-sub renameCall {
-       my $new = shift;
-
-       if ($new =~ m/\(/) {
-               $new =~ s/u32:|u64:/p/g;
-               $new =~ s/\$\{([^\}]+)\}/$1/g;
-               $new =~ s/[\(\)]/_/g;
-               $new =~ s/^/Call/;
-       }
-       $new;
-}
-
-# pre-process {data}
-sub preprocess {
-       my $api = shift;
-
-       # Find any anonymous types and add them in
-       my %anonymous = ();
-       foreach my $s (values %{$api->{data}}) {
-               # FIXME: just do this in export.cc and export-defines
-               $s->{items} = $s->{fields} if $s->{fields};
-               $s->{items} = $s->{arguments} if $s->{arguments};
-               $s->{items} = $s->{values} if $s->{values};
-
-               foreach my $m (grep { $_->{type} =~ m/struct:|union:/} @{$s->{items}}) {
-                       $anonymous{$m->{type}} = 1 if !defined($api->{data}->{$m->{type}});
-               }
-       }
-
-       foreach my $k (sort keys %anonymous) {
-               print " anon $k\n";
-               if ($k =~ m/^(.*):(.*)$/) {
-                       $api->{data}->{$k} = {
-                               name => $2,
-                               type => $k,
-                               size => 0,
-                               items => [],
-                       };
-               }
-       }
-
-       my $rename = {};
-       foreach my $i (findObjects($api, 'struct')) {
-               my ($obj, $dat) = @{$i};
-
-               if ($obj->{rename}) {
-                       my $old = $dat->{name};
-                       my $new = $obj->{rename}->($old);
-                       my $oldkey = $obj->{type}.':'.$old;
-                       my $newkey = $obj->{type}.':'.$new;
-
-                       $rename->{$oldkey} = $newkey;
-               }
-       }
-
-       $api->{rename} = $rename;
-}
-
-# preprocess {api}
-sub analyseAPI {
-       my $api = shift;
-
-       # Note that type:name regexes always start at the beginning
-
-       foreach my $obj (@{$api->{api}}) {
-               if ($obj->{name} =~ m@^/(.*)/$@) {
-                       $obj->{regex} = qr/^$obj->{type}:$1/;
-               } else {
-                       $obj->{regex} = qr/^$obj->{type}:$obj->{name}$/;
-               }
-
-               foreach my $opt (@{$obj->{options}}) {
-                       if ($opt =~ m/^(.*:?rename)=(.*)$/) {
-                               $obj->{$1} = parseRename($2);
-                       }
-               }
-
-               my $defmode = ($obj->{type} eq 'library' ? 'func' : 'field');
-               foreach my $inc (@{$obj->{items}}) {
-                       my $match = $inc->{match};
-                       my $mode = $defmode;
-
-                       if ($inc->{match} =~ m/^(.*):(.*)$/) {
-                               $match = $2;
-                               $mode = $1;
-                       }
-
-                       $inc->{mode} = $mode;
-                       if ($match =~ m@^/(.*)/$@) {
-                               $inc->{regex} = $mode ne 'field' ? qr/$mode:$1/ : qr/$1/;
-                       } else {
-                               $inc->{regex} = $mode ne 'field' ? qr/^$mode:$match$/ : qr/^$match$/;
-                       }
-
-                       foreach my $opt (@{$inc->{options}}) {
-                               if ($opt =~ m/^rename=(.*)$/) {
-                                       #print "option $opt ".Dumper($inc);
-                                       $inc->{"$mode:rename"} = parseRename($1);
-                               }
-                       }
-
-                       $inc->{"$mode:rename"} = $obj->{"$mode:rename"} if (!(defined($inc->{"$mode:rename"})) && defined($obj->{"$mode:rename"}));
-               }
-       }
-}
-
-# transfer info from {api} to {data}
-sub postprocess {
-       my $api = shift;
-       my $seen = {};
-       my %data = %{$api->{data}};
-
-       # apply func stuff
-       foreach my $obj (grep {$_->{type} eq 'func'} @{$api->{api}}) {
-               my @list;
-
-               if ($api->{data}->{$obj->{name}}) {
-                       push @list, ${data}{$obj->{name}};
-               } else {
-                       push @list, map { $data{$_} } grep { $_ =~ m/$obj->{regex}/ } keys %data;
-               }
-
-               foreach my $s (@list) {
-                       my $isconstructor = optionFlag('constructor', $obj);
-                       my $isstatic = optionFlag('static', $obj);
-
-                       $s->{constructor} = $isconstructor if $isconstructor;
-                       $s->{static} = $isstatic if $isstatic;
-                       $s->{rename} = $obj->{"$obj->{type}:rename"} ? $obj->{"$obj->{type}:rename"}->($s->{name}) : $s->{name};
-
-                       foreach my $inc (@{$obj->{items}}) {
-                               my @items;
-
-                               my $array = optionFlag('array', $inc, $obj);
-                               my $arraysize = optionValue('array-size', undef, $inc);
-                               my $instance = optionFlag('instance', $inc);
-                               my $success = optionValue('success', undef, $inc);
-
-                               if ($inc->{match} eq '<result>') {
-                                       push @items, $s->{result};
-                               } else {
-                                       @items = grep { $_->{name} =~ m/$inc->{regex}/ } @{$s->{items}};
-                               }
-
-                               foreach my $m (@items) {
-                                       $m->{array} = $array if $array;
-                                       $m->{arraysize} = $arraysize if defined($arraysize);
-                                       $m->{instance} = $instance if $instance;
-                                       $m->{success} = $success if defined($success);
-                               }
-                       }
-                       print Dumper({func=>$s, obj=>$obj});
-               }
-
-       }
-
-}
-
-1;
diff --git a/src/generate-native b/src/generate-native
deleted file mode 100755 (executable)
index b17434c..0000000
+++ /dev/null
@@ -1,1735 +0,0 @@
-#!/usr/bin/perl
-
-# -*- Mode:perl; perl-indent-level:4;tab-width:4; -*-
-
-# TODO: global defaults and/or pattern matching on api targets, partially done
-# TODO: perhaps an option which just dumps everything it finds at some level
-# TODO: the code here isn't a complete mess but could be improved
-#       - parameterise some functions
-# TODO: map int to boolean where appropriate
-# TODO: arrays with specified lengths passed as arguments could be size-checked in function stubs.
-# TODO: error codes -> exceptions?
-# TODO: auto-loading of libraries (library xx load=blah) option.
-# TODO: rename objects via .api file
-
-use Data::Dumper;
-use File::Basename;
-use File::Path qw(make_path);
-
-$scriptPath = dirname(__FILE__);
-
-$package = "";
-$output = "bin";
-$verbose = 0;
-$apidef = "api.def";
-
-# usage
-#  -t package    target package
-#  -d directory  output root
-#  -v            verbose
-#  -a datafile   add datafile to the dataset, can be from export.so or export-defines, etc
-
-my %typeSizes = (
-       i8 => 'byte', u8 => 'byte',
-       i16 => 'short', u16 => 'short',
-       i32 => 'int', u32 => 'int',
-       i64 => 'long', u64 => 'long',
-       f32 => 'float',
-       f64 => 'double',
-       );
-
-# or just use some formatting function table
-my %defineType = (
-       %typeSizes,
-       string => 'String'
-       );
-
-my %definePrefix = (
-       i8 => '(byte)',
-       u8 => '(byte)',
-       i16 => '(short)',
-       u16 => '(short)',
-       string => '"'
-       );
-my %defineSuffix = (
-       u64 => 'L',
-       i64 => 'L',
-       f32 => 'f',
-       string => '"'
-       );
-
-my %intSizes = ( 8 => 'byte', 16 => 'short', 32 => 'int', 64 => 'long' );
-my %typePrimitive = (
-       "byte" => 8,
-       "short" => 16,
-       "int" => 32,
-       "long" => 64,
-       "float" => 32,
-       "double" => 64,
-       );
-
-my %typeSignature = (
-       "byte" => "B",
-       "short" => "S",
-       "int" => "I",
-       "long" => "J",
-       "float" => "F",
-       "double" => "D",
-       "MemorySegment" => "Ljdk/incubator/foreign/MemorySegment;",
-       "MemoryAddress" => "Ljdk/incubator/foreign/MemoryAddress;",
-       );
-
-my %renameTable = (
-       'studly-caps' => sub { my $s = shift; $s =~ s/(?:^|_)(.)/\U$1/g; return $s; },
-       'camel-case' => sub { my $s = shift; $s =~ s/(?:_)(.)/\U$1/g; return $s; },
-       'upper-leadin' => sub { my $s = shift; $s =~ s/^([^_]+)/\U$1/; return $s; },
-       'identity' => sub {     return $_[0]; },
-       );
-
-my %data = ();
-
-while (@ARGV) {
-       my $cmd = shift;
-
-       if ($cmd eq "-t") {
-               $package = shift;
-    } elsif ($cmd eq "-d") {
-               $output = shift;
-    } elsif ($cmd eq "-v") {
-               $verbose++;
-    } elsif ($cmd eq "-a") {
-               my $file = shift;
-               my $info;
-
-               print "Add $file\n";
-
-               unless ($info = do $file) {
-                       die "couldn't parse $file: $@" if $@;
-                       die "couldn't import $file: $!"    unless defined $info;
-                       die "couldn't run $file"       unless $info;
-               }
-
-               %data = (%data, %{$info});
-       } else {
-               $apidef = $cmd;
-       }
-}
-
-my $api = loadControlFile($apidef);
-
-analyseAPI($api);
-analyseAndFixTypes($api);
-
-if (0) {
-       $s = $data{'struct:AVCodecContext'};
-       $obj = findAPIObject($api, 'struct', $s->{name});
-       print Dumper($obj);
-
-       foreach $m (@{$s->{fields}}) {
-               $inc = findAPIField($obj, $m->{name});
-               print " $m->{name} - $inc\n";
-       }
-}
-
-my $toDump = analyseDependencies(\%data, findRoots($api));
-
-if ($verbose > 1) {
-       print "Using:n";
-       print Dumper(\%data);
-       print "API:n";
-       print Dumper($api);
-}
-
-# find api.struct that matches a given struct name
-sub findAPIObject {
-       my $api = shift;
-       my $type = shift;
-       my $name = shift;
-
-       print "find api for $type:$name\n" if $verbose;
-       foreach $obj ( @{$api->{$type}} ) {
-               next if $obj->{name} eq '<default>';
-               print " $obj->{name} ? $name\n" if $verbose;
-               if ($obj->{name} =~ m@/(.*)/@) {
-                       my $rx = qr/$1/;
-                       return $obj if ($name =~ m/$rx/);
-               } elsif ($obj->{name} eq $name) {
-                       return $obj;
-               }
-       }
-
-       print " -> fallback=$type:<default>\n" if $verbose && $api->{"$type:<default>"};
-       return $api->{"$type:<default>"};
-}
-
-sub findAPIField {
-       my $obj = shift;
-       my $name = shift;
-
-       foreach $inc (grep { $_->{mode} eq 'field' } @{$obj->{items}}) {
-               return $inc if $name =~ m/$inc->{regex}/;
-       }
-}
-
-# find all directly referenced types and field types from api
-sub findRoots {
-       my $api = shift;
-       my %seen;
-
-       foreach $obj ( @{$api->{library}} ) {
-               foreach $inc ( @{$obj->{items}} ) {
-                       if ($inc->{mode} eq 'func') {
-                               my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
-
-                               foreach $func (@list) {
-                                       $seen{"func:$func->{name}"} ++;
-                               }
-                       } elsif ($inc->{mode} eq 'call') {
-                               my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
-
-                               foreach $func (@list) {
-                                       $seen{"call:$func->{name}"} ++;
-                               }
-                       }
-               }
-       }
-
-       # all defines included
-       foreach $def ( @{$api->{define}} ) {
-               $seen{"define:$def->{name}"} ++;
-       }
-
-       foreach $obj ( @{$api->{struct}}, @{$api->{func}}) {
-               my @list;
-
-               if ($obj->{name} =~ m@/(.*)/@) {
-                       my $rx = "$obj->{type}:$1";
-                       push @list, grep { $_ =~ m/$rx/ } keys %data;
-               } else {
-                       push @list, "$obj->{type}:$obj->{name}";
-               }
-
-               foreach $n (@list) {
-                       $seen{$n} ++;
-               }
-
-       }
-
-       delete $seen{'struct:<default>'};
-       delete $seen{'func:<default>'};
-       delete $seen{'call:<default>'};
-
-       my @list = sort keys %seen;
-
-       return \@list;
-}
-
-# analyse dependencies of the supplied roots
-# only fields actually referenced in the api.def file are included
-# \%seen = \%data, \@roots
-sub analyseDependencies {
-       my $data = shift;
-       my @roots = @{shift @_};
-       my %seen;
-
-       print "Finding dependencies of $#roots roots\n";
-
-       while ($#roots >= 0) {
-               my $name = shift @roots;
-               my $s = $data{$name};
-               my @list;
-
-               next if $seen{$name}++;
-
-               print "visit $name $s->{name}\n" if $verbose;
-
-               if ($s->{type} =~ m/struct|union/) {
-                       my $obj = findAPIObject($api, 'struct', $s->{name});
-                       if ($obj->{default} eq 'all') {
-                               push @list, @{$s->{fields}};
-                       } else {
-                               push @list, grep { findAPIField($obj, $_->{name}) } @{$s->{fields}};
-                       }
-               } elsif ($s->{type} =~ m/func|call/) {
-                       @list = @{$s->{arguments}};
-                       push @list, $s->{result};
-               }
-
-               foreach $m (@list) {
-                       my $type = $m->{type};
-
-                       print " item $m->{name} '$type'\n" if $verbose;
-                       if ($m->{ctype} =~ m/enum (.*)/) {
-                               $type = "enum:$1";
-                       }
-
-                       push @roots,$type if $data{$type};
-               }
-       }
-
-       foreach $name (sort grep { m/:/ } keys %seen) {
-               print " $name\n";
-       }
-       print "\n";
-       return \%seen;
-}
-
-# find which api->thing->items applies to a given field name, if any
-sub findAPIItem {
-       my $api = shift;
-       my $type = shift;
-       my $target = shift;
-       my $mode = shift;
-       my $name = shift;
-
-       #print "search for $target.$name in $type.$mode\n";
-       # what about exclude?
-       foreach $obj ( @{$api->{$type}} ) {
-               #print " check $obj->{name} against $target =~ /$obj->{regex}/\n";
-               #if ($obj->{name} eq $target) {
-               if ($target =~ m/$obj->{regex}/) {
-                       #print "  found $obj->{name}\n";
-                       foreach $inc (grep { $_->{mode} eq $mode } @{$obj->{items}}) {
-                               #print "  check $inc->{match}\n";
-                               return $inc if $name =~ m/$inc->{regex}/;
-                       }
-               }
-       }
-}
-
-sub analyseAPI {
-       my $api = shift;
-
-       foreach $obj ( @{$api->{struct}}, @{$api->{library}}, @{$api->{func}}) {
-               $obj->{access} = 'rw';
-               $obj->{default} = 'all';
-               $obj->{rename} = $renameTable{'identity'};
-               $obj->{'func:rename'} = $renameTable{'identity'};
-               $obj->{'field:rename'} = $renameTable{'identity'};
-
-               if ($obj->{name} =~ m@/(.*)/@) {
-                       $obj->{regex} = qr/$1/;
-               } else {
-                       $obj->{regex} = qr/^$obj->{name}$/;
-               }
-
-               foreach $o (@{$obj->{options}}) {
-                       if ($o =~ m/^default=(none|all)$/) {
-                               $obj->{default} = $1;
-                       } elsif ($o =~ m/^access=([rwi]+)$/) {
-                               $obj->{access} = $1;
-                       } elsif ($o =~ m/^success=(.*)$/) {
-                               # for functions?
-                               $obj->{success} = $1;
-                       } elsif ($o =~ m@^(rename|field:rename|func:rename)=(.*)@) {
-                               my $target = $1;
-
-                               foreach $n (split /,/,$2) {
-                                       my $old = $obj->{$target};
-                                       my $new = $renameTable{$n};
-
-                                       if ($n =~ m@^s/(.*)/(.*)/$@) {
-                                               my $rx = qr/$1/;
-                                               my $rp = $2;
-                                               $obj->{$target} = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
-                                       } elsif ($new) {
-                                               $obj->{$target} = sub { my $s=shift; $s = $old->($s); return $new->($s); };
-                                       }
-                               }
-                       }
-               }
-
-               my $defmode = $obj->{type} eq 'library' ? 'func' : 'field';
-
-               foreach $inc (@{$obj->{items}}) {
-                       if ($inc->{match} =~ m@^(field|func|define|struct|enum|call):/(.*)/$@) {
-                               $inc->{regex} = qr/$2/;
-                               $inc->{mode} = $1; # ?? "$1-include";
-                       } elsif ($inc->{match} =~ m@^(field|func|define|struct|enum|call):(.*)$@) {
-                               $inc->{regex} = qr/^$2$/;
-                               $inc->{mode} = $1;
-                       } elsif ($inc->{match} =~ m@^/(.*)/$@) {
-                               $inc->{regex} = qr/$1/;
-                               $inc->{mode} = $defmode;
-                       } else {
-                               $inc->{regex} = qr/^$inc->{match}$/;
-                               $inc->{mode} = $defmode;
-                       }
-
-                       $inc->{rename} = $renameTable{'identity'};
-                       $inc->{scope} = 'static' if $obj->{type} eq 'library';
-
-                       # maybe depends on mode above
-                       foreach $o (@{$inc->{options}}) {
-                               if ($o =~ m/^access=([rwi])+/) {
-                                       $inc->{access} = $1;
-                               } elsif ($o =~ m/^rename=(.*)/) {
-                                       foreach $n (split /,/,$1) {
-                                               my $old = $inc->{rename};
-                                               my $new = $renameTable{$n};
-
-                                               if ($n =~ m@^s/(.*)/(.*)/$@) {
-                                                       my $rx = qr/$1/;
-                                                       my $rp = $2;
-                                                       $inc->{rename} = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
-                                               } elsif ($new) {
-                                                       $inc->{rename} = sub { my $s=shift; $s = $old->($s); return $new->($s); };
-                                               } else {
-                                                       my $x = $n;
-                                                       $inc->{rename} = sub { return $x; };
-                                               }
-                                       }
-                               } elsif ($o =~ m/^array-size=(.*)/) {
-                                       $inc->{'array_size'} = $1;
-                               } elsif ($o =~ m/^array$/) {
-                                       $inc->{'array'} = 1;
-                               } elsif ($o =~ m/^implied=(.*)$/) {
-                                       $inc->{'implied'} = $1;
-                               } elsif ($o =~ m/^instance=(.*)/) {
-                                       $inc->{instance} = $1;
-                               } elsif ($o =~ m/^static$/) {
-                                       $inc->{scope} = 'static';
-                               } elsif ($o =~ m/^constructor=(.*)$/) {
-                                       $inc->{constructor} = $1;
-                               } elsif ($o =~ m/^constructor-result=(.*)$/) {
-                                       $inc->{constructor_result} = $1;
-                               } elsif ($o =~ m/^result_code$/) {
-                                       $inc->{result_code} = 1;
-                               } elsif ($o =~ m/^success=(.*)$/) {
-                                       $inc->{success} = $1;
-                               }
-                               # exclude mode, etc
-                       }
-
-                       $inc->{rename} = $obj->{"$inc->{mode}:rename"} if $inc->{rename} == $renameTable{'identity'} && $obj->{"$inc->{mode}:rename"};
-               }
-
-               if ($obj->{name} eq '<default>') {
-                       $api->{"$obj->{type}:<default>"} = $obj;
-               }
-       }
-
-       $api->{'call:<default>'} = { rename => $renameTable{'identity'}, scope => 'static'} if !$api->{'call:<default>'};
-}
-
-# take a signature-name and fix it
-sub fixAnonymousCall {
-       my $new = shift;
-
-       $new =~ s/u32:/p/g;
-       $new =~ s/u64:/p/g;
-       $new =~ s/\$\{([^\}]+)\}/$1/g;
-       $new =~ s/[\(\)]/_/g;
-       $new =~ s/^/Call/;
-       $new;
-}
-
-# anonymous structs
-#  the exporter doesn't output anonymous structs as they might
-#  just be forward references.  this fills in any missing types.
-# anonymouse calls
-#  anonymous functions are referenced by signature, convert any to an identifier
-# typeInfo
-#  setup typeInfo for all type references - memebers, fields, return values
-sub analyseAndFixTypes {
-       my $api = shift;
-       my %rename = ();
-
-       # pass 1, fix call definition names and keys, other renames
-       foreach $old (keys %data) {
-               if ($old =~ m/^call:(.*\(.*)/) {
-                       $rename{$old} = 'call:'.fixAnonymousCall($1);
-               } elsif ($old =~ m/^(call|func|struct):(.*)$/) {
-                       my $obj = findAPIObject($api, $1, $2);
-
-                       if ($obj && $obj->{rename} != $renameTable{'identity'}) {
-                               $rename{$old} = $1.':'.$obj->{rename}->($2);
-                       }
-               }
-       }
-
-       foreach $old (sort keys %rename) {
-               my $new = $rename{$old};
-               my $c;
-
-               $c = delete $data{$old};
-               print "rename $old -> $new\n";
-               $c->{name} = $new;
-               $c->{name} =~ s/^.*://;
-
-               $data{$new} = $c;
-       }
-
-       # pass 2 add typeinfo and anonymous types, fix call types
-       foreach $n (keys %data) {
-               my $s = $data{$n};
-               my @list;
-
-               if ($s->{type} =~ m/struct|union/) {
-                       @list = @{$s->{fields}};
-               } elsif ($s->{type} =~ m/func|call/) {
-                       @list = @{$s->{arguments}};
-                       push @list, $s->{result};
-               }
-
-               foreach $a (@list) {
-                       if ($a->{type} =~ m/(struct|union):(.*)/ && !defined($data{$a->{type}})) {
-                               my $t = $1;
-                               my $n = $2;
-                               my $obj = findAPIObject($api, $t, $n);
-
-                               if ($obj && $obj->{rename} != $renameTable{'identity'}) {
-                                       $n = $obj->{rename}->($n);
-                                       $rename{$a->{type}} = "$t:$n";
-                                       $a->{type} = "$t:$n";
-                               }
-
-                               if (!defined($data{$a->{type}})) {
-                                       print "Add anonymous $1 $2\n";
-                                       $data{$a->{type}} = {
-                                               name => $n,
-                                               type => $t,
-                                               size => 0
-                                       };
-                               }
-                       } else {
-                               $a->{type} = $rename{$a->{type}} if ($rename{$a->{type}});
-                       }
-
-                       # must be last
-                       $a->{typeInfo} = analyseTypeInfo($s, $a);
-               }
-       }
-
-       # pass 3 create java signatures
-       foreach $n (keys %data) {
-               my $s = $data{$n};
-
-               if ($s->{type} =~ m/^(call|func)$/) {
-                       $s->{signature} = formatSignature($s);
-               }
-       }
-}
-
-sub isVoid {
-       my $m = shift @_;
-
-       return $m->{type} eq 'void' && !$m->{deref};
-}
-
-# format a single layout type item for non-bitfield types
-# type - type record that contains type and deref
-# withName - '.withName()' - empty, or really any other MemoryLayout adjustment functions.
-sub formatTypeLayout {
-       my $m = shift @_;
-       my $withName = shift @_;
-       my $desc = "";
-
-       if ($m->{deref} =~ m/^(u64|u32):/) {
-               $desc .= "Memory.POINTER$withName";
-       } elsif ($m->{type} =~ m/^([iuf]\d+)$/) {
-               if ($m->{deref} =~ m/\[(\d*)u64:.*\]/) {
-                       $desc .= "MemoryLayout.sequenceLayout($1, Memory.POINTER)$withName";
-               } elsif ($m->{deref} =~ m/\[(\d*).*\]/) {
-                       $desc .= "MemoryLayout.sequenceLayout($1, Memory.".uc($typeSizes{$m->{type}}).")$withName";
-               } else {
-                       $desc .= 'Memory.'.uc($typeSizes{$m->{type}})."$withName";
-               }
-       } elsif ($m->{type} =~ m/^(struct|union):(.*)/) {
-               my $type = $2;
-               if ($m->{deref} =~ m/\[(\d*)u64:.*\]/) {
-                       $desc .= "MemoryLayout.sequenceLayout($1, Memory.POINTER)$withName";
-               } elsif ($m->{deref} =~ m/\[(\d*).*\]/) {
-                       $desc .= "MemoryLayout.sequenceLayout($1, $type.LAYOUT)$withName";
-               } else {
-                       $desc .= "$type.LAYOUT$withName";
-               }
-       } else {
-               print Dumper($m);
-               die ("unknown type");
-       }
-
-       return $desc;
-}
-
-sub formatFunctionDescriptor {
-       my $c = shift;
-       my @arguments = @{$c->{arguments}};
-       my $result = $c->{result};
-       my $desc;
-       my $index = 0;
-
-       if (!isVoid($result)) {
-               $desc = "FunctionDescriptor.of(\n ";
-               $desc .= formatTypeLayout($result);
-               $index = 1;
-       } else {
-               $desc = "FunctionDescriptor.ofVoid(\n ";
-       }
-
-       foreach $m (@arguments) {
-               $desc .= ",\n " if ($index++ > 0);
-               $desc .= formatTypeLayout($m, ".withName(\"$m->{name}\")");
-       }
-
-       $desc .= "\n)";
-
-       return $desc;
-}
-
-sub formatSignature {
-       my $c = shift @_;
-       my @arguments = @{$c->{arguments}};
-       my $desc = '(';
-
-       foreach $m (@arguments) {
-               $desc .= $typeSignature{$m->{typeInfo}->{carrier}};
-       }
-       $desc .= ')';
-
-       if ($c->{result}->{typeInfo}->{type} ne 'void') {
-               $desc .= $typeSignature{$c->{result}->{typeInfo}->{carrier}};
-       } else {
-               $desc .= 'V';
-       }
-
-       return $desc;
-}
-
-# TODO: perhaps ByteArray should just be MemorySegment, kinda painful to wrap them all the time
-sub analyseTypeInfo {
-       my $s = shift;
-       my $m = shift;
-       my $info = {};
-       my $inc;
-
-       #print " query $s->{name} $s->{type} '$m->{name}', '$m->{type}'\n";
-       if ($s->{type} eq 'struct') {
-               $inc = findAPIItem($api, 'struct', $s->{name}, 'field', $m->{name});
-       } elsif ($s->{type} eq 'func') {
-               $inc = findAPIItem($api, 'func', $s->{name}, 'field', $m->{name});
-       }
-
-       # default for everything not specifically handled
-       $info->{carrier} = "MemoryAddress";
-       $info->{resolve} = "(Addressable)Memory.address(\${value})";
-
-       if ($m->{deref} =~ m/^(u64:|u32:)\(/) {
-               # This is a function pointer, type must be type = 'call:.*'
-               if ($m->{type} =~ m/^call:(.*)/) {
-                       $info->{type} = "Memory.FunctionPointer<$1>";
-                       $info->{create} = "$1.downcall(\${result}, \${scope})";
-               } else {
-                       die();
-               }
-       } elsif ($m->{type} =~ m/^([iuf]\d+)$/) {
-               # primitive types
-
-               if ($m->{deref} =~ m/\[(\d*)u64:.*\]/) {
-                       $info->{byValue} = 1;
-                       $info->{type} = "Memory.PointerArray";
-                       $info->{create} = $info->{type}.".create(\${result})";
-               } elsif ($m->{deref} =~ m/\[(\d*).*\]/) {
-                       # TODO: some mode thing rather than byvalue?
-                       $info->{byValue} = 1;
-                       $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
-                       $info->{create} = $info->{type}.".create(\${result})";
-               } elsif ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
-                       $info->{type} = "Memory.PointerArray";
-                       $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
-               } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
-                       # assume any char * is a string unless an array-size or array is specified
-                       if ($inc->{array}) {
-                               $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
-                               $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
-                       } elsif ($inc->{array_size}) {
-                               $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
-                               $info->{create} = $info->{type}.".createArray(\${result}, \${array_size}, \${scope})";
-                       } elsif ($inc->{result_code}) {
-                               my $holder = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
-                               $info->{type} = $typeSizes{$m->{type}};
-                               $info->{declare} = "$holder $m->{name} = $holder.createArray(1, frame)";
-                               $info->{hide} = 1;
-                               $info->{result_code} = 1;
-                               $s->{success} = $inc->{success};
-                               $s->{resolveFrame} = 1;
-                               $s->{result_code} = $m;
-                       } elsif ($typeSizes{$m->{type}} eq 'byte') {
-                               $info->{type} = 'String';
-                               $info->{resolve} = "(Addressable)Memory.address(frame.copy(\${value}))";
-                               $info->{create} = "(\${result}).getUtf8String(0)";
-                               $info->{resolveFrame} = 1;
-                               # for a function or a constructor that uses this element
-                               $s->{resolveFrame} = 1;
-                       } else {
-                               # ideally length 0 but panama-foreign doesn't grok that so fuckit
-                               $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
-                               $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
-                       }
-               } else {
-                       $info->{type} = $typeSizes{$m->{type}};
-                       $info->{carrier} = $typeSizes{$m->{type}};
-                       $info->{resolve} = "($info->{type})(\${value})";
-                       $info->{create} = "\${result}";
-                       if ($inc->{implied}) {
-                               $info->{hide} = 1;
-                               $info->{implied} = $inc->{implied};
-                               $info->{resolve} = "($info->{type})($inc->{implied})";
-                       }
-               }
-       } elsif ($m->{type} =~ m/^(struct|union):(.*)/) {
-               my $type = $2;
-               if ($m->{deref} =~ m/\[(\d*)(.*)\]/) {
-                       $info->{byValue} = 1;
-                       # handle  'type name[x]' and  'type *name[x]'
-                       my $count = $1;
-                       my $deref = $2;
-                       if ($deref =~ m/^u64:u64:/) {
-                               die("can't handle double-deref array");
-                       } elsif ($deref =~ m/^u64:/) {
-                               $info->{type} = "Memory.HandleArray<$type>";
-                               $info->{create} = "Memory.HandleArray.create(\${result}, $type\:\:create, \${scope})";
-                       } else {
-                               $info->{type} = $type;
-                               #$info->{create} = $info->{type}.".create(\${result}, \${scope})";
-                               $info->{create} = $info->{type}.".create(\${result})";
-                       }
-               } elsif ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
-                       # this assumes ** is as far as it gets
-                       $info->{type} = "Memory.HandleArray<$type>";
-                       if ($inc->{array_size}) {
-                               $info->{create} = "Memory.HandleArray.createArray(\${result}, \${array_size}, $type\:\:create, \${scope})";
-                       } else {
-                               $info->{create} = "Memory.HandleArray.createArray(\${result}, Long.MAX_VALUE, $type\:\:create, \${scope})";
-                       }
-               } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
-                       $info->{type} = $type;
-                       $info->{create} = $info->{type}.".create(\${result}, \${scope})";
-               } else {
-                       # FIXME: change this to a reftype or something
-                       $info->{byValue} = 1;
-                       $info->{type} = $type;
-                       $info->{create} = $info->{type}.".create(\${result})";
-               }
-       } elsif ($m->{type} eq "void") {
-               if ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
-                       $info->{type} = "Memory.PointerArray";
-                       $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
-               } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
-                       # TODO: this should be MemorySegment for input arguments?  Or at least Addressable
-                       $info->{type} = "MemoryAddress";
-                       $info->{create} = "\${result}";
-                       $info->{resolve} = "(Addressable)Memory.address(\${value})";
-               } else {
-                       $info->{type} = "void";
-                       $info->{carrier} = "void";
-                       delete $info->{resolve};
-               }
-       } else {
-               print Dumper($m);
-               die ("unknown type");
-       }
-
-       return $info;
-}
-
-# TODO: see if this can be merged with formatFunction
-# TODO: should constructor take a resourcescope always?
-# TODO: exceptions for errors
-sub formatConstructor {
-       my $c = shift;
-       my $inc = shift;
-       my @arguments = @{$c->{arguments}};
-       my $result = $c->{result};
-       my $desc;
-       my $index = 0;
-       my $count = 0;
-       my $desc = "";
-       my $name = $c->{name};
-       my $rtype = $result->{typeInfo}->{type};
-       my $otype = $data{$arguments[$inc->{constructor_result}]->{type}}->{name};
-
-
-       $name = $inc->{rename}->($name);
-
-       $desc .= "static " if $inc->{scope} eq 'static';
-       $desc .= $otype;
-       $desc .= " $name(";
-
-       for $m (@arguments) {
-               if (($inc->{constructor} && $index != $inc->{constructor_result})) {
-                       $desc .= ", " if ($count++ > 0);
-                       $desc .= $m->{typeInfo}->{type};
-                       $desc .= " $m->{name}"
-               }
-               $index++;
-       }
-       if ($inc->{constructor}) {
-               $desc .= ", " if ($count++ > 0);
-               $desc .= "ResourceScope scope";
-       }
-       $desc .=") {\n ";
-
-       $desc .= "$result->{typeInfo}->{carrier} res\$value;\n" if ($rtype ne "void");
-
-       $desc .= " try ";
-       $desc .= "(Frame frame = Memory.createFrame()) " if ($c->{resolveFrame});
-       $desc .= "{\n";
-       $desc .= "   Memory.HandleArray<$otype> res\$holder = Memory.HandleArray.createArray(1, frame, $otype\:\:create, scope);\n" if ($inc->{constructor});
-       $desc .= "   res\$value = ($result->{typeInfo}->{carrier})" if ($rtype ne "void");
-       $desc .= "   " if ($rtype eq "void");
-
-       $index = 0;
-       $desc .= "$c->{name}\$FH.invokeExact(\n   ";
-       for $m (@arguments) {
-               my $resolve = $m->{typeInfo}->{resolve};
-
-               if ($inc->{constructor} && $index == $inc->{instance}) {
-                       $desc .= ",\n    " if ($index++ > 0);
-                       $desc .= "(Addressable)res\$holder.address()";
-               } elsif ($inc->{scope} ne 'static' && $index == $inc->{instance}) {
-                       $desc .= ",\n    " if ($index++ > 0);
-                       $desc .= "(Addressable)address()";
-               } else {
-                       $desc .= ",\n    " if ($index++ > 0);
-
-                       if ($resolve) {
-                               $resolve =~ s/\$\{value\}/$m->{name}/g;
-                               $desc .= $resolve;
-                       } else {
-                               $desc .= "$m->{name}";
-                       }
-               }
-       }
-       $desc .= ");\n";
-
-       if ($rtype ne "void" && defined $inc->{success}) {
-       #       my $create = $result->{typeInfo}->{create};
-
-       #       # ooh, templates could insert other arguments or values as well?
-       #       $create =~ s/\$\{result\}/res\$value/;
-       #       if ($inc->{scope} eq 'static') {
-       #               $create =~ s/\$\{scope\}/ResourceScope.globalScope()/;
-       #       } else {
-       #               $create =~ s/\$\{scope\}/scope()/;
-       #       }
-
-               foreach $code (split /,/,$inc->{success}) {
-                       $desc .= "   if (res\$value == $code) return res\$holder.getAtIndex(0);\n";
-               }
-       } else {
-               $desc .= "   return res\$holder.getAtIndex(0);\n";
-       }
-       # throw Error()?
-       $desc .= " } catch (Throwable t) { throw new RuntimeException(t); }\n";
-
-       # throw failures here based on res$value
-       $desc .= " return null;\n";
-
-       $desc .="}";
-
-       #print "$desc\n";
-       return $desc;
-}
-
-sub formatFunction {
-       my $c = shift;
-       my $inc = shift;
-       my @arguments = @{$c->{arguments}};
-       my $result = $c->{result};
-       my $desc;
-       my $index = 0;
-       my $count = 0;
-       my $desc = "";
-       my $name = $c->{name};
-       my $rtype = $result->{typeInfo}->{type};
-
-       if ($inc->{constructor}) {
-               return formatConstructor($c, $inc);
-       }
-
-       $name = $inc->{rename}->($name);
-
-       $desc .= "static " if $inc->{scope} eq 'static';
-       $desc .= $rtype;
-       $desc .= " $name(";
-
-       for $m (@arguments) {
-               if (($inc->{scope} eq 'static' || $index != $inc->{instance}) && !$m->{typeInfo}->{hide}) {
-                       $desc .= ", " if ($count++ > 0);
-                       $desc .= $m->{typeInfo}->{type};
-                       $desc .= " $m->{name}"
-               }
-               $index++;
-       }
-       $desc .=") {\n";
-
-       if ($c->{result_code}) {
-               my $r = $c->{result_code};
-               $desc .= "$r->{typeInfo}->{type} $r->{name}\$value;\n";
-       }
-       $desc .= "$result->{typeInfo}->{carrier} res\$value;\n" if ($rtype ne "void");
-
-       $desc .= " try ";
-       $desc .= "(Frame frame = Memory.createFrame()) " if ($c->{resolveFrame});
-       $desc .= "{\n";
-
-       if ($c->{result_code}) {
-               my $r = $c->{result_code};
-               $desc .= "  $r->{typeInfo}->{declare};\n";
-       }
-
-       $desc .= "  res\$value = ($result->{typeInfo}->{carrier})" if ($rtype ne "void");
-       $desc .= "  " if ($rtype eq "void");
-
-       $index = 0;
-       $desc .= "$c->{name}\$FH.invokeExact(\n   ";
-       for $m (@arguments) {
-               my $resolve = $m->{typeInfo}->{resolve};
-
-               if ($inc->{scope} ne 'static' && $index == $inc->{instance}) {
-                       $desc .= ",\n   " if ($index++ > 0);
-                       $desc .= "(Addressable)address()";
-               } else {
-                       $desc .= ",\n   " if ($index++ > 0);
-
-                       if ($resolve) {
-                               $resolve =~ s/\$\{value\}/$m->{name}/g;
-                               $desc .= $resolve;
-                       } else {
-                               $desc .= "$m->{name}";
-                       }
-               }
-       }
-       $desc .= ");\n";
-
-       my $error_value;
-
-       if ($rtype ne "void") {
-               my $create = $result->{typeInfo}->{create};
-
-               # ooh, templates could insert other arguments or values as well?
-               $create =~ s/\$\{result\}/res\$value/;
-               # TODO: libraries have a static scope() but instances don't, so could do better here
-               if ($inc->{scope} eq 'static') {
-                       $create =~ s/\$\{scope\}/ResourceScope.globalScope()/;
-               } else {
-                       $create =~ s/\$\{scope\}/scope()/;
-               }
-
-               my $success;
-
-               if ($c->{result_code}) {
-                       my $r = $c->{result_code};
-
-                       $error_value = "$r->{name}\$value";
-                       $desc .= "  $error_value = $r->{name}.get(0);\n";
-                       $success = $c->{success} ? $c->{success} : '0';
-               } elsif ($c->{success}) {
-                       $success = $c->{success};
-                       $error_value = "res\$value";
-               } elsif ($inc->{success}) {
-                       $success = $inc->{success};
-                       $error_value = "res\$value";
-               }
-               if ($success) {
-                       $desc .= "  if (";
-                       $count = 0;
-                       foreach $s (split /,/,$success) {
-                               $desc .= " || " if ($count++ > 0);
-                               $desc .= "($error_value == $s)";
-                       }
-                       $desc .= ")\n";
-               }
-
-               $desc .= "  return $create;\n";
-       }
-       # throw Error()?
-       $desc .= " } catch (Throwable t) { throw new RuntimeException(t); }\n";
-
-       # assume it's an int
-       if ($error_value) {
-               $desc .= " throw new RuntimeException(String.format(\"error=%d\", $error_value));\n";
-       }
-
-       $desc .="}";
-
-       return $desc;
-}
-
-# create an interface for function pointers
-# FiXME: this should be exportCallback to a file?
-sub formatCallback {
-       my $c = shift;
-       my $obj = shift;
-       my @arguments = @{$c->{arguments}};
-       my $result = $c->{result};
-       my $desc;
-       my $index = 0;
-       my $desc;
-       my $name = $c->{name};
-
-       #print "\nCall\n";
-       #print Dumper($c);
-
-       my $rtype = $result->{typeInfo}->{type};
-
-       $desc  = "\@FunctionalInterface\n";
-       $desc .= "public interface $name {\n";
-
-       # the public (functional) interface
-       $index = 0;
-       $desc .= " $result->{typeInfo}->{type} call(";
-       for $m (@arguments) {
-               $desc .= ", " if ($index++ > 0);
-               $desc .= $m->{typeInfo}->{type};
-               $desc .= " $m->{name}"
-       }
-       $desc .= ");\n";
-
-       # the internal interface
-       $index = 0;
-       $desc .= " \@FunctionalInterface\n";
-       $desc .= " interface Trampoline {\n  ";
-       $desc .=  $result->{typeInfo}->{carrier};
-       $desc .= " call(";
-       for $m (@arguments) {
-               $desc .= ", " if ($index++ > 0);
-               $desc .= $m->{typeInfo}->{carrier};
-               $desc .= " $m->{name}"
-       }
-       $desc .= ");\n";
-       $desc .= " }\n\n";
-
-       # native descriptor
-       $desc .= " static FunctionDescriptor DESCRIPTOR() {\n";
-       $desc .= "  return ";
-       my $tmp = formatFunctionDescriptor($c);
-       $tmp =~ s/^/  /mg;
-       $tmp =~ s/^ *//;
-       $desc .= $tmp;
-       $desc .= ";\n }\n";
-
-       # Factory method for upcalls
-       # TODO: optional?
-       $desc .= " public static Memory.FunctionPointer<$name> upcall($name target, ResourceScope scope) {\n";
-       $desc .= "  Trampoline trampoline = (";
-       $index = 0;
-       for $m (@arguments) {
-               $desc .= ", " if ($index++ > 0);
-               $desc .= "$m->{name}"
-       }
-       $desc .= ") -> {\n";
-       #$desc .= "   try {\n";
-       $desc .= "   ";
-       $desc .= "return " if $rtype ne "void";
-       $desc .= "target.call(\n    ";
-       $index = 0;
-       for $m (@arguments) {
-               my $create = $m->{typeInfo}->{create};
-
-               $create =~ s/\$\{result\}/$m->{name}/g;
-               $create =~ s/\$\{scope\}/scope/g;
-
-               $desc .= ",\n    " if ($index++ > 0);
-               $desc .= "$create";
-       }
-       $desc .= ");\n";
-       #$desc .= "   } catch (Exception x) { }{\n";
-       # FIXME: or null for address
-       #$desc .= "   return 0;\n" if $rtype != "void";
-       #$desc .= "   }\n";
-       $desc .= "  };\n";
-
-       $desc .= "  return new Memory.FunctionPointer<>(\n";
-       $desc .= "    Memory.upcall(\n";
-       $desc .= "     trampoline,\n";
-       $desc .= "     \"call\",\n";
-       $desc .= "     \"$c->{signature}\",\n";
-       $desc .= "     DESCRIPTOR(),\n";
-       $desc .= "     scope),\n";
-       $desc .= "    target);\n";
-       $desc .= " }\n";
-
-       # downcalls
-       $desc .= " public static Memory.FunctionPointer<$name> downcall(MemoryAddress addr, ResourceScope scope) {\n";
-       $desc .= "  NativeSymbol symbol = NativeSymbol.ofAddress(\"$name\", addr, scope);\n";
-       $desc .= "  MethodHandle $name\$FH = Memory.downcall(symbol, DESCRIPTOR());\n";
-       $desc .= "  return new Memory.FunctionPointer<$name>(\n";
-       $desc .= "   symbol,\n";
-
-       # HACK: this is basically the same as any function call, just patch in the changes for now
-       $tmp = formatFunction($c, $obj);
-
-       $tmp =~ s/^(.*) ($name)\(/(/;
-       $tmp =~ s/\) \{/) -> {/;
-       $tmp =~ s/^/   /mg;
-       $desc .= $tmp;
-
-       $desc .= "\n";
-       $desc .= "  );\n";
-       $desc .= " }\n";
-       $desc .= "}\n";
-
-       # replace leading ' ' with '\t'
-       $desc =~ s/(?:\G|^) /\t/mg;
-
-       return $desc;
-}
-
-# some bitfield support stuff.
-# maximum size allowed for field holder based on start offset
-# offset
-sub fieldMaxHolder {
-       my $offset = shift @_;
-
-       return 64 if ($offset & 63) == 0;
-       return 32 if ($offset & 31) == 0;
-       return 16 if ($offset & 15) == 0;
-       return 8 if ($offset & 7) == 0;
-       return 0;
-}
-
-sub fieldLimit {
-       my $size = shift @_;
-
-       return 64 if ($size > 32);
-       return 32 if ($size > 16);
-       return 16 if ($size > 8);
-       return 8;
-}
-
-# offset, size
-# returns @sizes required to hold them, based on alignment rules
-sub fieldHolders {
-       my $offset = shift @_;
-       my $bits = shift @_;
-       my $end = $offset + $bits;
-       my @sizes = ();
-
-       while ($offset < $end) {
-               my $limit = fieldLimit($bits);
-               my $max = fieldMaxHolder($offset);
-               my $step = ($limit < $max) ? $limit : $max;
-
-               push @sizes, $step;
-
-               $offset += $step;
-               $bits -= $step;
-       }
-
-       return @sizes;
-}
-
-sub formatLayout {
-       my $s = shift @_;
-       my @fields = @{$s->{fields}};
-       my $index = 0;
-       my $bitfieldIndex = 0;
-       my $desc;
-       my $last = 0;
-       my $maxSize = 8;
-
-       $desc = "MemoryLayout.$s->{type}Layout(\n ";
-
-       for (my $i = 0; $i <= $#fields; $i++) {
-               my $f = $fields[$i];
-
-               if ($f->{offset} > $last) {
-                       $desc .= ",\n" if ($index++ > 0);
-                       $desc .= ' MemoryLayout.paddingLayout('.($f->{offset} - $last).')';
-               }
-
-               $maxSize = fieldLimit($f->{size}) if (fieldLimit($f->{size}) > $maxSize);
-
-               if ($f->{ctype} eq 'bitfield') {
-                       my $start = $f->{offset};
-                       my $end = $f->{size} + $f->{offset};
-                       my $j = $i + 1;
-                       my $max = fieldMaxHolder($start);
-
-                       # breaks bitfields into char/short/int/long blocks
-                       # TODO: need more info for mapping to get/settters
-
-                       #print "> $f->{name} $f->{size} @ $f->{offset}\n";
-
-                       while ($j <= $#fields && $fields[$j]->{ctype} eq "bitfield") {
-                               my $g = $fields[$j];
-
-                               #print "> $g->{name} $g->{size} @ $g->{offset}\n";
-
-                               if ($g->{offset} > $end || ($g->{offset} - $start >= $max)) {
-                                       foreach $size (fieldHolders($start, $end - $start)) {
-                                               $desc .= ",\n " if ($index++ > 0);
-                                               $desc .= 'Memory.'.uc($intSizes{$size}).".withName(\"bitfield\$$bitfieldIndex\")";
-                                               $bitfieldIndex++;
-                                       }
-                                       $desc .= ",\n " if ($index++ > 0);
-                                       $desc .= 'MemoryLayout.paddingLayout('.($g->{offset}-$end).')';
-                                       $start = $g->{offset};
-                                       $max = fieldMaxHolder($start);
-                               }
-                               $end = $g->{size} + $g->{offset};
-                               $j++;
-                       }
-
-                       foreach $size (fieldHolders($start, $end - $start)) {
-                               $desc .= ",\n " if ($index++ > 0);
-                               $desc .= 'Memory.'.uc($intSizes{$size}).".withName(\"bitfield\$$bitfieldIndex\")";
-                               $bitfieldIndex++;
-                       }
-
-
-                       $i = $j-1;
-               } else {
-                       $desc .= ",\n " if ($index++ > 0);
-                       $desc .= formatTypeLayout($f, ".withName(\"$f->{name}\")");
-               }
-
-               $last = $fields[$i]->{offset} + $fields[$i]->{size};
-       }
-
-       if ($last < $s->{size}) {
-               $desc .= ",\n " if ($index++ > 0);
-               $desc .= 'MemoryLayout.paddingLayout('.($s->{size} - ${last}).')';
-       }
-
-       $desc .= "\n)";
-       $desc .= ".withBitAlignment($maxSize)";
-
-       return $desc;
-}
-
-sub formatGetSet {
-       my $s = shift;
-       my $m = shift;
-       my $rename = shift;
-       my $access = shift;
-       my $inc = shift;
-       my $desc = "";
-       my $info = $m->{typeInfo};
-       my $Name = ucfirst($rename);
-       my $tmp;
-
-       # info -> needsalloc?
-
-       # TODO: embedded arrays are quite different setup
-
-       if ($info->{byValue}) {
-               $tmp = $info->{create};
-               $tmp =~ s/\$\{result\}/segment/g;
-               $tmp =~ s/\$\{scope\}/scope()/g;
-
-               $desc .= " public $info->{type} get$Name() {\n";
-               #$desc .= "  MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
-               #$desc .= "  MemorySegment segment = this.segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n";
-               $desc .= "  MemorySegment segment = this.segment.asSlice($m->{name}\$byteOffset, $m->{name}\$byteSize);\n";
-               $desc .= "  return $tmp;\n";
-               $desc .= " }\n";
-
-               if ($access =~ m/i/) {
-                       $desc .= " public $info->{type} get$Name"."At(long index) {\n";
-                       #$desc .= "  MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
-                       #$desc .= "  MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
-                       #$desc .= "  segment = this.segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n";
-                       $desc .= "  MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index + $m->{name}\$byteOffset, $m->{name}\$byteSize);\n";
-                       $desc .= "  return $tmp;\n";
-                       $desc .= " }\n";
-               }
-       } elsif ($access =~ /r/) {
-               $tmp = $info->{create};
-
-               $tmp =~ s/\$\{result\}/($info->{carrier})$m->{name}\$VH.get(segment)/g;
-               $tmp =~ s/\$\{scope\}/scope()/g;
-               # fixme: lookup type of array size? somewhere?  doesn't matter i think
-               $tmp =~ s/\${array_size}/(long)$inc->{array_size}\$VH.get(segment)/g;
-
-               $desc .= " public $info->{type} get$Name() {\n";
-               $desc .= "  return $tmp;\n";
-               $desc .= " }\n";
-
-               if ($access =~ m/i/) {
-                       $desc .= " public $info->{type} get$Name"."At(long index) {\n";
-                       $desc .= "  MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
-                       $desc .= "  return $tmp;\n";
-                       $desc .= " }\n";
-               }
-       }
-
-       if ($access =~ m/w/ && !$info->{byValue}) {
-               $tmp = $info->{resolve};
-               $tmp =~ s/\$\{value\}/value/g;
-
-               $desc .=  " public void set$Name($info->{type} value) {\n";
-               $desc .=  " try (Frame frame = Memory.createFrame()) {\n" if ($info->{resolveFrame});
-               $desc .=  "  $m->{name}\$VH.set(segment, $tmp);\n";
-               $desc .=  " }\n" if ($info->{resolveFrame});
-               $desc .=  " }\n";
-
-               if ($access =~ m/i/) {
-                       $desc .=  " public void set$Name"."At(long index, $info->{type} value) {\n";
-                       $desc .=  " try (Frame frame = Memory.createFrame()) {\n" if ($info->{resolveFrame});
-                       $desc .=  "  MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
-                       $desc .=  "  $m->{name}\$VH.set(segment, $tmp);\n";
-                       $desc .=  " }\n" if ($info->{resolveFrame});
-                       $desc .=  " }\n";
-               }
-       }
-
-       return $desc;
-}
-
-sub exportStruct {
-       my $f = shift;
-       my $s = shift;
-       my $obj = shift;
-       my @fields = @{$s->{fields}};
-       my $isHandle = $s->{size} == 0;
-       my $doArray = $obj->{access} =~ m/i/;
-       #my @functions = @{shift @_};
-
-       print $f "package $package;\n" if $package;
-
-       print $f "import jdk.incubator.foreign.*;\n";
-       print $f "import java.lang.invoke.*;\n";
-
-       print $f "public class $s->{name} implements Memory.Addressable {\n";
-
-       # TODO: parameterise and use typeInfo data.
-       if (!$isHandle) {
-               print $f " public final MemorySegment segment;\n";
-               # constructors
-               print $f " private $s->{name}(MemorySegment segment) { this.segment = segment; }\n";
-               print $f " public static $s->{name} create(MemorySegment segment) { return new $s->{name}(segment); }\n";
-               print $f " public static $s->{name} create(MemoryAddress address, ResourceScope scope) {\n";
-               print $f "  return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope)) : null;\n";
-               print $f " }\n";
-               if ($doArray) {
-                       print $f " public static $s->{name} createArray(MemoryAddress address, long size, ResourceScope scope) {\n";
-                       print $f "  return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, size * LAYOUT.byteSize(), scope)) : null;\n";
-                       print $f " }\n";
-
-                       print $f " public long length() { return segment.byteSize() / LAYOUT.byteSize(); }\n";
-               }
-               print $f " public static $s->{name} create(Frame frame) { return create(frame.allocate(LAYOUT)); }\n";
-               print $f " public static $s->{name} create(ResourceScope scope) { return create(MemorySegment.allocateNative(LAYOUT, scope)); }\n";
-               print $f " public MemoryAddress address() { return segment.address(); }\n";
-               print $f " public ResourceScope scope() { return segment.scope(); }\n";
-       } else {
-               # not sure if handles need scopes
-               print $f " MemoryAddress address;\n";
-               print $f " ResourceScope scope;\n";
-               # constructors
-               print $f " private $s->{name}(MemoryAddress address, ResourceScope scope) { this.address = address; this.scope = scope;}\n";
-               print $f " public static $s->{name} create(MemoryAddress address, ResourceScope scope) { return MemoryAddress.NULL != address ? new $s->{name}(address, scope) : null; }\n";
-               print $f " public MemoryAddress address() { return address; }\n";
-               print $f " public ResourceScope scope() { return scope; }\n";
-       }
-
-       my %seen;
-
-       # Any defines
-       foreach $inc (grep { $_->{mode} eq 'define' } @{$obj->{items}}) {
-               my $def = $data{$inc->{match}};
-
-               die ("unknown define $inc->{match} in $s->{name}\n") if !$def;
-
-               delete $toDump->{$inc->{match}};
-
-               foreach $m (@{$def->{values}}) {
-                       print $f " /**\n ($m->{comment}) */\n" if ($m->{comment});
-                       print $f " public static final $defineType{$m->{type}} $m->{name} = $definePrefix{$m->{type}}$m->{value}$defineSuffix{$m->{type}};\n";
-               }
-       }
-
-       # TODO: any enums we want to include here I suppose?
-
-       # Accessors
-       foreach $m (@fields) {
-               my $access = $obj->{access};
-               my $rename = $obj->{'field:rename'};
-               my $matches = 0;
-               my $matchinc;
-
-               # check for match
-               foreach $inc (grep { $_->{mode} eq 'field' } @{$obj->{items}}) {
-                       $matches = $m->{name} =~ m/$inc->{regex}/;
-
-                       if ($matches) {
-                               $access = $inc->{access} if $inc->{access};
-                               $rename = $inc->{rename} if $inc->{rename} != $renameTable{'identity'};
-                               $matchinc = $inc;
-                               last;
-                       }
-               }
-
-               my $output = $matches || ($obj->{default} eq 'all');
-
-               if ($output) {
-                       my $name = $rename ? $rename->($m->{name}) : $m->{name};
-
-                       print $f formatGetSet($s, $m, $name, $access, $matchinc);
-               }
-       }
-
-       # Functions
-       foreach $inc (grep { $_->{mode} eq 'func' } @{$obj->{items}}) {
-               my @list;
-
-               print "$obj->{name} match $inc->{match} regex $inc->{regex}\n" if $verbose;
-
-               if ($data{$inc->{match}}) {
-                       push @list, $data{$inc->{match}};
-               } else {
-                       @list = grep { $_->{name} =~ m/$inc->{regex}/ } values %data;
-               }
-
-               foreach $c (@list) {
-                       my $tmp;
-
-                       next if $seen{$c->{name}}++;
-
-                       print $f " static final MethodHandle $c->{name}\$FH = Memory.downcall(\"$c->{name}\",\n";
-                       $tmp = formatFunctionDescriptor($c);
-                       print $f "$tmp);\n";
-
-                       $tmp = formatFunction($c, $inc);
-                       print $f 'public '.$tmp."\n\n";
-               }
-       }
-
-       # layout and varhandles
-       if ($#fields >= 0) {
-               print $f "static final GroupLayout LAYOUT = ".formatLayout($s).";\n";
-
-               foreach $m (@fields) {
-                       print $f " // type='$m->{type}' deref='$m->{deref}'  info->type ='$m->{typeInfo}->{type}'\n";
-                       if ($m->{typeInfo}->{byValue}) {
-                               print $f " static final long $m->{name}\$byteOffset = "
-                                       ." LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
-                               print $f " static final long $m->{name}\$byteSize = "
-                                       ."LAYOUT.select(MemoryLayout.PathElement.groupElement(\"$m->{name}\")).byteSize();\n";
-                       } else {
-                               print $f " static final VarHandle $m->{name}\$VH = "
-                                       ."LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
-                       }
-               }
-       }
-
-       # verification?
-       if (!$isHandle) {
-               print $f <<END;
-               static void check\$offset(String name, long bitoffset) {
-                       long byteoffset = bitoffset/8;
-                       long offset = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement(name));
-                       if (offset != byteoffset)
-                               throw new AssertionError(String.format("%s.offset %d != %d", name, byteoffset, offset), null);
-               }
-               {
-END
-               foreach $m (@fields) {
-                       print $f "check\$offset(\"$m->{name}\", $m->{offset});\n";
-               }
-my $bytes = $s->{size}/8;
-       print $f <<END;
-               if (LAYOUT.byteSize() != $bytes)
-                       throw new AssertionError(String.format("$s->{name}.sizeof = %d != $bytes", LAYOUT.byteSize()), null);
-       }
-END
-       }
-       print $f "}\n";
-}
-
-# file,enum
-sub exportEnum {
-       my $f = shift;
-       my $s = shift;
-       my @values = @{$s->{values}};
-       my $jtype = $typeSizes{$s->{value_type}};
-       my $prefix = $definePrefix{$s->{value_type}};
-       my $suffix = $definePrefix{$s->{value_type}};
-       print $f "package $package;\n" if $package;
-
-       print $f "public interface $s->{name} {\n";
-
-       foreach $v (@values) {
-               print $f " public static final $jtype $v->{name} = $prefix$v->{value}$suffix;\n";
-       }
-
-       print $f "}\n";
-}
-
-# copies a skeleton file and patches it to the target package
-sub copySkeletonFile {
-       my $src = shift @_;
-       my $dst = shift @_;
-
-       open (my $d, ">", $dst) || die ("Cannot open '$src' for writing");
-       open (my $s, "<", $src) || die ("Cannot open '$dst' for reading");
-
-       while (<$s>) {
-               s/^package .*;/package $package;/;
-               print $d $_;
-       }
-
-       close $s;
-       close $d;
-
-}
-
-# init output
-$outputPath = $package;
-$outputPath =~ s@\.@/@g;
-$outputPath = "$output/$outputPath";
-
-make_path($outputPath);
-
-copySkeletonFile("$scriptPath/template/Memory.java", "$outputPath/Memory.java");
-copySkeletonFile("$scriptPath/template/Frame.java", "$outputPath/Frame.java");
-
-sub nameToPath {
-       my $dir = shift @_;
-       my $name = shift @_;
-
-       $name =~ s@\.@/@g;
-       $name = "$dir/$name.java";
-       return $name;
-}
-
-#print "api\n";
-#print Dumper($api);
-
-# Dump struct type
-foreach $obj ( @{$api->{struct}} ) {
-       my @list;
-
-       next if $obj->{name} eq '<default>';
-
-       if ($obj->{name} =~ m@/(.*)/@) {
-               my $rx = qr/struct:$1/;
-
-               @list = map { s/struct://; $_ } grep { $_ =~ m/$rx/ } keys %data;
-       } else {
-               push @list, $obj->{name};
-       }
-
-       foreach $name (@list) {
-               my $path = nameToPath($output, "$package.$name");
-               my $s = $data{"struct:$name"};
-
-               #print Dumper($obj);
-
-               delete $toDump->{"struct:$name"};
-
-               if ($s) {
-                       open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
-
-                       exportStruct($f, $s, $obj);
-
-                       close $f;
-               } else {
-                       print "No struct $name\n";
-               }
-       }
-}
-
-# find flag
-sub flag {
-       my $obj = shift;
-       my $flag = shift;
-
-       return grep { $_ eq $flag } @{$obj->{options}};
-}
-
-# find option(s)
-sub option {
-       my $obj = shift;
-       my $name = shift;
-       my $rx = qr/^$name=/;
-
-       return grep { $_ =~ m/$rx/ } @{$obj->{options}};
-}
-
-# Dump library type
-foreach $lib ( @{$api->{library}} ) {
-       my $path = nameToPath($output, "$package.$lib->{name}");
-       my $dynamic = flag($lib, 'dynamic');
-
-       open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
-
-       print $f "package $package;\n";
-       print $f "import jdk.incubator.foreign.*;\n";
-       print $f "import java.lang.invoke.*;\n";
-
-       print $f "public class $lib->{name} {\n";
-       print $f " static ResourceScope scope() { return ResourceScope.globalScope(); }\n";
-
-       foreach $inc (@{$lib->{items}}) {
-               if ($inc->{mode} eq 'func') {
-                       my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
-                       foreach $c (@list) {
-                               my $tmp;
-
-                               print $f " static final MethodHandle $c->{name}\$FH = Memory.downcall(\"$c->{name}\",\n";
-                               $tmp = formatFunctionDescriptor($c);
-                               print $f $tmp.");\n";
-
-                               $tmp = formatFunction($c, $inc);
-                               print $f "public ";
-                               print $f $tmp."\n\n";
-                       }
-               } elsif ($inc->{mode} eq 'define') {
-                       print "looking for define $inc->{regex}\n";
-
-                       my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
-                       foreach $c (@list) {
-                               delete $toDump->{"define:$c->{name}"};
-                               foreach $m (@{$c->{values}}) {
-                                       print $f " /**\n ($m->{comment}) */\n" if ($m->{comment});
-                                       print $f " public static final $defineType{$m->{type}} $m->{name} = $definePrefix{$m->{type}}$m->{value}$defineSuffix{$m->{type}};\n";
-                               }
-                       }
-               }
-       }
-
-       print $f "}\n";
-
-       close $f;
-}
-
-print "remaining dependent types\n";
-foreach $k (sort grep { !m/func:/ } keys %{$toDump}) {
-       print " $k\n";
-}
-print "\n";
-
-# Calls referenced
-foreach $k (sort keys %{$toDump}) {
-       next if (!($k =~ m/call:(.+)/));
-
-       my $name = $1;
-       my $c = $data{$k};
-       my $obj = findAPIObject($api, 'call', $name);
-
-       my $path = nameToPath($output, "$package.$name");
-
-       open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
-
-       print $f "package $package;\n";
-       print $f "import jdk.incubator.foreign.*;\n";
-       print $f "import java.lang.invoke.*;\n";
-
-       print $f formatCallback($c, $obj);
-
-       close $f;
-}
-
-# any struct remaining in toDump (but not in api)
-# FIXME: how to lookup obj?
-foreach $k (sort keys %{$toDump}) {
-       if ($k =~ m/struct:(.*)/) {
-               my $name = $1;
-               my $s = $data{$k};
-               my $path = nameToPath($output, "$package.$name");
-
-               die("missing struct $name") if !$s;
-
-               delete $toDump->{$k};
-
-               my $obj = findAPIObject($api, 'struct', $name);
-
-               open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
-               exportStruct($f, $s, $obj);
-               close $f;
-       }
-}
-
-# Dump enum types used by everything and not dumped elsehwere
-foreach $k (sort keys %{$toDump}) {
-       if ($k =~ m/enum:(.*)/) {
-               my $name = $1;
-               my $s = $data{$k};
-               my $path = nameToPath($output, "$package.$name");
-
-               die("missing enum $name") if !$s;
-
-               open(my $f, ">", $path) || die ("Cannot open '$path' for writing");
-
-               exportEnum($f, $s);
-
-               close $f;
-       }
-}
-
-# Dump define types not dumped elsehwere
-foreach $k (sort keys %{$toDump}) {
-       if ($k =~ m/define:(.*)/) {
-               my $name = $1;
-               my $s = $data{$k};
-               my $path = nameToPath($output, "$package.$name");
-
-               die("missing define $name") if !$s;
-
-               open(my $f, ">", $path) || die ("Cannot open '$path' for writing");
-
-               print $f "package $package;\n" if $package;
-               print $f "public interface $s->{name} {\n";
-
-               foreach $m (@{$s->{values}}) {
-                       # some parsing problem here
-                       next if !$m->{value};
-
-                       print $f " /**\n ($m->{comment}) */\n" if ($m->{comment});
-                       print $f " public static final $defineType{$m->{type}} $m->{name} = $definePrefix{$m->{type}}$m->{value}$defineSuffix{$m->{type}};\n";
-               }
-
-               print $f "}\n";
-
-               close $f;
-       }
-}
-
-# and we're done
-exit 0;
-
-sub loadControlFile {
-       my $path = shift @_;
-       my %def = ();
-       my $target;
-
-       open (my $d,"<",$path);
-
-       while (<$d>) {
-               next if /\s*\#/;
-
-               chop;
-
-               if ($target) {
-                       if (m/\s*\}\s*$/) {
-                               undef $target;
-                       } elsif (/^\s*(\S+)\s*(.*)/) {
-                               my @options = split(/\s+/,$2);
-                               push @{$target->{items}}, {
-                                       match => $1,
-                                       options => \@options
-                               };
-                       }
-               } elsif (/^(\w+)\s+(\S*)\s*(.*)\s+\{/) {
-                       my @options = split(/\s+/,$3);
-
-                       $target = {
-                               type => $1,
-                               name => $2,
-                               options => \@options,
-                               items => []
-                       };
-                       push @{$def{$1}}, $target;
-               } elsif (/\S/) {
-                       die("invalid line: %_");
-               }
-       }
-
-       close $d;
-
-       return \%def;
-}
similarity index 97%
rename from api/api.c
rename to src/notzed.api/native/api.c
index 58cac5f..0a2b03b 100644 (file)
--- a/api/api.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include "api.h"
+#include "libapi/api.h"
 
 static void funca(int a) {
        printf("funca: %d\n", a);
similarity index 100%
rename from api/api.h
rename to src/notzed.api/native/libapi/api.h
diff --git a/src/notzed.api/native/native.make b/src/notzed.api/native/native.make
new file mode 100644 (file)
index 0000000..a5a1efb
--- /dev/null
@@ -0,0 +1,5 @@
+
+notzed.api_NATIVE_LIBRARIES=api
+
+api_SOURCES=api.c
+api_HEADERS=libapi/api.h
@@ -3,21 +3,22 @@ package api.test;
 
 import jdk.incubator.foreign.*;
 
-import proto.apiobject.*;
+import api.*;
 import java.lang.invoke.*;
+import au.notzed.nativez.*;
 
 public class TestAPI {
 
        public static void main(String[] args) {
                System.loadLibrary("api");
 
-               try (Frame frame = Memory.createFrame();
+               try (Frame frame = Frame.frame();
                        ResourceScope scope = ResourceScope.newConfinedScope()) {
 
-                       data a = data.create(frame);
-                       data b = data.create(frame);
+                       Data a = Data.create(frame);
+                       Data b = Data.create(frame);
 
-                       Memory.FunctionPointer<Call__i32> cb = Call__i32.upcall(() -> {
+                       FunctionPointer<Call__i32> cb = Call__i32.upcall(() -> {
                                        return 56;
                                }, scope);
 
@@ -48,11 +49,11 @@ public class TestAPI {
 
                        // dynamic lookup
                        System.out.println("call funca via symbol lookup");
-                       Memory.FunctionPointer<Call_i32_v> funca = Call_i32_v.downcall(api.func("funca"), scope);
+                       FunctionPointer<Call_i32_v> funca = Call_i32_v.downcall(API.func("funca"), scope);
                        System.out.printf(" %s\n", funca.symbol());
                        funca.function().call(12);
 
-                       api api = proto.apiobject.api.create();
+                       API api = API.create();
 
                        System.out.println("call funca via function table");
                        api.getFunca().function().call(99);
diff --git a/src/notzed.apiobject/classes/module-info.java b/src/notzed.apiobject/classes/module-info.java
new file mode 100644 (file)
index 0000000..cb7ca72
--- /dev/null
@@ -0,0 +1,5 @@
+
+module notzed.apiobject {
+       requires transitive notzed.nativez;
+       exports api;
+}
similarity index 93%
rename from test-api-object/api-object.api
rename to src/notzed.apiobject/gen/apiobject.api
index 91d14b8..408166b 100644 (file)
@@ -10,14 +10,14 @@ struct <default> default=all field:rename=studly-caps access=rw {
 call <default> default=all call:rename=call access=rw {
 }
 
-struct api {
+struct api rename=API {
        enum://;
        define:API;
        library:api-static;
        library:api-calls;
 }
 
-struct data {
+struct data rename=Data {
        func:print_data instance:0 rename=print;
 }
 
@@ -41,6 +41,6 @@ library api-calls func:rename=s/^api_//,camel-case instance:0 {
 }
 
 # grab all defines that come from api.h
-define API api/api.h {
+define API libapi/api.h {
        api.h   file-include;
 }
diff --git a/src/notzed.apiobject/gen/apiobject.h b/src/notzed.apiobject/gen/apiobject.h
new file mode 100644 (file)
index 0000000..32a074c
--- /dev/null
@@ -0,0 +1,2 @@
+
+#include <libapi/api.h>
diff --git a/src/notzed.apiobject/gen/gen.make b/src/notzed.apiobject/gen/gen.make
new file mode 100644 (file)
index 0000000..719527c
--- /dev/null
@@ -0,0 +1,3 @@
+
+notzed.apiobject_API = apiobject
+notzed.apiobject_APIFLAGS = -t api -Isrc/notzed.api/native
@@ -3,22 +3,23 @@ package api.test;
 
 import jdk.incubator.foreign.*;
 
-import proto.apistatic.*;
-import static proto.apistatic.APILib.*;
+import api.*;
+import static api.APILib.*;
 import java.lang.invoke.*;
+import au.notzed.nativez.*;
 
 public class TestAPI {
 
        public static void main(String[] args) {
                System.loadLibrary("api");
 
-               try (Frame frame = Memory.createFrame();
+               try (Frame frame = Frame.frame();
                        ResourceScope scope = ResourceScope.newConfinedScope()) {
 
                        data a = data.create(frame);
                        data b = data.create(frame);
 
-                       Memory.FunctionPointer<Call__i32> cb = Call__i32.upcall(() -> {
+                       FunctionPointer<Call__i32> cb = Call__i32.upcall(() -> {
                                        return 56;
                                }, scope);
 
@@ -49,7 +50,7 @@ public class TestAPI {
 
                        // dynamic lookup
                        System.out.println("call funca via symbol lookup");
-                       Memory.FunctionPointer<Call_i32_v> funca = Call_i32_v.downcall(api_func("funca"), scope);
+                       FunctionPointer<Call_i32_v> funca = Call_i32_v.downcall(api_func("funca"), scope);
                        System.out.printf(" %s\n", funca.symbol());
                        funca.function().call(12);
 
diff --git a/src/notzed.apistatic/classes/module-info.java b/src/notzed.apistatic/classes/module-info.java
new file mode 100644 (file)
index 0000000..6662845
--- /dev/null
@@ -0,0 +1,6 @@
+
+module notzed.apistatic {
+       requires notzed.nativez;
+
+       exports api;
+}
similarity index 95%
rename from test-api-static/api-static.api
rename to src/notzed.apistatic/gen/apistatic.api
index e3c7eca..da1fff8 100644 (file)
@@ -20,6 +20,6 @@ call <default> access=rw call:rename=call {
 }
 
 # grab all defines in api.h
-define API api/api.h {
+define API libapi/api.h {
        api.h       file-include;
 }
diff --git a/src/notzed.apistatic/gen/apistatic.h b/src/notzed.apistatic/gen/apistatic.h
new file mode 100644 (file)
index 0000000..32a074c
--- /dev/null
@@ -0,0 +1,2 @@
+
+#include <libapi/api.h>
diff --git a/src/notzed.apistatic/gen/gen.make b/src/notzed.apistatic/gen/gen.make
new file mode 100644 (file)
index 0000000..9a17dc5
--- /dev/null
@@ -0,0 +1,3 @@
+
+notzed.apistatic_API = apistatic
+notzed.apistatic_APIFLAGS = -t api -Isrc/notzed.api/native
diff --git a/src/notzed.clstatic/classes/module-info.java b/src/notzed.clstatic/classes/module-info.java
new file mode 100644 (file)
index 0000000..4b7b5db
--- /dev/null
@@ -0,0 +1,5 @@
+
+module notzed.clstatic {
+       requires transitive notzed.nativez;
+       exports opencl;
+}
diff --git a/src/notzed.clstatic/classes/opencl/cl_event_list.java b/src/notzed.clstatic/classes/opencl/cl_event_list.java
new file mode 100755 (executable)
index 0000000..f53b441
--- /dev/null
@@ -0,0 +1,64 @@
+
+package opencl;
+
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.*;
+import java.lang.invoke.*;
+import au.notzed.nativez.*;
+
+public class cl_event_list implements Pointer {
+
+       public final MemorySegment segment;
+       int index;
+
+       cl_event_list(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static cl_event_list create(MemorySegment segment) {
+               return new cl_event_list(segment);
+       }
+
+       public static cl_event_list createArray(long length, SegmentAllocator allocator) {
+               return create(allocator.allocateArray(Memory.POINTER, length));
+       }
+
+       @Override
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       @Override
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       public final MemorySegment segment() {
+               return segment;
+       }
+
+       public final MemoryAddress reserve() {
+               return address().addOffset(index * Memory.POINTER.byteSize());
+       }
+
+       public final void advance() {
+               index += 1;
+       }
+
+       public static jdk.incubator.foreign.Addressable address(cl_event_list a) {
+               return a != null ? a.address() : MemoryAddress.NULL;
+       }
+
+       public static jdk.incubator.foreign.Addressable reserve(cl_event_list a) {
+               return a != null ? a.reserve() : MemoryAddress.NULL;
+       }
+
+       public static void advance(cl_event_list a) {
+               if (a != null)
+                       a.advance();
+       }
+
+       public int count() {
+               return index;
+       }
+}
@@ -1,13 +1,13 @@
 
 package opencl.test;
 
-import static proto.opencl.CL.*;
-import proto.opencl.*;
-import proto.opencl.Memory.*;
+import static opencl.CL.*;
+import opencl.*;
+import au.notzed.nativez.*;
 import java.io.PrintStream;
 import jdk.incubator.foreign.*;
 
-public class TestOpenCL {
+public class clinfo {
 
        static class platform_string {
                final int key;
@@ -22,7 +22,7 @@ public class TestOpenCL {
                }
 
                void print(PrintStream out, cl_platform_id p) {
-                       try (Frame frame = Memory.createFrame()) {
+                       try (Frame frame = Frame.frame()) {
                                LongArray sizep = LongArray.createArray(1, frame);
                                int res;
                                MemorySegment seg;
@@ -58,7 +58,7 @@ public class TestOpenCL {
        public static void main(String[] args) throws Exception {
                System.loadLibrary("OpenCL");
 
-               try (Frame frame = Memory.createFrame()) {
+               try (Frame frame = Frame.frame()) {
                        IntArray countp = IntArray.createArray(1, frame);
                        HandleArray<cl_platform_id> platforms;
                        int res;
diff --git a/src/notzed.clstatic/gen/gen.make b/src/notzed.clstatic/gen/gen.make
new file mode 100644 (file)
index 0000000..43303c7
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+notzed.clstatic_API = opencl
+notzed.clstatic_APIFLAGS = -t opencl -I$(CLSTATIC_HOME)/include -Isrc/notzed.clstatic/gen
diff --git a/src/notzed.clstatic/gen/opencl.api b/src/notzed.clstatic/gen/opencl.api
new file mode 100644 (file)
index 0000000..11126ea
--- /dev/null
@@ -0,0 +1,183 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+include code.api
+
+# override cl_event entirely
+type /^u64:u64:\$\{_cl_event\}$/ copy=<pointer> {
+       type            {{ 'cl_event_list' }}
+       tonative        {{ 'cl_event_list.address({value})' }}
+}
+
+include types.api
+
+struct <default> struct:rename=s/^_cl_/cl_/ {
+}
+
+struct _cl_image_format template=code:class=struct-array {
+}
+
+code event {
+       reserve {{ (jdk.incubator.foreign.Addressable)cl_event_list.reserve({value}) }}
+       success {{
+               cl_event_list.advance(event);
+       }}
+}
+
+library CL success:errcode_ret=CL_SUCCESS {
+       define:CLConstants;
+
+       # core functions, resolved by dlopen;
+       clGetPlatformIDs                array:platforms implied:num_entries=(int)Memory.length(platforms);
+       clGetPlatformInfo;
+       clGetDeviceIDs                  array:devices implied:num_entries=(int)Memory.length(devices);
+       clGetDeviceInfo;
+       clCreateSubDevices;
+       clRetainDevice;
+       clReleaseDevice;
+       clSetDefaultDeviceCommandQueue;
+       clGetDeviceAndHostTimer;
+       clGetHostTimer;
+       clCreateContext                 implied:num_entries=(int)Memory.length(devices) implied:user_data=(Addressable)MemoryAddress.NULL;
+       clCreateContextFromType;
+       clRetainContext;
+       clReleaseContext;
+       clGetContextInfo;
+       clCreateCommandQueueWithProperties;
+       clRetainCommandQueue;
+       clReleaseCommandQueue;
+       clGetCommandQueueInfo;
+       clCreateBuffer;
+       clCreateSubBuffer;
+       clCreateImage;
+       clCreatePipe;
+       clRetainMemObject;
+       clReleaseMemObject;
+       clGetSupportedImageFormats      implied:num_entries=(int)Memory.length(image_formats);
+       clGetMemObjectInfo;
+       clGetImageInfo;
+       clGetPipeInfo;
+       clSetMemObjectDestructorCallback;
+       clSVMAlloc                      success:result$=!null;
+       clSVMFree;
+       clCreateSamplerWithProperties;
+       clRetainSampler;
+       clReleaseSampler;
+       clGetSamplerInfo;
+       clCreateProgramWithSource;
+       clCreateProgramWithBinary;
+       clCreateProgramWithBuiltInKernels;
+       clCreateProgramWithIL;
+       clRetainProgram;
+       clReleaseProgram;
+       clBuildProgram;
+       clCompileProgram;
+       clLinkProgram;
+       clUnloadPlatformCompiler;
+       clGetProgramInfo;
+       clGetProgramBuildInfo;
+       clCreateKernel;
+       clCreateKernelsInProgram;
+       clCloneKernel;
+       clRetainKernel;
+       clReleaseKernel;
+       clSetKernelArg;
+       clSetKernelArgSVMPointer;
+       clSetKernelExecInfo;
+       clGetKernelInfo;
+       clGetKernelArgInfo;
+       clGetKernelWorkGroupInfo;
+       clGetKernelSubGroupInfo;
+       clWaitForEvents;
+       clGetEventInfo;
+       clCreateUserEvent;
+       clRetainEvent;
+       clReleaseEvent;
+       clSetUserEventStatus;
+       clSetEventCallback;
+       clGetEventProfilingInfo;
+       clFlush;
+       clFinish;
+
+       library:enqueue;
+
+       clEnqueueMapBuffer;
+       clEnqueueMapImage;
+
+       clGetExtensionFunctionAddressForPlatform;
+       clCreateImage2D;
+       clCreateImage3D;
+       clEnqueueMarker                         onsuccess=code:event=success;
+       clEnqueueWaitForEvents          implied=num_events=event_list.count() tonative=event=code:next-event;
+       clEnqueueBarrier;
+       clUnloadCompiler;
+       clGetExtensionFunctionAddress;
+       clCreateCommandQueue;
+       clCreateSampler;
+}
+
+library enqueue ignore
+    success:result$=CL_SUCCESS
+    implied:num_events_in_wait_list=(int)event_wait_list.count()
+    tonative:event=code:event=reserve
+    onsuccess=code:event=success {
+       clEnqueueReadBuffer;
+       clEnqueueReadBufferRect;
+       clEnqueueWriteBuffer;
+       clEnqueueWriteBufferRect;
+       clEnqueueFillBuffer;
+       clEnqueueCopyBuffer;
+       clEnqueueCopyBufferRect;
+       clEnqueueReadImage;
+       clEnqueueWriteImage;
+       clEnqueueFillImage;
+       clEnqueueCopyImage;
+       clEnqueueCopyImageToBuffer;
+       clEnqueueCopyBufferToImage;
+       clEnqueueUnmapMemObject;
+       clEnqueueMigrateMemObjects;
+       clEnqueueNDRangeKernel;
+       clEnqueueNativeKernel;
+       clEnqueueMarkerWithWaitList;
+       clEnqueueBarrierWithWaitList;
+       clEnqueueSVMFree;
+       clEnqueueSVMMemcpy;
+       clEnqueueSVMMemFill;
+       clEnqueueSVMMap;
+       clEnqueueSVMUnmap;
+       clEnqueueSVMMigrateMem;
+
+       clEnqueueTask;
+}
+
+# extension with dynamic resolution
+library cl_khr_gl_event
+       template=code:class=library-dynamic
+       success:errcode_ret=CL.CL_SUCCESS {
+
+       code:<inline> {{
+       public {name} create(cl_platform_id platform, ResourceScope scope) {
+               // OpenCL 1.2+ only
+               return create((s) -> CL.clGetExtensionFunctionAddressForPlatform(platform, s), scope);
+               //return create(CL::clGetExtensionFunctionAddress, scope);
+       }
+       }}
+
+       clCreateEventFromGLsyncKHR;
+}
+
+# base constants
+define CLConstants opencl.h {
+       /.*/cl.h/             file-include;
+}
+
+# TODO: a way to manually include a value
+#  CL_HUGE_VALF float=xx?
+define CLPlatformConstants opencl.h {
+       /^CL_API_/                      exclude;
+       CL_PROGRAM_STRING_DEBUG_INFO    exclude;
+       /^__CL_/                        exclude;
+       # huge/nan/infinity in c aren't compatible with java's string representation
+       CL_HUGE_VALF|CL_HUGE_VAL|CL_NAN|CL_INFINITY     exclude;
+       /.*/cl_platform.h/              file-include;
+
+}
@@ -16,7 +16,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package proto.ffmpeg;
+package ffmpeg;
 
 import java.nio.ByteBuffer;
 
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package proto.ffmpeg;
+package ffmpeg;
 
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.BufferOverflowException;
 import jdk.incubator.foreign.*;
-import proto.ffmpeg.Memory.*;
+import au.notzed.nativez.*;
 
 public class FramePixelReader implements AVPixelReader {
 
@@ -70,19 +70,13 @@ public class FramePixelReader implements AVPixelReader {
 
                // FIXME: check sizes
 
-               try (Frame a = Memory.createFrame()) {
+               try (Frame a = Frame.frame()) {
                        IntArray stride = IntArray.createArray(4, a);
                        PointerArray data = PointerArray.createArray(4, a);
-                       ByteArray buf = ByteArray.create(buffer);
                        int res;
 
                        res = AVUtil.av_image_fill_linesizes(stride, fmt, dwidth);
-                       res = AVUtil.av_image_fill_pointers(data, fmt, h, buf, stride);
-
-                       System.out.printf(" base     %016x\n", buffer.address().toRawLongValue());
-                       for (int i=0;i<4;i++) {
-                               System.out.printf(" %d %6d %016x\n", i, stride.get(i), data.get(i).address().toRawLongValue());
-                       }
+                       res = AVUtil.av_image_fill_pointers(data, fmt, h, buffer, stride);
 
                        if (res > 0 && res <= buffer.byteSize()) {
                                ctx.scale(
@@ -96,8 +90,14 @@ public class FramePixelReader implements AVPixelReader {
                }
        }
 
+       public void getPixelsHeap(int y, int h, int fmt, MemorySegment buffer, int scanlineStride) {
+               MemorySegment dst = getSegment(bufferSize(h, fmt));
+               getPixels(y, h, fmt, dst, scanlineStride);
+               buffer.copyFrom(dst);
+       }
+
        public long bufferSize(int h, int fmt) {
-               try (Frame a = Memory.createFrame()) {
+               try (Frame a = Frame.frame()) {
                        IntArray stride = IntArray.createArray(4, a);
                        PointerArray data = PointerArray.createArray(4, a);
                        int res;
@@ -110,38 +110,22 @@ public class FramePixelReader implements AVPixelReader {
        }
 
        public void getPixels(int y, int h, int fmt, ByteBuffer buffer, int scanlineStride) {
-               getPixels(y, h, fmt, MemorySegment.ofByteBuffer((ByteBuffer)buffer), scanlineStride);
+               if (buffer.isDirect())
+                       getPixels(y, h, fmt, MemorySegment.ofByteBuffer((ByteBuffer)buffer), scanlineStride);
+               else
+                       getPixelsHeap(y, h, fmt, MemorySegment.ofByteBuffer((ByteBuffer)buffer), scanlineStride);
        }
 
        public void getPixels(int y, int h, int fmt, byte[] buffer, int offset, int scanlineStride) {
-               SwsContext ctx = getContext(fmt);
-
-               try (Frame a = Memory.createFrame()) {
-                       IntArray stride = IntArray.createArray(4, a);
-                       PointerArray data = PointerArray.createArray(4, a);
-                       MemorySegment seg = getSegment(bufferSize(h, fmt));
-                       ByteArray dst = ByteArray.create(seg);
-                       int res;
-
-                       res = AVUtil.av_image_fill_linesizes(stride, fmt, dwidth);
-                       res = AVUtil.av_image_fill_pointers(data, fmt, h, dst, stride);
-
-                       AVUtil.av_image_fill_linesizes(stride, fmt, dwidth);
-
-                       ctx.scale(
-                               frame.getData(),
-                               frame.getLinesize(),
-                               y, h,
-                               data, stride);
-
-                       MemorySegment.ofArray(buffer).copyFrom(seg);
-               }
+               getPixelsHeap(y, h, fmt, MemorySegment.ofArray(buffer).asSlice(offset), scanlineStride);
        }
 
        public void getPixels(int y, int h, int fmt, short[] buffer, int offset, int scanlineStride) {
+               getPixelsHeap(y, h, fmt, MemorySegment.ofArray(buffer).asSlice(offset * 2), scanlineStride);
        }
 
        public void getPixels(int y, int h, int fmt, int[] buffer, int offset, int scanlineStride) {
+               getPixelsHeap(y, h, fmt, MemorySegment.ofArray(buffer).asSlice(offset * 4), scanlineStride);
        }
 
        public void release() {
@@ -1,43 +1,76 @@
 
 package ffmpeg.test;
 
+import java.lang.invoke.*;
+
 import jdk.incubator.foreign.*;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import proto.ffmpeg.Memory.*;
-import proto.ffmpeg.*;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.MemoryImageSource;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import au.notzed.nativez.*;
+import ffmpeg.*;
 
-import static proto.ffmpeg.AVMediaType.*;
-import static proto.ffmpeg.AVPixelFormat.*;
+import static ffmpeg.AVMediaType.*;
+import static ffmpeg.AVPixelFormat.*;
 
 public class TestFFMPEG {
 
+       static class DataImage extends JPanel {
+
+               final int w, h, stride;
+               final MemoryImageSource source;
+               final Image image;
+               final int[] pixels;
+
+               public DataImage(int w, int h) {
+                       this.w = w;
+                       this.h = h;
+                       this.stride = (w + 15) & ~15;
+                       this.pixels = new int[stride * h];
+                       this.source = new MemoryImageSource(w, h, pixels, 0, stride);
+                       this.source.setAnimated(true);
+                       this.source.setFullBufferUpdates(true);
+                       this.image = Toolkit.getDefaultToolkit().createImage(source);
+               }
+
+               @Override
+               protected void paintComponent(Graphics g) {
+                       super.paintComponent(g);
+                       g.drawImage(image, 0, 0, this);
+               }
+       }
+
        public static void main(String[] args) throws Exception {
                System.loadLibrary("avcodec");
                System.loadLibrary("avformat");
                System.loadLibrary("swscale");
                System.loadLibrary("avutil");
 
-               try (Frame frame = Memory.createFrame();
+               try (Frame frame = Frame.frame();
                        ResourceScope scope = ResourceScope.newConfinedScope()) {
                        int res;
 
-                       if (false) {
-                               AVInputFormat avif = null;
-                               while ((avif = AVInputFormat.next(avif)) != null) {
+                       if (true) {
+                               AVInputFormat avif;
+                               PointerArray iter = PointerArray.createArray(1, frame);
+
+                               while ((avif = AVInputFormat.next(iter)) != null) {
                                        System.out.printf("name: %30s %s\n",
                                                avif.getName(),
                                                avif.getLongName());
                                }
                        }
 
+                       System.out.println("open 'movie.avi'");
                        AVFormatContext format = AVFormatContext.openInput("movie.avi", null, null, scope);
 
-                       if (format == null) {
-                               throw new IOException("File not found: movie.avi");
-                       }
-
                        format.findStreamInfo(null);
 
                        HandleArray<AVStream> streams = format.getStreams();
@@ -75,8 +108,7 @@ public class TestFFMPEG {
                        }
 
                        AVCodec codec = AVCodec.findDecoder(vcp.getCodecID());
-                       //AVCodecContext c = AVCodecContext.alloc(codec);
-                       AVCodecContext c = codec.allocContext();
+                       AVCodecContext c = AVCodecContext.alloc(codec);
                        AVFrame avframe = AVFrame.alloc();
                        AVPacket packet = AVPacket.alloc();
 
@@ -91,6 +123,11 @@ public class TestFFMPEG {
                        long nframes = 0;
                        long pts = 0;
 
+                       DataImage image = null;
+                       AVPixelReader imagepr = null;
+                       long time0 = -1 ,stamp0 = 0;
+                       AVRational timeBase = vstream.getTimeBase();
+
                        while ((res = format.readFrame(packet)) >= 0) {
                                int index = packet.getStreamIndex();
 
@@ -107,11 +144,42 @@ public class TestFFMPEG {
                                                int w = avframe.getWidth();
                                                int h = avframe.getHeight();
                                                int fmt = avframe.getFormat();
+                                               long stamp = timeBase.rescale(pts, 1000);
 
                                                if (true) {
+                                                       if (image == null) {
+                                                               JFrame window;
+
+                                                               image = new DataImage(w, h);
+                                                               window = new JFrame("movie");
+                                                               window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                                                               window.setContentPane(image);
+                                                               window.setSize(w, h);
+                                                               window.setVisible(true);
+
+                                                               imagepr = avframe.createPixelReader(w, h, 0);
+                                                       }
+
+                                                       imagepr.getPixels(0, h, AV_PIX_FMT_BGRA, image.pixels, 0, image.stride);
+
+                                                       try {
+                                                               if (time0 == -1) {
+                                                                       time0 = System.currentTimeMillis();
+                                                                       stamp0 = stamp;
+                                                               }
+
+                                                               long diff = (stamp - stamp0) - (System.currentTimeMillis() - time0);
+                                                               if (diff > 0)
+                                                                       Thread.sleep(diff);
+                                                       } catch (InterruptedException ex) {
+                                                       }
+
+                                                       image.source.newPixels();
+                                               }
+
+                                               if (false) {
                                                        if (pix == null) {
-                                                               //pix = frame.getPixelReader(w, h, 0);
-                                                               pix = new FramePixelReader(avframe, w, h, 0);
+                                                               pix = avframe.createPixelReader(w, h, 0);
                                                        }
 
                                                        byte[] pixels = new byte[w*h*3];
@@ -123,14 +191,14 @@ public class TestFFMPEG {
                                                                fos.write(header.getBytes());
                                                                fos.write(pixels);
                                                        }
-                                               }
 
-                                               System.out.printf("%06d video frame %dx%d @ %d\n", nframes, w, h, fmt);
-                                               nframes++;
+                                                       System.out.printf("%06d video frame %dx%d @ %d\n", nframes, w, h, fmt);
+                                                       nframes++;
+                                                       if (nframes == 10)
+                                                               break;
+                                               }
                                        }
                                }
-                               if (nframes == 10)
-                                       break;
                        }
                }
        }
diff --git a/src/notzed.ffmpeg/classes/module-info.java b/src/notzed.ffmpeg/classes/module-info.java
new file mode 100644 (file)
index 0000000..a156a85
--- /dev/null
@@ -0,0 +1,7 @@
+
+module notzed.ffmpeg {
+       requires transitive notzed.nativez;
+       requires java.desktop;
+
+       exports ffmpeg;
+}
diff --git a/src/notzed.ffmpeg/gen/ffmpeg.api b/src/notzed.ffmpeg/gen/ffmpeg.api
new file mode 100644 (file)
index 0000000..a003ecb
--- /dev/null
@@ -0,0 +1,225 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+include code.api
+include types.api
+
+struct <default> field:rename=studly-caps default=all func:rename=camel-case access=rw {
+}
+
+struct AVFormatContext default=none func:rename=s/^(avformat_|av_)//,camel-case field:rename=studly-caps {
+       iformat         access=r rename=InputFormat;
+       oformat         access=r rename=OutputFormat;
+       pb                      rename=IOContext;
+
+       ctx_flags       access=r;
+       nb_streams      access=r rename=NumStreams;
+
+       streams         array-size=nb_streams array;
+
+       start_time      access=r;
+       duration        access=r;
+       bit_rate        access=r;
+
+       interrupt_callback;
+
+       func:avformat_open_input                scope:0=explicit success:result$=0 return:0 array:options;
+       func:avformat_close_input               raw-in:0 rename=close;
+
+       func:avformat_find_stream_info  instance:0;
+       func:av_read_frame                              instance:0;
+}
+
+struct AVStream default=none field:rename=studly-caps {
+       index                                   access=r;
+       id                                              access=r rename=ID;
+       time_base;
+       start_time;
+       duration;
+       nb_frames                               rename=NumFrames;
+       discard;
+       avg_frame_rate                  rename=AverageFrameRate;
+       sample_aspect_ratio;
+       codecpar                                rename=CodecParameters;
+}
+
+struct AVCodec access=r default=none func:rename=s/^avcodec_//,camel-case field:rename=studly-caps {
+       id                       rename=ID;
+       name;
+       long_name;
+       type;
+       capabilities;
+       max_lowres;
+       # need some sort of length=null-terminated here i suppose;
+       supported_framerates    rename=framerates;
+       pix_fmts                                rename=pixelFormats;
+
+       func:av_codec_next                                      rename=next;
+       func:avcodec_find_decoder                       ;
+       func:avcodec_find_decoder_by_name       ;
+
+       define:AVCodecBits;
+}
+
+struct AVCodecContext default=none func:rename=s/^avcodec_//,camel-case field:rename=studly-caps {
+       codec_id      rename=CodecID;
+
+       skip_loop_filter;
+       skip_idct;
+       skip_frame;
+
+       func:avcodec_alloc_context3             rename=alloc;
+       func:avcodec_open2                              instance:0 rename=open;
+       func:avcodec_send_packet                instance:0;
+       func:avcodec_receive_packet             instance:0;
+       func:avcodec_send_frame                 instance:0;
+       func:avcodec_receive_frame              instance:0;
+
+       define:AVCodecContextBits;
+}
+
+struct AVFrame default=all func:rename=s/^av_frame_//,camel-case field:rename=studly-caps {
+       func:av_frame_alloc;
+       func:av_frame_free                              instance:0;
+
+       code:<inline> {{
+       public AVPixelReader createPixelReader(int w, int h, int flags) {
+               return new FramePixelReader(this, w, h, 0);
+       }
+       }}
+}
+
+struct AVFrameSideData default=none {
+}
+
+struct AVRational field:rename=studly-caps {
+       func:av_rescale;
+
+       code:<inline> {{
+       public long rescale(long ts, int scale) {
+               return av_rescale(ts, scale * getNum(), getDen());
+       }
+       }}
+}
+
+struct AVCodecParameters func:rename=s/^avcodec_parameters_//,camel-case field:rename=studly-caps {
+       codec_id                 rename=CodecID;
+
+       func:avcodec_parameters_alloc;
+       func:avcodec_parameters_free                    instance:0;
+       func:avcodec_parameters_copy                    instance:0;
+       func:avcodec_parameters_to_context              instance:1;
+       func:avcodec_parameters_from_context    instance:0;
+}
+
+struct AVPacket default=all func:rename=s/^av_packet_//,camel-case field:rename=studly-caps {
+       pts|dts    rename=upper-leadin;
+       data       array-size=size;
+
+       func:av_packet_alloc;
+       func:av_packet_free             instance:0;
+       func:av_init_packet             instance:0 rename=init;
+}
+
+struct SwsContext func:rename=s/^sws_// {
+#       func:/sws_/;
+       func:sws_getContext;
+       func:sws_freeContext instance:0;
+       func:sws_scale instance:0;
+
+       define:sws;
+}
+
+struct SwsFilter {
+}
+struct SwsVector {
+}
+struct AVPixFmtDescriptor {
+}
+struct AVComponentDescriptor access=rwi {
+}
+
+library AVUtil {
+       av_image_copy_plane segment:src segment:dst;
+       av_image_copy_to_buffer segment:dst;
+       av_image_fill_arrays segment:src;
+       av_image_fill_pointers segment:ptr;
+       /av_image_/;
+}
+
+struct AVProbeData default=none field:rename=studly-caps {
+}
+
+struct AVBufferRef access=rwi default=none {
+}
+struct AVPacketSideData default=none {
+}
+struct AVIOContext default=none {
+}
+struct AVIOInterruptCB default=none {
+}
+
+struct AVDictionaryEntry {
+}
+
+struct AVDictionary func:rename=s/^av_dict_//,camel-case {
+       define:dict;
+       func:/av_dict_/;
+}
+
+struct AVInputFormat default=none access=r field:rename=studly-caps {
+       name;
+       long_name;
+       flags;
+       extensions;
+       mime_type;
+
+       func:av_demuxer_iterate                 rename=next;
+       func:av_register_input_format   instance:0 rename=register;
+       func:av_find_input_format               rename=find;
+}
+
+struct AVOutputFormat default=none access=r field:rename=studly-caps {
+       name;
+       long_name;
+       mime_type;
+       flags;
+       extensions;
+       audio_codec;
+       video_codec;
+       subtitle_codec;
+
+       func:av_muxer_iterate         rename=next;
+}
+
+define dict ffmpeg.h {
+       /AV_DICT_/;
+}
+
+define AVChannelLayoutBits ffmpeg.h {
+       /^AV_CH_LAYOUT_/        x64;
+}
+
+define AVErrorBits ffmpeg.h {
+       /^AVERROR_/             x32;
+}
+
+define AVCodecContextBits ffmpeg.h {
+       /^AV_CODEC_FLAG_|AV_CODEC_FLAG2_/       u32;
+       /^AV_INPUT_BUFFER_/     i32;
+}
+
+define AVCodecBits ffmpeg.h {
+       /^AV_CODEC_CAP_/        x32;
+}
+
+define AVIOContextBits ffmpeg.h {
+       /^AVSEEK_|AVIO_FLAG_|AVIO_SEEKABLE_/;
+}
+
+define AVOptionsBits ffmpeg.h {
+       /^AV_OPT_/;
+}
+
+define sws ffmpeg.h {
+       /^SWS_/;
+}
diff --git a/src/notzed.ffmpeg/gen/ffmpeg.h b/src/notzed.ffmpeg/gen/ffmpeg.h
new file mode 100644 (file)
index 0000000..9d1868b
--- /dev/null
@@ -0,0 +1,27 @@
+
+struct bob {
+       int jane[4][4];
+       int a, b;
+};
+
+struct ZFoo {
+       char *p;
+       int q:5;
+       unsigned int r:3;
+       int *a;
+       unsigned int *b;
+       float **c;
+       double **d;
+       struct bob *x;
+       struct bob **y;
+
+
+       struct bob z[2];
+};
+
+
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/opt.h>
diff --git a/src/notzed.ffmpeg/gen/gen.make b/src/notzed.ffmpeg/gen/gen.make
new file mode 100644 (file)
index 0000000..4b7855a
--- /dev/null
@@ -0,0 +1,3 @@
+
+notzed.ffmpeg_API = ffmpeg
+notzed.ffmpeg_APIFLAGS = -t ffmpeg -I$(FFMPEG_HOME)/include -Isrc/notzed.ffmpeg/gen
diff --git a/src/notzed.nativez/bin/export-api b/src/notzed.nativez/bin/export-api
new file mode 100755 (executable)
index 0000000..5823932
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+
+# meta script to run everything at once.
+
+# TODO: add an option that dumps out the make dependencies
+
+use strict;
+use File::Basename;
+#use autodie qw(system);                       # super-ugly and unecessary output though
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+
+use Data::Dumper;
+use File::Path qw(make_path);
+
+use config;
+
+my $apidef;
+my $apibase;
+my $apih;
+my $var = {
+       package => 'api',
+       output => 'bin',
+       workdir => 'bin',
+       verbose => 0,
+       include => [],
+};
+
+while (@ARGV) {
+       my $cmd = shift(@ARGV);
+
+       if ($cmd =~ m/^(-[^-])(.+)/) {
+               $cmd = $1;
+               unshift @ARGV, $2;
+       }
+
+       if ($cmd eq "-t") {
+               $var->{package} = shift;
+    } elsif ($cmd eq "-d") {
+               $var->{output} = shift;
+    } elsif ($cmd eq "-w") {
+               $var->{workdir} = shift;
+    } elsif ($cmd eq "-I") {
+               push @{$var->{include}}, shift;
+    } elsif ($cmd eq "-v") {
+               $var->{verbose}++;
+       } else {
+               $apidef = $cmd;
+               $apih = "$1.h" if ($apidef =~ m/^(.*).api$/);
+               $apibase = basename($apidef, '.api');
+
+       #       if ($apidef =~ m/^(.*).api$/) {
+       #               $apih = $1;
+       #       } else {
+       #               die ("api definition must end in '.api'");
+       #       }
+       }
+}
+
+push @{$var->{include}}, "$FindBin::Bin/../lib";
+
+print Dumper($var);
+
+die ("Missing config argument") if !defined($apidef);
+die ("Unable to find config: $apidef") if !-f $apidef;
+die ("Unable to find matching header for: $apidef") if !-f $apih;
+
+my $api = new config($var, $apidef);
+
+my @includes = map { ('-I', $_ ) } @{$var->{include}};
+my @cmd = (
+       "gcc",
+       "-fplugin=$FindBin::Bin/../lib/libexport.so",
+       "-fplugin-arg-libexport-output=$var->{workdir}/$apibase.pm",
+#      "-fplugin-arg-libexport-verbose=$var->{verbose}",
+       "-O0",
+       "-o",
+       "/dev/null",
+       @includes,
+       "$apih"
+);
+
+print join " ", @cmd, "\n";
+system(@cmd) == 0 || die("command failed");
+
+my @defines = ();
+if (grep { $_->{type} eq 'define' } @{$api->{objects}}) {
+       @cmd = (
+               "$FindBin::Bin/export-defines",
+               '--hack-new-format-2',
+               $var->{verbose} ? '-v' : (),
+               '-o',
+               "$var->{workdir}/$apibase-defines.pm",
+               @includes,
+               $apidef
+       );
+
+       print join " ", @cmd, "\n";
+       system(@cmd) == 0 || die("command failed");
+       push @defines, '-a', "./$var->{workdir}/$apibase-defines.pm";
+}
+
+@cmd = (
+       "$FindBin::Bin/generate-api",
+       $var->{verbose} ? '-v' : (),
+       '-t', $var->{package},
+       '-d', $var->{output},
+       '-a', "./$var->{workdir}/$apibase.pm",
+       @defines,
+       $apidef
+);
+print join " ", @cmd, "\n";
+system(@cmd) == 0 || die("command failed");
similarity index 94%
rename from src/export-defines
rename to src/notzed.nativez/bin/export-defines
index 1774d21..7b986e0 100755 (executable)
@@ -1,14 +1,13 @@
 #!/usr/bin/perl
 
-use File::Basename;
+use FindBin;
+use lib "$FindBin::Bin/../lib";
 
+use File::Basename;
 use Data::Dumper;
 
-my $scriptPath = dirname(__FILE__);
-push @INC,$scriptPath;
-
-require genconfig;
-require genconfig2;
+#require genconfig;
+require config;
 
 my @includes = ();
 my $header;
@@ -26,7 +25,7 @@ while (@ARGV) {
 
        if ($cmd eq "-t") {
                $package = shift;
-    } elsif ($cmd eq "-d") {
+    } elsif ($cmd eq "-o") {
                $output = shift;
     } elsif ($cmd eq "-v") {
                $verbose++;
@@ -46,15 +45,15 @@ die ("no output specified") if !$output;
 my $defs;
 my @xports;
 if ($hackformat == 1) {
-       $defs = genconfig::loadControlFile($control);
-       @exports = grep { $_->{type} eq 'define' } @{$defs};
+       #$defs = genconfig::loadControlFile($control);
+       #@exports = grep { $_->{type} eq 'define' } @{$defs};
 } elsif ($hackformat == 2) {
-       push @includes, $scriptPath;
-       my $conf = new genconfig2({ include => \@includes }, $control);
+       #push @includes, "$FindBin::Bin/../lib";
+       my $conf = new config({ include => \@includes }, $control);
        $defs = $conf->{objects};
        @exports = grep { $_->{type} eq 'define' } @{$defs};
        foreach $export (@exports) {
-               $export->{options}->[0] = $conf->findInclude($export->{options}->[0]);
+               $export->{import} = $conf->findInclude($export->{options}->[0]);
        }
 } else {
        $defs = loadControlFile($control);
@@ -126,7 +125,7 @@ foreach $export (@exports) {
 # load all defines once and link in
 # keep_comments is a bit broken
 foreach $export (@exports) {
-       my $header = $export->{header};
+       my $header = $export->{import};
 
        if (!defined($rawDefines{$header})) {
                $rawDefines{$header} = scanDefines($header, { CPPFLAGS=>$CPPFLAGS, keep_comments=>0 });
@@ -288,9 +287,9 @@ sub scanDefines {
        my $source;
        my $sourceLine;
 
-    print STDERR "Scanning $header\n";
+    print "Scanning $header\n";
 
-    print STDERR "cpp -dD ".($o->{keep_comments} ? '-CC' : '')." $o->{CPPFLAGS} $header\n";
+    print "cpp -dD ".($o->{keep_comments} ? '-CC' : '')." $o->{CPPFLAGS} $header\n";
 
     open (my $in,"-|","cpp -dD ".($o->{keep_comments} ? '-CC' : '')." $o->{CPPFLAGS} $header") // die("Can't find include file: $header");
     while (<$in>) {
similarity index 71%
rename from src/generate-api-2
rename to src/notzed.nativez/bin/generate-api
index df765b2..9d6a74f 100755 (executable)
@@ -14,15 +14,14 @@ use File::Basename;
 use strict;
 
 use Carp 'verbose';
-$SIG{ __DIE__ } = sub { Carp::confess( @_ ) };
-
-my $scriptPath = dirname(__FILE__);
-push @INC,$scriptPath;
+use FindBin;
+use lib "$FindBin::Bin/../lib";
 
-require genapi;
-require code;
-require method;
+use api;
+use code;
+use method;
 
+$SIG{ __DIE__ } = sub { Carp::confess( @_ ) };
 $Data::Dumper::Indent = 1;
 
 my $apidef = "api.api";
@@ -56,54 +55,23 @@ while (@ARGV) {
        }
 }
 
-push @{$vars->{include}}, $scriptPath;
+push @{$vars->{include}}, "$FindBin::Bin/../lib";
 
 print Dumper($vars) if $vars->{verbose};
 
-my $api = new genapi($apidef, $vars, @apilist);
+my $api = new api($apidef, $vars, @apilist);
 
 #print Dumper($api);
 
-# TODO: make a module for this.
-copySkeletonFile($api, 'Memory.java');
-copySkeletonFile($api, 'Frame.java');
-
 exportLibraries($api);
 exportStructs($api);
 exportConstants($api);
 exit 0;
 
-# copies a skeleton file and patches it to the target package
-sub copySkeletonFile {
-       my $api = shift;
-       my $name = shift;
-       my $src = "$scriptPath/template/$name";
-       my $dst = $api->{vars}->{package};
-
-       $dst =~ s@\.@/@g;
-       $dst = "$api->{vars}->{output}/$dst/$name";
-
-       make_path(dirname($dst));
-
-       open (my $d, ">", $dst.'~') || die ("Cannot open '$src' for writing");
-       open (my $s, "<", $src) || die ("Cannot open '$dst' for reading");
-
-       while (<$s>) {
-               s/^package .*;/package $api->{vars}->{package};/o;
-               print $d $_;
-       }
-
-       close $s;
-       close $d;
-
-       rename ($dst.'~', $dst) || die ("unable to rename $d: $!");
-}
-
 sub formatFunction {
        my $api = shift;
        my $c = shift;
-       my $template = $api->{index}->{'code:method'};
-       my $invoke = genapi::findItem($template, 'invoke');
+       my $template = shift;
        my $info = new method($api, $c);
 
        #print 'function='.Dumper($c);
@@ -112,11 +80,11 @@ sub formatFunction {
 
        my $code;
 
-       foreach my $l (split /\n/,Dumper($info->{vars}).Dumper($info->{arguments})) {
-               $code .= "// $l\n";
-       }
+       #foreach my $l (split /\n/,Dumper($info->{vars}).Dumper($info->{arguments})) {
+       #       $code .= "// $l\n";
+       #}
 
-       $code .= code::applyTemplate($invoke, $info->{vars});
+       $code .= code::applyTemplate($template, $info->{vars});
        $code =~ s/^\s*\n//osgm;
 
        return $code;
@@ -126,8 +94,8 @@ sub formatCall {
        my $api = shift;
        my $c = shift;
        my $template = $api->{index}->{'code:method'};
-       my $upcall = genapi::findItem($template, 'upcall');
-       my $downcall = genapi::findItem($template, 'downcall');
+       my $upcall = api::findItem($template, 'upcall');
+       my $downcall = api::findItem($template, 'downcall');
        my $info = new method($api, $c);
 
        #print 'function='.Dumper($c);
@@ -141,11 +109,12 @@ sub formatCall {
        $info->{vars}->{downcall} = ($c->{access} =~ m/r/) ? code::applyTemplate($downcall, $info->{vars}) : '';
        $info->{vars}->{upcall} = ($c->{access} =~ m/w/) ? code::applyTemplate($upcall, $info->{vars}) : '';
 
-       return $code.code::applyTemplate(genapi::findItem($api->{index}->{'code:class'}, 'call'), $info->{vars});
+       return $code.code::applyTemplate(api::findItem($api->{index}->{'code:class'}, 'call'), $info->{vars});
 }
 
 sub formatItems {
        my $api = shift;
+       my $obj = shift;
        my $inc = shift;
        my $res = shift;
        my @list;
@@ -156,7 +125,14 @@ sub formatItems {
        } $api->findMatches($inc);
 
        if ($inc->{type} eq 'func') {
-               push @{$res->{func}}, map { formatFunction($api, $_) } @list;
+               my $def = $api->{index}->{'func:<default>'};
+               my $func = api::optionValue('func:template', 'code:method=invoke', $inc, $res->{template});
+               my $init = api::optionValue('init:template', undef, $inc, $res->{template});
+               my $funct = findTemplateName($api, $func);
+               my $initt = findTemplateName($api, $init) if defined $init;
+
+               push @{$res->{func}}, map { formatFunction($api, $_, $funct) } @list;
+               push @{$res->{init}}, map { formatFunction($api, $_, $initt) } @list if defined($initt);
        } elsif ($inc->{type} eq 'define') {
                push @{$res->{define}}, map { code::formatDefine($api, $_) } @list;
        } elsif ($inc->{type} eq 'enum') {
@@ -175,34 +151,54 @@ sub formatLibrary {
        my $data = $api->{data};
        my $d;
 
-       print "library $obj->{name}\n";
+       print "library $obj->{name}\n" if ($api->{vars}->{verbose} > 0);
 
        foreach my $inc (@{$obj->{items}}) {
                if ($inc->{type} eq 'library') {
                        $api->{output}->{"$inc->{match}"}++;
                        formatLibrary($api, $api->{index}->{$inc->{match}}, $res);
-               } elsif ($inc->{type} =~ m/func|call|define|enum/) {
-                       print "  $inc->{match}\n";
-                       formatItems($api, $inc, $res);
+               } elsif ($inc->{type} =~ m/^(func|call|define|enum)$/no) {
+                       print "  $inc->{match}\n" if ($api->{vars}->{verbose} > 1);
+                       formatItems($api, $obj, $inc, $res);
+               } elsif ($inc->{match} eq 'code:<inline>') {
+                       print "  $inc->{match}\n" if ($api->{vars}->{verbose} > 1);
+                       push @{$res->{func}}, $inc->{literal};
+               } elsif ($inc->{type} eq 'code') {
+                       # apply template perhaps, or apply with set?
+                       push @{$res->{func}}, map {
+                               if (defined($inc->{options}->[0])) {
+                                       api::findItem($_, $inc->{options}->[0])->{literal};
+                               } else {
+                                       $_->{literal}
+                               }
+                       } grep { $_->{match} =~ m/$inc->{regex}/ } @{$api->{api}};
                } elsif ($inc->{type} ne 'field') {
+                       print Dumper($inc);
                        die;
                }
        }
 }
 
+sub findTemplateName {
+       my $api = shift;
+       my $name = shift;
+
+       if ($name =~ m/^(.+)=(.+)$/) {
+               my $template = api::findItem($api->{index}->{$1}, $2);
+               return $template if defined $template;
+       }
+       die "can't find template '$name'\n";
+}
+
 sub findTemplate {
        my $api = shift;
        my $obj = shift;
        my $s = shift;
        my $data = $api->{data};
        my $def = $api->{index}->{"$s->{type}:<default>"};
-       my $tmp = genapi::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
-
-       if ($tmp =~ m/^(.+)=(.+)$/) {
-               return genapi::findItem($api->{index}->{$1}, $2);
-       }
-       die;
+       my $name = api::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
 
+       return findTemplateName($api, $name);
 }
 
 # TODO: embedded structs
@@ -213,7 +209,6 @@ sub formatStruct {
        my $data = $api->{data};
        my $seen = {};
        my $structTemplate = findTemplate($api, $obj, $s);
-
        my @members = code::scanFields($api, $s);
        my @membersOutput = grep { $_->{field}->{output} } @members;
 
@@ -223,7 +218,7 @@ sub formatStruct {
                map {
                        my $accessor = $api->{index}->{$_};
 
-                       map { code::applyTemplate($_, $match) } grep { $_ } map { genapi::findItem($accessor, $_) } map {
+                       map { code::applyTemplate($_, $match) } grep { $_ } map { api::findItem($accessor, $_) } map {
                                my @list = ();
                                if ($_ =~ m/r/o) {
                                        push @list, 'get';
@@ -235,7 +230,7 @@ sub formatStruct {
                                }
                                @list;
                        } $m->{access};
-               } split(/,/,genapi::optionValue('template', undef, $type));
+               } split(/,/,api::optionValue('template', undef, $type));
        } @membersOutput;
 
        my $res = {
@@ -244,7 +239,8 @@ sub formatStruct {
                define => [],
                enum => [],
                call => [],
-               seen => {}
+               seen => {},
+               template => $structTemplate,
        };
 
        formatLibrary($api, $obj, $res);
@@ -275,20 +271,22 @@ sub formatStruct {
 sub exportLibraries {
        my $api = shift;
        my $data = $api->{data};
-       my $template = $api->{index}->{'code:class'};
-       my $library = genapi::findItem($template, 'library');
+       my $def = $api->{index}->{'library:<default>'};
 
        foreach my $obj (grep { $_->{type} eq 'library' } @{$api->{api}}) {
                next if $api->{output}->{"$obj->{type}:$obj->{name}"};
                next if $obj->{output} != 1;
 
+               my $library = findTemplateName($api, api::optionValue('template', 'code:class=library', $obj, $def));
                my $res = {
                        library => [],
                        func => [],
+                       init => [],
                        define => [],
                        enum => [],
                        call => [],
-                       seen => {}
+                       seen => {},
+                       template => $library,
                };
 
                formatLibrary($api, $obj, $res);
@@ -296,6 +294,7 @@ sub exportLibraries {
                my $vars = {
                        %{$api->{vars}},
                        name => $obj->{name},
+                       init => join("\n", @{$res->{init}}),
                        defines => join("\n", @{$res->{define}}),
                        enums => join("\n", @{$res->{enum}}),
                        funcs => join("\n", @{$res->{func}}),
@@ -338,10 +337,10 @@ sub exportStructs {
        foreach my $obj (grep { $_->{type} =~ m/call|struct|union/ } @{$api->{api}}) {
                my @list = $api->findMatches($obj);
 
-               print "gen ".($#list+1)." $obj->{type} $obj->{name}\n";
+               print "gen ".($#list+1)." $obj->{type} $obj->{name}\n" if ($api->{vars}->{verbose} > 0);
                foreach my $s (@list) {
                        next if $api->{output}->{"$s->{type}:$s->{name}"};
-                       print "  $s->{name}\n";
+                       print "  $s->{name}\n" if ($api->{vars}->{verbose} > 1);
 
                        export($api, $s->{rename}, formatClass($api, $obj, $s));
                }
@@ -360,7 +359,7 @@ sub exportConstants {
        my $api = shift;
        my $data = $api->{data};
        my $template = $api->{index}->{'code:class'};
-       my $constant = genapi::findItem($template, 'constants');
+       my $constant = api::findItem($template, 'constants');
 
        # hmm, not sure if i should use obj or just the values directly here
        foreach my $s (grep { $_->{type} =~ m/define|enum/ } values %{$api->{data}}) {
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Array.java b/src/notzed.nativez/classes/au/notzed/nativez/Array.java
new file mode 100644 (file)
index 0000000..30d0f7c
--- /dev/null
@@ -0,0 +1,7 @@
+
+package au.notzed.nativez;
+
+public interface Array<T> {
+       long length();
+       T getAtIndex(long i);
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/ByteArray.java b/src/notzed.nativez/classes/au/notzed/nativez/ByteArray.java
new file mode 100644 (file)
index 0000000..bfe21da
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class ByteArray extends AbstractList<Byte> implements Pointer {
+       final MemorySegment segment;
+
+       private ByteArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static ByteArray create(MemorySegment segment) {
+               return new ByteArray(segment);
+       }
+
+       public static ByteArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length, scope));
+       }
+
+       public static ByteArray createArray(MemoryAddress address, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
+       }
+
+       public static ByteArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.BYTE, length));
+       }
+
+       public static ByteArray create(String value, SegmentAllocator alloc) {
+               return create(alloc.allocateUtf8String(value));
+       }
+
+       public static ByteArray create(String value, ResourceScope scope) {
+               return create(SegmentAllocator.nativeAllocator(scope).allocateUtf8String(value));
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public Byte get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public Byte set(int index, Byte value) {
+               byte old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.BYTE.byteSize();
+       }
+
+       public byte getAtIndex(long index) {
+               return (byte)segment.get(Memory.BYTE, index);
+       }
+
+       public void setAtIndex(long index, byte value) {
+               segment.set(Memory.BYTE, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/DoubleArray.java b/src/notzed.nativez/classes/au/notzed/nativez/DoubleArray.java
new file mode 100644 (file)
index 0000000..9a0bfce
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class DoubleArray extends AbstractList<Double> implements Pointer {
+       final MemorySegment segment;
+
+       private DoubleArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static DoubleArray create(MemorySegment segment) {
+               return new DoubleArray(segment);
+       }
+
+       public static DoubleArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length * Memory.DOUBLE.byteSize(), scope));
+       }
+
+       public static DoubleArray createArray(MemoryAddress address, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
+       }
+
+       public static DoubleArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.DOUBLE, length));
+       }
+
+       public static DoubleArray create(SegmentAllocator alloc, double... values) {
+               return create(alloc.allocateArray(Memory.DOUBLE, values));
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public Double get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public Double set(int index, Double value) {
+               double old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.DOUBLE.byteSize();
+       }
+
+       public double getAtIndex(long index) {
+               return segment.getAtIndex(Memory.DOUBLE, index);
+       }
+
+       public void setAtIndex(long index, double value) {
+               segment.setAtIndex(Memory.DOUBLE, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/FloatArray.java b/src/notzed.nativez/classes/au/notzed/nativez/FloatArray.java
new file mode 100644 (file)
index 0000000..e81a57e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class FloatArray extends AbstractList<Float> implements Pointer {
+       final MemorySegment segment;
+
+       private FloatArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static FloatArray create(MemorySegment segment) {
+               return new FloatArray(segment);
+       }
+
+       public static FloatArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length * Memory.FLOAT.byteSize(), scope));
+       }
+
+       public static FloatArray createArray(MemoryAddress address, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
+       }
+
+       public static FloatArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.FLOAT, length));
+       }
+
+       public static FloatArray create(SegmentAllocator alloc, float... values) {
+               return create(alloc.allocateArray(Memory.FLOAT, values));
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public Float get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public Float set(int index, Float value) {
+               float old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.FLOAT.byteSize();
+       }
+
+       public float getAtIndex(long index) {
+               return segment.getAtIndex(Memory.FLOAT, index);
+       }
+
+       public void setAtIndex(long index, float value) {
+               segment.setAtIndex(Memory.FLOAT, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Frame.java b/src/notzed.nativez/classes/au/notzed/nativez/Frame.java
new file mode 100644 (file)
index 0000000..e45032e
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package au.notzed.nativez;
+
+import java.lang.ref.Cleaner;
+import jdk.incubator.foreign.*;
+import static jdk.incubator.foreign.ValueLayout.OfAddress;
+
+/**
+ * This is a per-thread stack-based allocator.
+ * <pre>
+ * try (Frame f = Memory.createFrame()) {
+ *             MemorySegment a = f.allocate(size);
+ * }
+ * </pre>
+ * Any memory allocated is freed when the frame is closed.
+ * <p>
+ * This is quite a bit faster than using an arena allocator.
+ */
+public class Frame implements AutoCloseable, SegmentAllocator {
+
+       private final long tos;
+       private Stack stack;
+       private ResourceScope overflow;
+
+       Frame(long tos, Stack stack) {
+               this.tos = tos;
+               this.stack = stack;
+       }
+
+       private static final ResourceScope scope = ResourceScope.newSharedScope(Cleaner.create());
+       private static final ThreadLocal<Stack> stacks = ThreadLocal.withInitial(() -> new Stack(scope));
+
+       public static Frame frame() {
+               return stacks.get().createFrame();
+       }
+
+       private static class Stack {
+               private final MemorySegment stack;
+               private long sp;
+               private Thread thread = Thread.currentThread();
+
+               Stack(ResourceScope scope) {
+                       stack = MemorySegment.allocateNative(4096, 4096, scope);
+                       sp = 4096;
+               }
+               Frame createFrame() {
+                       return new Frame(sp, this);
+               }
+       }
+
+       @Override
+       public MemorySegment allocate(long size, long alignment) {
+               if (stack.thread != Thread.currentThread())
+                       throw new IllegalStateException();
+               if (alignment != Long.highestOneBit(alignment))
+                       throw new IllegalArgumentException();
+               if (stack.sp >= size) {
+                       stack.sp = (stack.sp - size) & ~(alignment - 1);
+                       return stack.stack.asSlice(stack.sp, size).fill((byte)0);
+               } else {
+                       // or arena allocator?
+                       if (overflow == null)
+                               overflow = ResourceScope.newConfinedScope();
+                       return MemorySegment.allocateNative(size, alignment, overflow);
+               }
+       }
+
+       @Override
+       public void close() {
+               stack.sp = tos;
+               stack = null;
+               if (overflow != null) {
+                       overflow.close();
+                       overflow = null;
+               }
+       }
+
+       public MemorySegment allocateInt() {
+               return allocate(Memory.INT);
+       }
+
+       public MemorySegment allocateInt(int count) {
+               return allocate(Memory.INT, count);
+       }
+
+       public MemorySegment allocateLong() {
+               return allocate(Memory.LONG);
+       }
+
+       public MemorySegment allocateLong(int count) {
+               return allocateArray(Memory.LONG, count);
+       }
+
+       public MemorySegment allocatePointer() {
+               return allocate(Memory.POINTER);
+       }
+
+       public MemorySegment allocatePointer(int count) {
+               return allocateArray(Memory.POINTER, count);
+       }
+
+       public MemorySegment allocateArray(OfAddress type, MemoryAddress[] value) {
+               MemorySegment m = allocateArray(type, value.length);
+               for (int i=0;i<value.length;i++)
+                       m.setAtIndex(type, i, value[i]);
+               return m;
+       }
+
+       public MemorySegment copy(byte value) {
+               return allocate(Memory.BYTE, value);
+       }
+
+       public MemorySegment copy(short value) {
+               return allocate(Memory.SHORT, value);
+       }
+
+       public MemorySegment copy(int value) {
+               return allocate(Memory.INT, value);
+       }
+
+       public MemorySegment copy(long value) {
+               return allocate(Memory.LONG, value);
+       }
+
+       public MemorySegment copy(float value) {
+               return allocate(Memory.FLOAT, value);
+       }
+
+       public MemorySegment copy(double value) {
+               return allocate(Memory.DOUBLE, value);
+       }
+
+       public MemorySegment copy(byte[] value) {
+               return allocateArray(Memory.BYTE, value);
+       }
+
+       public MemorySegment copy(int[] value) {
+               return allocateArray(Memory.INT, value);
+       }
+
+       public MemorySegment copy(long[] value) {
+               return allocateArray(Memory.LONG, value);
+       }
+
+       public MemorySegment copy(float[] value) {
+               return allocateArray(Memory.FLOAT, value);
+       }
+
+       public MemorySegment copy(String value) {
+               return allocateUtf8String(value);
+       }
+
+       /*
+       public <T extends Native> MemorySegment copy(T[] array) {
+               MemorySegment mem = allocateAddress(array.length);
+               for (int i = 0; i < array.length; i++)
+                       MemoryAccess.setAddressAtIndex(mem, i, array[i].address());
+               return mem;
+       }
+
+       public <T extends Native> MemorySegment copy(T value) {
+               return copy(value.address());
+       }
+
+       public <T extends Native> MemorySegment copy(MemoryAddress value) {
+               MemorySegment mem = allocateAddress();
+               MemoryAccess.setAddress(mem, value);
+               return mem;
+       }
+        */
+       // create an array pointing to strings
+       public MemorySegment copy(String[] array) {
+               if (array != null) {
+                       MemorySegment list = allocatePointer(array.length);
+                       for (int i = 0; i < array.length; i++) {
+                               list.setAtIndex(Memory.POINTER, i, copy(array[i]));
+                       }
+                       return list;
+               } else {
+                       return Memory.NULL;
+               }
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/FunctionPointer.java b/src/notzed.nativez/classes/au/notzed/nativez/FunctionPointer.java
new file mode 100644 (file)
index 0000000..dd36722
--- /dev/null
@@ -0,0 +1,7 @@
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.NativeSymbol;
+
+public record FunctionPointer<T>(NativeSymbol symbol, T function) {
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java b/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java
new file mode 100644 (file)
index 0000000..3f7d299
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+/**
+ * A HandleArray is a typed PointerArray
+ * It's flexible and can be used to represent multiple levels of typed arrays, etc.
+ *
+ * The supplied ResourceScope is used for any retrieved entries.
+ */
+public class HandleArray<T extends Pointer> extends AbstractList<T> implements Pointer {
+       public final MemorySegment segment;
+       final ResourceScope scope;
+       BiFunction<MemoryAddress,ResourceScope,T> create;
+
+       private HandleArray(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
+               this.segment = segment;
+               this.create = create;
+               this.scope = scope;
+       }
+
+       public static <T extends Pointer> HandleArray<T> create(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create) {
+               return new HandleArray<>(segment, create, segment.scope());
+       }
+
+       public static <T extends Pointer> HandleArray<T> create(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
+               return new HandleArray<>(segment, create, scope);
+       }
+
+       public static <T extends Pointer> HandleArray<T> createArray(long size, SegmentAllocator alloc, BiFunction<MemoryAddress,ResourceScope,T> create) {
+               return create(alloc.allocateArray(Memory.POINTER, size), create);
+       }
+
+       public static <T extends Pointer> HandleArray<T> createArray(long size, SegmentAllocator alloc, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
+               return create(alloc.allocateArray(Memory.POINTER, size), create, scope);
+       }
+
+       public static <T extends Pointer> HandleArray<T> createArray(MemoryAddress address, long size, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, size * Memory.POINTER.byteSize(), scope), create);
+       }
+
+       @Override
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public T get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public T set(int index, T value) {
+               T old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.POINTER.byteSize();
+       }
+
+       public T getAtIndex(long index) {
+               MemoryAddress ptr = segment.getAtIndex(Memory.POINTER, index);
+               return ptr != null ? create.apply(ptr, scope) : null;
+       }
+
+       public void setAtIndex(long index, T value) {
+               segment.setAtIndex(Memory.POINTER, index, value != null ? value.address() : MemoryAddress.NULL);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/IntArray.java b/src/notzed.nativez/classes/au/notzed/nativez/IntArray.java
new file mode 100644 (file)
index 0000000..0b82479
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class IntArray extends AbstractList<Integer> implements Pointer {
+       final MemorySegment segment;
+
+       private IntArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static IntArray create(MemorySegment segment) {
+               return new IntArray(segment);
+       }
+
+       public static IntArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length * Memory.INT.byteSize(), scope));
+       }
+
+       public static IntArray createArray(MemoryAddress address, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
+       }
+
+       public static IntArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.INT, length));
+       }
+
+       public static IntArray create(SegmentAllocator alloc, int... values) {
+               return create(alloc.allocateArray(Memory.INT, values));
+       }
+
+       public final MemorySegment segment() {
+               return segment;
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public Integer get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public Integer set(int index, Integer value) {
+               int old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.INT.byteSize();
+       }
+
+       public int getAtIndex(long index) {
+               return segment.getAtIndex(Memory.INT, index);
+       }
+
+       public void setAtIndex(long index, int value) {
+               segment.setAtIndex(Memory.INT, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/LongArray.java b/src/notzed.nativez/classes/au/notzed/nativez/LongArray.java
new file mode 100644 (file)
index 0000000..9885ced
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class LongArray extends AbstractList<Long> implements Pointer {
+       final MemorySegment segment;
+
+       public LongArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static LongArray create(MemorySegment segment) {
+               return new LongArray(segment);
+       }
+
+       public static LongArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length * Memory.LONG.byteSize(), scope));
+       }
+
+       public static LongArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.LONG, length));
+       }
+
+       public static LongArray create(SegmentAllocator alloc, long... values) {
+               return create(alloc.allocateArray(Memory.LONG, values));
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public Long get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public Long set(int index, Long value) {
+               long old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.LONG.byteSize();
+       }
+
+       public long getAtIndex(long index) {
+               return segment.getAtIndex(Memory.LONG, index);
+       }
+
+       public void setAtIndex(long index, long value) {
+               segment.setAtIndex(Memory.LONG, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Memory.java b/src/notzed.nativez/classes/au/notzed/nativez/Memory.java
new file mode 100644 (file)
index 0000000..c935f22
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import java.lang.invoke.*;
+import java.lang.ref.Cleaner;
+import jdk.incubator.foreign.*;
+import static jdk.incubator.foreign.ValueLayout.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class Memory {
+
+       // probably should be INT8 INT16, etc
+       public  static final OfByte BYTE = JAVA_BYTE;
+       public static final OfShort SHORT = JAVA_SHORT.withBitAlignment(16);
+       public static final OfInt INT = JAVA_INT.withBitAlignment(32);
+       public static final OfLong LONG = JAVA_LONG.withBitAlignment(64);
+       public static final OfFloat FLOAT = JAVA_FLOAT.withBitAlignment(32);
+       public static final OfDouble DOUBLE = JAVA_DOUBLE.withBitAlignment(64);
+       public static final OfAddress POINTER = ADDRESS.withBitAlignment(64);
+
+       static final ResourceScope sharedScope = ResourceScope.newSharedScope(); // cleaner?
+       static final MemorySegment NULL = MemorySegment.ofAddress(MemoryAddress.NULL, 1, ResourceScope.globalScope());
+
+       public static ResourceScope sharedScope() {
+               return sharedScope;
+       }
+
+       public static MethodHandle downcall(String name, FunctionDescriptor desc) {
+               return SymbolLookup.loaderLookup().lookup(name)
+                       .map(sym -> CLinker.systemCLinker().downcallHandle(sym, desc))
+                       .orElse(null);
+       }
+
+       public static MethodHandle downcall(NativeSymbol sym, FunctionDescriptor desc) {
+               return CLinker.systemCLinker().downcallHandle(sym, desc);
+       }
+
+       public static MethodHandle downcall(String name, MemoryAddress sym, FunctionDescriptor desc, ResourceScope scope) {
+               return sym != MemoryAddress.NULL
+                       ? CLinker.systemCLinker().downcallHandle(NativeSymbol.ofAddress(name, sym, scope), desc)
+                       : null;
+       }
+
+       public static MethodHandle downcall(String name, FunctionDescriptor desc, Function<String,MemoryAddress> resolve, ResourceScope scope) {
+               MemoryAddress sym = resolve.apply(name);
+               return sym != MemoryAddress.NULL
+                       ? CLinker.systemCLinker().downcallHandle(NativeSymbol.ofAddress(name, sym, scope), desc)
+                       : null;
+       }
+
+       static final MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+       public static NativeSymbol upcall(Object instance, FunctionDescriptor desc, ResourceScope scope) {
+               try {
+                       java.lang.reflect.Method m = instance.getClass().getMethods()[0];
+                       MethodHandle handle = lookup.findVirtual(instance.getClass(), "call", MethodType.methodType(m.getReturnType(), m.getParameterTypes()))
+                                             .bindTo(instance);
+                       return CLinker.systemCLinker().upcallStub(handle, desc, scope);
+               } catch (Throwable t) {
+                       throw new AssertionError(t);
+               }
+       }
+
+       public static NativeSymbol upcall(MethodHandles.Lookup lookup, Object instance, String signature, FunctionDescriptor desc, ResourceScope scope) {
+               try {
+                       java.lang.reflect.Method m = instance.getClass().getMethods()[0];
+                       MethodHandle handle = lookup.findVirtual(instance.getClass(), m.getName(), MethodType.fromMethodDescriptorString(signature, Memory.class.getClassLoader()))
+                                             .bindTo(instance);
+                       return CLinker.systemCLinker().upcallStub(handle, desc, scope);
+               } catch (Throwable t) {
+                       throw new AssertionError(t);
+               }
+       }
+
+       public static NativeSymbol upcall(MethodHandles.Lookup lookup, Object instance, String method, String signature, FunctionDescriptor desc, ResourceScope scope) {
+               try {
+                       MethodHandle handle = lookup.findVirtual(instance.getClass(), method, MethodType.fromMethodDescriptorString(signature, Memory.class.getClassLoader()))
+                                             .bindTo(instance);
+                       return CLinker.systemCLinker().upcallStub(handle, desc, scope);
+               } catch (Throwable t) {
+                       throw new AssertionError(t);
+               }
+       }
+
+       public static MemoryAddress address(Addressable v) {
+               return v != null ? v.address() : MemoryAddress.NULL;
+       }
+
+       public static MemoryAddress address(Pointer v) {
+               return v != null ? v.address() : MemoryAddress.NULL;
+       }
+
+       public static <T> MemoryAddress address(FunctionPointer<T> v) {
+               return v != null ? v.symbol().address() : MemoryAddress.NULL;
+       }
+
+       public static long length(List<?> list) {
+               return list != null ? list.size() : 0;
+       }
+
+       public static long length(Array<?> list) {
+               return list != null ? list.length() : 0;
+       }
+
+       public static long size(MemorySegment s) {
+               return s != null ? s.byteSize() : 0;
+       }
+}
similarity index 96%
rename from src/template/Native.java
rename to src/notzed.nativez/classes/au/notzed/nativez/Native.java
index d214edc..191728b 100644 (file)
@@ -14,7 +14,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package api;
+package au.notzed.nativez;
 
 import java.io.StringReader;
 import java.lang.invoke.*;
@@ -45,7 +45,7 @@ import java.lang.System.Logger.Level;
  * <p>
  * FIXME: there are MemorySegment based accessors for primitive types now, use those
  */
-public class Native implements Memory.Addressable {
+public class Native implements Pointer {
 
        private final MemoryAddress p;
 
@@ -63,6 +63,10 @@ public class Native implements Memory.Addressable {
                return p;
        }
 
+       public ResourceScope scope() {
+               return ResourceScope.globalScope();
+       }
+
        /* ********************************************************************** */
        /* GC handling */
        /* ********************************************************************** */
@@ -110,7 +114,7 @@ public class Native implements Memory.Addressable {
                }
                return o;
                }*/
-       public static <T extends Native> T resolve(MemoryAddress p, Function<MemoryAddress, T> create) {
+       public static <T extends Native> T resolve(Class<T> jtype, MemoryAddress p, Function<MemoryAddress, T> create) {
                T o;
                boolean step = false;
 
@@ -125,7 +129,7 @@ public class Native implements Memory.Addressable {
 
                        String fmt;
 
-                       if (h == null || (o = (T)(h.get())) == null) {
+                       if (h == null || (o = jtype.cast(h.get())) == null) {
                                o = create.apply(p);
 
                                fmt = h == null ? "  create $%016x %s" : "  replac $%016x %s";
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java b/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java
new file mode 100644 (file)
index 0000000..cf93675
--- /dev/null
@@ -0,0 +1,14 @@
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+/**
+ * Because you can't implement foriegn.Addressable for some silly reason
+ */
+public interface Pointer {
+       MemoryAddress address();
+       default ResourceScope scope() {
+               return ResourceScope.globalScope();
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/PointerArray.java b/src/notzed.nativez/classes/au/notzed/nativez/PointerArray.java
new file mode 100644 (file)
index 0000000..cdc4189
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class PointerArray extends AbstractList<MemoryAddress> implements Pointer {
+       final MemorySegment segment;
+
+       private PointerArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static PointerArray create(MemorySegment segment) {
+               return new PointerArray(segment);
+       }
+
+       public static PointerArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length, scope));
+       }
+
+       public static PointerArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.POINTER, length));
+       }
+
+       public static PointerArray create(Frame alloc, MemoryAddress... values) {
+               return create(alloc.allocateArray(Memory.POINTER, values));
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public MemoryAddress get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public MemoryAddress set(int index, MemoryAddress value) {
+               MemoryAddress old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.POINTER.byteSize();
+       }
+
+       public MemoryAddress getAtIndex(long index) {
+               return segment.getAtIndex(Memory.POINTER, index);
+       }
+
+       public void setAtIndex(long index, MemoryAddress value) {
+               segment.setAtIndex(Memory.POINTER, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/au/notzed/nativez/ShortArray.java b/src/notzed.nativez/classes/au/notzed/nativez/ShortArray.java
new file mode 100644 (file)
index 0000000..f93599d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.notzed.nativez;
+
+import jdk.incubator.foreign.*;
+
+import java.util.AbstractList;
+import java.util.function.Function;
+import java.util.function.BiFunction;
+import java.util.List;
+
+public class ShortArray extends AbstractList<Short> implements Pointer {
+       final MemorySegment segment;
+
+       private ShortArray(MemorySegment segment) {
+               this.segment = segment;
+       }
+
+       public static ShortArray create(MemorySegment segment) {
+               return new ShortArray(segment);
+       }
+
+       public static ShortArray createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, length * Memory.SHORT.byteSize(), scope));
+       }
+
+       public static ShortArray createArray(MemoryAddress address, ResourceScope scope) {
+               return create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
+       }
+
+       public static ShortArray createArray(long length, SegmentAllocator alloc) {
+               return create(alloc.allocateArray(Memory.SHORT, length));
+       }
+
+       public static ShortArray create(SegmentAllocator alloc, short... values) {
+               return create(alloc.allocateArray(Memory.SHORT, values));
+       }
+
+       public final MemoryAddress address() {
+               return segment.address();
+       }
+
+       public final ResourceScope scope() {
+               return segment.scope();
+       }
+
+       @Override
+       public int size() {
+               return (int)length();
+       }
+
+       @Override
+       public Short get(int index) {
+               return getAtIndex(index);
+       }
+
+       @Override
+       public Short set(int index, Short value) {
+               short old = getAtIndex(index);
+               setAtIndex(index, value);
+               return old;
+       }
+
+       public long length() {
+               return segment.byteSize() / Memory.SHORT.byteSize();
+       }
+
+       public short getAtIndex(long index) {
+               return segment.getAtIndex(Memory.SHORT, index);
+       }
+
+       public void setAtIndex(long index, short value) {
+               segment.setAtIndex(Memory.SHORT, index, value);
+       }
+}
diff --git a/src/notzed.nativez/classes/module-info.java b/src/notzed.nativez/classes/module-info.java
new file mode 100644 (file)
index 0000000..7edcf0a
--- /dev/null
@@ -0,0 +1,5 @@
+
+module notzed.nativez {
+       requires transitive jdk.incubator.foreign;
+       exports au.notzed.nativez;
+}
diff --git a/src/notzed.nativez/lib/api.pm b/src/notzed.nativez/lib/api.pm
new file mode 100644 (file)
index 0000000..ec07956
--- /dev/null
@@ -0,0 +1,974 @@
+
+package api;
+
+use strict;
+
+use File::Path qw(make_path);
+use File::Basename;
+use Data::Dumper;
+use List::Util qw(first);
+
+use config;
+
+my %renameTable = (
+       'studly-caps' => sub { my $s = shift; $s =~ s/(?:^|_)(.)/\U$1/g; return $s; },
+       'camel-case' => sub { my $s = shift; $s =~ s/(?:_)(.)/\U$1/g; return $s; },
+       'upper-leadin' => sub { my $s = shift; $s =~ s/^([^_]+)/\U$1/; return $s; },
+       'identity' => sub {     return $_[0]; },
+       'call' => sub {
+               my $s = shift;
+
+               if ($s =~ m/\(/) {
+                       $s =~ s/u32:|u64:/p/g;
+                       $s =~ s/\$\{([^\}]+)\}/$1/g;
+                       $s =~ s/[\(\)]/_/g;
+                       $s =~ s/^/Call/;
+               }
+               $s;
+       }
+);
+
+my %defaultTable = (
+       'struct:<default>' => {
+               name => '<default>',
+               items => [],
+               options => [ 'default=none', 'access=rw', 'field:rename=studly-caps' ],
+               regex => qr/^struct:<default>$/,
+               type => 'struct'
+       },
+       'union:<default>' => {
+               name => '<default>',
+               items => [],
+               options => [ 'default=all', 'access=rw', 'field:rename=studly-caps' ],
+               regex => qr/^union:<default>$/,
+               type => 'union'
+       },
+       'call:<default>' => {
+               name => '<default>',
+               items => [],
+               options => [ 'call:rename=call', 'access=r' ],
+               regex => qr/^call:<default>$/,
+               type => 'call'
+       },
+       'func:<default>' => {
+               name => '<default>',
+               items => [],
+               options => [],
+               regex => qr/^func:<default>$/,
+               type => 'func'
+       },
+       'enum:<default>' => {
+               name => '<default>',
+               items => [],
+               options => [],
+               regex => qr/^enum:<default>$/,
+               type => 'enum'
+       },
+);
+
+sub new {
+       my $class = shift;
+       my $file = shift;
+       my $vars = shift;
+       my $self = {
+               vars => $vars,
+               index => {},
+               data => {},
+               types => [],
+       };
+
+       my $conf = new config($vars, $file);
+       $self->{api} = $conf->{objects};
+       foreach my $obj (@{$self->{api}}) {
+               $self->{index}->{"$obj->{type}:$obj->{name}"} = $obj;
+       }
+       foreach my $k (keys %defaultTable) {
+               if (!defined($self->{index}->{$k})) {
+                       $self->{index}->{$k} = $defaultTable{$k};
+                       push @{$self->{api}}, $defaultTable{$k};
+               }
+       }
+
+       while ($#_ >= 0) {
+               my $name = shift;
+               my $info = loadAPIFile($name);
+
+               $self->{data} = { %{$self->{data}}, %{$info} };
+       }
+
+       analyseAPI($self);
+       preprocess($self);
+
+       # add phantom 'api' entries for anything else required
+       foreach my $s (findDependencies($self)) {
+               my $n = "$s->{type}:$s->{name}";
+               my $def = $self->{index}->{"$s->{type}:<default>"};
+
+               die "no default for implicit dependency $n" if (!$def);
+
+               my $obj = {
+                       %$def,
+                       match => "$s->{type}:$s->{name}",
+                       name => $s->{name},
+                       regex => qr/^$n$/,
+               };
+               $obj->{rename} = $obj->{"$obj->{type}:rename"} ? $obj->{"$obj->{type}:rename"}->($obj->{name}) : $obj->{name};
+
+               print " implicit $n\n";
+               push @{$self->{api}}, $obj;
+               $self->{index}->{$n} = $obj;
+       }
+
+       postprocess($self);
+
+       # form the types
+       # TODO: could match the regexes to every possible type in the api and create a direct index
+       my $copyIndex = {};
+       foreach my $obj (grep { $_->{type} eq 'type' && $_->{name} =~ m@<.*>@ } @{$self->{api}}) {
+               $copyIndex->{$obj->{name}} = $obj;
+       }
+
+       foreach my $obj (grep { $_->{type} eq 'type' && $_->{name} =~ m@^/.*/$@ } @{$self->{api}}) {
+               push @{$self->{types}}, initType($self, $copyIndex, $obj);
+       }
+
+       bless $self, $class;
+
+       return $self;
+}
+
+# class.name to class/name.java
+sub classToPath {
+       my $api = shift;
+       my $name = shift;
+
+       $name = $api->{vars}->{package}.'.'.$name;
+       $name =~ s@\.@/@g;
+       $name = $api->{vars}->{output}.'/'.$name.'.java';
+       $name;
+}
+
+sub closeOutput {
+       my $api = shift;
+       my $name = shift;
+       my $f = shift;
+       my $path = $api->classToPath($name);
+
+       close($f) || die;
+       rename($path.'~', $path) || die ("rename failed: $!");
+}
+
+sub openOutput {
+       my $api = shift;
+       my $name = shift;
+       my $path = $api->classToPath($name);
+       my $dir = dirname($path);
+
+       make_path($dir) if (!-d $dir);
+
+       open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
+       print "writing '$path'\n";
+       $f;
+}
+
+sub renameFunction {
+       my $api = shift;
+       my $name = shift;
+       $renameTable{$name};
+}
+
+sub initType {
+       my $api = shift;
+       my $index = shift;
+       my $obj = shift;
+       my $type = {};
+
+       $type->{options} = [ @{$obj->{options}} ];
+
+       # FIXME: per-item options, set etc?
+       foreach my $inc (@{$obj->{items}}) {
+               $type->{items}->{$inc->{match}} = $inc->{literal};
+       }
+
+       my $v = optionValue('select', undef, $obj);
+       $type->{select} = $v if defined($v);
+
+       #print "init $obj->{name}\n";
+       foreach my $c (split /,/,optionValue('copy', undef, $obj)) {
+               my $copy = $index->{$c};
+
+               if ($copy) {
+                       #print " copy $c\n";
+                       my $proto = initType($api, $index, $copy);
+
+                       foreach my $k (keys %{$proto->{items}}) {
+                               $type->{items}->{$k} = $proto->{items}->{$k} if !defined($type->{items}->{$k});
+                       }
+
+                       push @{$type->{options}}, @{$proto->{options}};
+               } else {
+                       die ("type copy target $c not found");
+               }
+       }
+
+       $type->{regex} = qr/$1/ if $obj->{name} =~ m@/(.*)/@;
+
+       return $type;
+}
+
+# returns { type=>$type, match=>$match)
+#  match is named groups from regex (%+)
+sub findType {
+       my $api = shift;
+       my $m = shift;
+       my $deref = $m->{deref};
+       my @list;
+
+       foreach my $type (@{$api->{types}}) {
+               my $select = !defined($type->{select}) || defined($m->{$type->{select}});
+
+               if ($select && ($deref =~ m/$type->{regex}/)) {
+                       my %match = %+;
+
+                       return { deref=>$deref, type=>$type, match=>\%match };
+               }
+       }
+       die ("cannot find matching type '$deref' for member $m->{name}");
+       undef;
+}
+
+# find value of first option of the given name
+sub optionValue {
+       my $name = shift;
+       my $or = shift;
+
+       #print "optionValue $name\n";
+       foreach my $obj (@_) {
+               foreach my $opt (@{$obj->{options}}) {
+                       #print "? $name $opt = ".($opt =~m/^\Q$name\E=(.*)$/)."\n";
+                       #print " = $opt\n" if ($opt =~m/^\Q$name\E=(.*)$/);
+                       return $1 if ($opt =~m/^\Q$name\E=(.*)$/);
+               }
+       }
+       $or;
+}
+
+# look for all matching options of the given name
+# multiple objects are searched, the first one with
+#  the given parameter overrides the rest.
+# name, object, object *
+sub optionValues {
+       my $name = shift;
+       my $rx = qr/$name/;
+       my @list;
+
+       foreach my $obj (@_) {
+               foreach my $opt (@{$obj->{options}}) {
+                       push @list, $1 if ($opt =~m/^$rx=(.*)$/);
+               }
+               last if ($#list >= 0);
+       }
+       @list;
+}
+
+# same as above but doesn't short-circuit
+sub optionValuesAll {
+       my $name = shift;
+       my $rx = qr/$name/;
+       my @list;
+
+       foreach my $obj (@_) {
+               foreach my $opt (@{$obj->{options}}) {
+                       push @list, $1 if ($opt =~m/^$rx=(.*)$/);
+               }
+       }
+       @list;
+}
+
+# find first occurance of a flag
+sub optionFlag {
+       my $name = shift;
+
+       foreach my $obj (@_) {
+               foreach my $opt (@{$obj->{options}}) {
+                       return 1 if ($opt eq $name);
+               }
+       }
+       undef;
+}
+
+sub findItem {
+       my $s = shift;
+       my $name = shift;
+
+       foreach my $i (@{$s->{items}}) {
+               return $i if $i->{match} eq $name;
+       }
+       undef;
+}
+
+sub findAllItems {
+       my $api = shift;
+       my $obj = shift;
+       my $s = shift;
+       my %visited = ();
+       my @fields = ();
+
+       #print Dumper($obj);
+
+       my @all = @{$s->{items}};
+       my %index;
+
+       foreach my $m (@all) {
+               $index{$m->{name}} = $m;
+       }
+
+       foreach my $inc (@{$obj->{items}}) {
+               my $d = $index{$inc->{match}};
+
+               if ($d) {
+                       next if $visited{$d->{type}.':'.$d->{name}}++;
+                       push @fields, [ $inc, $d ];
+               } else {
+                       foreach my $d (grep { $_->{name} =~ m/$inc->{regex}/ } @all) {
+                               next if $visited{$d->{type}.':'.$d->{name}}++;
+                               push @fields, [ $inc, $d ];
+                       }
+               }
+       }
+
+       if (optionValue('default', undef, $obj, $api->{index}->{'struct:<default>'}) eq 'all') {
+               #print "* add all items\n";
+               foreach my $d (@all) {
+                       next if $visited{$d->{type}.':'.$d->{name}}++;
+                       push @fields, [ $obj, $d ];
+               }
+       }
+
+       return @fields;
+}
+
+sub findField {
+       my $s = shift;
+       my $name = shift;
+
+       foreach my $i (@{$s->{items}}) {
+               return $i if $i->{name} eq $name;
+       }
+       undef;
+}
+
+# ######################################################################
+
+sub addDependencies {
+       my $api = shift;
+       my $obj = shift;
+       my $s = shift;
+       my $add = shift;
+
+       #print "add deps for '$s->{name}'\n";
+       if ($s->{type} =~ m/^(struct|union)$/n) {
+               # include embedded structures always
+               foreach my $d (grep { $_->{type} =~ m/^(struct|union):/ && $_->{deref} =~ m/\[\d+\$\{|^\$\{/ } @{$s->{items}}) {
+                       #print "  embedded $d->{name} $d->{deref}\n";
+                       $add->($d->{type});
+               }
+
+               # include selected fields optionally
+               if ($obj) {
+                       foreach my $i (findAllItems($api, $obj, $s)) {
+                               my ($inc, $d) = @{$i};
+                               #print "  selected $d->{name} $d->{type} $d->{deref}\n";
+                               $add->($d->{type}) if ($d->{type} =~ m/^(struct|union|func|call|enum):/);
+                               # HACK: enum types are integers but ctype includes the actual type
+                               $add->("enum:$1") if ($d->{ctype} =~ m/^enum (.+)/);
+                       }
+               }
+       } elsif ($s->{type} =~ m/^(call|func)/n) {
+               # for calls/func need all fields
+               foreach my $d (grep { $_->{type} =~ m/^(struct|union|func|call|enum):/ } @{$s->{items}}, $s->{result}) {
+                       #print "  argument $d->{name} $d->{type} $d->{deref}\n";
+                       $add->($d->{type});
+               }
+       }
+}
+
+# use either {match} or {regex} to get all matches for a data type
+sub findMatches {
+       my $api = shift;
+       my $inc = shift;
+       my $data = $api->{data};
+
+       if ($inc->{match} eq 'func:<match-function>') {
+               my $match = eval $inc->{literal};
+
+               if (!defined($match)) {
+                       die "unable to parse match function $inc->{match} $! $@";
+               }
+               grep { $match->($_) } values %$data;
+       } else {
+               my $s = $data->{$inc->{match}};
+
+               if (defined($s)) {
+                       $s;
+               } else {
+                       map { $data->{$_} } grep { $_ =~ m/$inc->{regex}/ } keys %$data;
+               }
+       }
+}
+
+# find all extra types used by the api requested
+sub findDependencies {
+       my $api = shift;
+       my %data = %{$api->{data}};
+       my %seen;
+       my %deps;
+       my $setdeps = sub { my $d = shift; $deps{$d} = 1; };
+
+       print "Root types\n";
+       foreach my $obj (@{$api->{api}}) {
+               if ($obj->{type} eq 'library') {
+                       foreach my $inc (@{$obj->{items}}) {
+                               next if ($inc->{type} eq 'library');
+
+                               #print "? $inc->{regex}\n";
+                               foreach my $s (findMatches($api, $inc)) {
+                                       my $n = "$s->{type}:$s->{name}";
+
+                                       print "+ $n\n";
+
+                                       $seen{$n}++;
+                                       $s->{output} = 1;
+                                       addDependencies($api, $obj, $s, $setdeps);
+                               }
+                       }
+               } elsif ($obj->{type} =~ m/^(struct|union|call|func|enum|define)$/) {
+                       #foreach my $n (grep { $_ =~ m/$obj->{regex}/ } keys %data) {
+                       foreach my $s (findMatches($api, $obj)) {
+                               my $n = "$s->{type}:$s->{name}";
+
+                               $seen{$n}++;
+                               $s->{output} = 1;
+                               addDependencies($api, $obj, $s, $setdeps);
+                       }
+               }
+       }
+
+       # at this point 'seen' contains everything explicitly requested
+       # and deps is anything else they need but not referenced directly
+       # recursively grab anything else
+
+       my @list = ();
+       my @stack = sort keys %deps;
+       my $pushstack = sub { my $d = shift; push @stack, $d; };
+       while ($#stack >= 0) {
+               my $n = shift @stack;
+               my $s;
+
+               next if $seen{$n}++;
+
+               $s = $data{$n};
+
+               if ($s) {
+                       print "Add referent: $n\n";
+                       $s->{output} = 1;
+                       addDependencies($api, $api->{index}->{"$s->{type}:<default>"}, $s, $pushstack);
+               } elsif ($n =~ m/^(.*):(.*)$/) {
+                       print "Add anonymous: $n\n";
+                       # type not know, add anonymous
+                       $s = {
+                               name => $2,
+                               type => $1,
+                               size => 0,
+                               items => [],
+                               output => 1,
+                       };
+                       $api->{data}->{$n} = $s;
+               }
+
+               # maybe it should have some skeleton metadata?
+               # depends on where it's used i suppose
+               push @list, $s;
+       }
+
+       print "Added ".($#list+1)." dependencies\n";
+       return @list;
+}
+
+# ######################################################################
+
+sub loadAPIFile {
+       my $file = shift;
+       my $info;
+
+       unless ($info = do $file) {
+               die "couldn't parse $file: $@"  if $@;
+               die "couldn't import $file: $!" unless defined $info;
+               die "couldn't run $file"                unless $info;
+       }
+
+       return $info;
+}
+
+sub parseRename {
+       my $how = shift;
+       my $rename = $renameTable{'identity'};
+
+       foreach my $n (split /,/,$how) {
+               my $old = $rename;
+               my $new = $renameTable{$n};
+
+               if ($n =~ m@^s/(.*)/(.*)/$@) {
+                       my $rx = qr/$1/;
+                       my $rp = $2;
+                       $rename = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
+               } elsif ($new) {
+                       $rename = sub { my $s=shift; $s = $old->($s); return $new->($s); };
+               } else {
+                       my $x = $n;
+                       $rename = sub { return $x; };
+               }
+       }
+       $rename;
+}
+
+# pre-process {data}
+sub preprocess {
+       my $api = shift;
+
+       # Find any anonymous types and add them in
+       my %anonymous = ();
+       foreach my $s (values %{$api->{data}}) {
+               # FIXME: fix the list names in export.cc and export-defines
+               $s->{items} = $s->{fields} if $s->{fields};
+               $s->{items} = $s->{arguments} if $s->{arguments};
+               $s->{items} = $s->{values} if $s->{values};
+
+               foreach my $m (grep { $_->{type} =~ m/struct:|union:/} @{$s->{items}}) {
+                       $anonymous{$m->{type}} = 1 if !defined($api->{data}->{$m->{type}});
+               }
+
+               # add 'result' name
+               $s->{result}->{name} = 'result$' if $s->{type} =~ m/func|call/;
+
+               # add a canonical deref for all types
+               if ($s->{type} =~ m/func|call|struct|union/) {
+                       foreach my $m (grep { !defined($_->{deref}) } @{$s->{items}}, ($s->{type} =~ m/func|call/) ? $s->{result} : ()) {
+                               if ($m->{type} =~ m/^(union|struct):(.*)/) {
+                                       $m->{deref} = "\${$2}";
+                               } elsif ($m->{ctype} eq 'bitfield') {
+                                       $m->{deref} = "bitfield";
+                               } else {
+                                       $m->{deref} = $m->{type};
+                               }
+                       }
+               }
+
+               # all 'defines' are output by default
+               $s->{output} = 1 if $s->{type} eq 'define';
+       }
+
+       foreach my $k (sort keys %anonymous) {
+               print " anon $k\n";
+               if ($k =~ m/^(.*):(.*)$/) {
+                       $api->{data}->{$k} = {
+                               name => $2,
+                               type => $1,
+                               size => 0,
+                               items => [],
+                       };
+               }
+       }
+}
+
+# preprocess {api}
+sub analyseAPI {
+       my $api = shift;
+
+       # Note that type:name regexes always start at the beginning
+
+       foreach my $obj (@{$api->{api}}) {
+               if ($obj->{name} =~ m@^/(.*)/$@) {
+                       $obj->{regex} = qr/^$obj->{type}:$1/;
+               } else {
+                       $obj->{regex} = qr/^$obj->{type}:$obj->{name}$/;
+               }
+               $obj->{match} = "$obj->{type}:$obj->{name}";
+
+               foreach my $opt (@{$obj->{options}}) {
+                       if ($opt =~ m/^(.+:rename)=(.*)$/) {
+                               $obj->{$1} = parseRename($2);
+                       } elsif ($opt =~ m/^rename=(.*)$/) {
+                               $obj->{"$obj->{type}:rename"} = parseRename($1);
+                       }
+               }
+
+               my $defmode = ($obj->{type} eq 'library' ? 'func' : 'field');
+               foreach my $inc (@{$obj->{items}}) {
+                       my $match = $inc->{match};
+                       my $mode = $defmode;
+
+                       if ($inc->{match} =~ m/^(.*):(.*)$/) {
+                               $match = $2;
+                               $mode = $1;
+                       }
+
+                       $inc->{type} = $mode;
+                       if ($match =~ m@^/(.*)/$@) {
+                               $inc->{regex} = $mode ne 'field' ? qr/$mode:$1/ : qr/$1/;
+                       } else {
+                               $inc->{regex} = $mode ne 'field' ? qr/^$mode:$match$/ : qr/^$match$/;
+                       }
+
+                       foreach my $opt (@{$inc->{options}}) {
+                               if ($opt =~ m/^rename=(.*)$/) {
+                                       #print "option $opt ".Dumper($inc);
+                                       $inc->{"$mode:rename"} = parseRename($1);
+                               }
+                       }
+
+                       $inc->{"$mode:rename"} = $obj->{"$mode:rename"} if (!(defined($inc->{"$mode:rename"})) && defined($obj->{"$mode:rename"}));
+               }
+       }
+}
+
+#
+# 'lib/struct/func' level    'lib.inc' level            'func/struct.inc' level  setting
+#  array:name1                array:name1                array                    char* is array, void* is a Segment
+#  array-size:name1=name2     array-size:name1=name2     array-size=name2         implied array size from name2
+#  implied:name1=expr         implied:name1=expr         implied=expr             value of name1 is calculated from other arguments
+#  instance:name1             instance:name1             instance                 parameter/return is instance, implies non-static function
+
+#  scope:name1=scope[,close]  scope:name1=scope[,close]  scope=scope[,close]      parameter/return is a new instance with given scope
+#  success:name1=values       success:name1=values       success=values           which parameter and values indicate integer success
+#                                                                                 pointer types are considered in/out and implied
+#  success:name1=!null        success:name1=!null        success=!null            which parameter and values indicate non-null success
+#  return:name1               return:name1               return                   return this output argument instead of the return code
+
+#  access:name1=rwi           access:name1=rwi           access=rwi               set access type, read, write, indexed
+#  access=rwi                 access=rwi                 access=rwi                - can also be set as default
+
+#  onsuccess=blah             onsuccess=blah             onsuccess=blah
+
+# 'name' is parameter name, or parameter index, or result$ for return value
+# scope is instance for instance-scope, explicit for explicit scope, global for global scope.  default is global.
+
+# err, maybe this format makes more sense:
+#   name1:success=values   name1:success=values  success=values
+#   name1:instance         name1:instance        instance
+
+my @itemFlags = (
+       'array',
+       'segment',
+       'instance',
+       'return',
+       'raw',
+       'raw-in',
+);
+
+my @itemOptions = (
+       'array-size',
+       'scope',
+       'success',
+       'implied',
+       'tonative',
+);
+
+my @itemAnyOptions = (
+       'access',
+);
+
+# process struct->field
+#  api, obj (struct, func), inc (field entry), s (struct), m (struct field)
+# Note: keep in sync with processFunc
+sub processField {
+       my $api = shift;
+       my $obj = shift;
+       my $inc = shift;
+       my $s = shift;
+       my $m = shift;
+       my $def = $api->{index}->{"$s->{type}:<default>"};
+
+       #print "process $s->{type}:$s->{name}.$m->{name}\n";
+       #print "  $m->{name}\n";
+
+       foreach my $flag (@itemFlags) {
+               my $value = optionFlag("$flag", $inc);
+               $value = optionFlag("$flag:$m->{name}", $obj, $def) if !defined($value);
+               $m->{$flag} = $value if (defined($value));
+       }
+       foreach my $option (@itemOptions) {
+               my $value = optionValue("$option", undef, $inc);
+               $value = optionValue("$option:$m->{name}", undef, $obj, $def) if !defined($value);
+               $m->{$option} = $value if (defined($value));
+       }
+       foreach my $option (@itemAnyOptions) {
+               my $value = optionValue("$option", undef, $inc);
+               $value = optionValue("$option:$m->{name}", undef, $obj, $def) if !defined($value);
+               $value = optionValue("$option", undef, $obj, $def) if !defined($value);
+               $m->{$option} = $value if (defined($value));
+       }
+
+       $m->{output} = 1;
+       $m->{rename} = (first { defined $_ } $inc->{'field:rename'}, $obj->{'field:rename'}, $def->{'field:rename'}, $renameTable{identity})->($m->{name});
+}
+
+# process func or call main type
+sub processTypeFunc {
+       my $api = shift;
+       my $seen = shift;
+       my $obj = shift;
+       my $def = $api->{index}->{"$obj->{type}:<default>"};
+
+       foreach my $s (@_) {
+               my $v;
+
+               if ($seen->{"$s->{type}:$s->{name}"}++) { print "warning: seen $s->{type}:$s->{name}\n"; next; }
+
+               print " $s->{name}\n" if ($api->{vars}->{verbose} > 1);
+
+               foreach my $m ($s->{result}, @{$s->{items}}) {
+                       my $inc = findItem($obj, $m->{name});
+
+                       processField($api, $obj, $inc, $s, $m);
+               }
+
+               $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
+               $s->{access} = optionValue('access', '', $obj, $def);
+               $v = optionValue('onsuccess', undef, $obj, $def);
+               $s->{onsuccess} = $v if defined $v;
+
+               postProcessType($s);
+       }
+}
+
+# process library->func
+# process struct->func
+#  api, obj (library, struct), inc (func entry), s ($data->{func:name})
+# Note: keep in sync with processField
+sub processFunc {
+       my $api = shift;
+       my $obj = shift;
+       my $inc = shift;
+       my $s = shift;
+       my $index = -1;
+       my $def = $api->{index}->{"$s->{type}:<default>"};
+       my $v;
+
+       print "process $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
+
+       foreach my $m (defined($s->{result}) ? $s->{result} : (), @{$s->{items}}) {
+               #print "  $m->{name}\n";
+
+               foreach my $flag (@itemFlags) {
+                       my $value = optionFlag("$flag:$m->{name}", $inc, $obj, $def);
+                       $value = optionFlag("$flag:$index", $inc, $obj, $def) if !defined($value);
+                       $m->{$flag} = $value if (defined($value));
+               }
+               foreach my $option (@itemOptions) {
+                       my $value = optionValue("$option:$m->{name}", undef, $inc, $obj, $def);
+                       $value = optionValue("$option:$index", undef, $inc, $obj, $def) if !defined($value);
+                       $m->{$option} = $value if (defined($value));
+               }
+               foreach my $option (@itemAnyOptions) {
+                       my $value = optionValue("$option:$m->{name}", undef, $inc, $obj, $def);
+                       $value = optionValue("$option:$index", undef, $inc, $obj, $def) if !defined($value);
+                       $value = optionValue("$option", undef, $inc, $obj, $def) if !defined($value);
+                       $m->{$option} = $value if (defined($value));
+               }
+               $m->{output} = 1;
+               $index++;
+       }
+
+       $s->{rename} = (first { defined $_ } $inc->{"$s->{type}:rename"}, $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
+       $s->{access} = optionValue('access', '', $inc, $obj, $def);
+       $v = optionValue('onsuccess', undef, $inc, $obj, $def);
+       $s->{onsuccess} = $v if defined ($v);
+
+       postProcessType($s);
+}
+
+# finally link up array sizes and work out what is output or not
+# TODO: struct field array-size should be output but not included in constructors?
+sub postProcessType {
+       my $s = shift;
+       my $static = 1;
+
+       foreach my $m (defined($s->{result}) ? $s->{result} : (), @{$s->{items}}) {
+               $static = 0 if ($m->{instance});
+
+               # FIXME: collect multiple scopes here
+               $s->{scope} = 'explicit' if ($m->{scope} =~ m/^explicit$|^explicit,(.*)$/);
+               $s->{scope} = 'global' if ($m->{scope} eq 'global');
+               $s->{scope} = 'object' if ($m->{scope} eq 'object');
+
+               if ($m->{'array-size'}) {
+                       my $size = findField($s, $m->{'array-size'});
+
+                       print Dumper($s) if (!defined($size));
+                       die "can't find array-size=$m->{'array-size'}" if !defined($size);
+
+                       $size->{output} = 0 if ($s->{type} =~ m/func|call/);
+                       $size->{'array-size-source'} = $m;
+                       $m->{'array-size'} = $size;
+               }
+
+               # no java arg for instance parameter
+               $m->{output} = 0 if ($m->{instance});
+               # don't generate java args for values calculated
+               $m->{output} = 0 if (defined($m->{implied}));
+               # don't generate java args for return statuss unless they're also the constructed value
+               $m->{output} = 0 if (defined($m->{success}) && !$m->{scope});
+               # don't generate java args for output arguments
+               $m->{output} = 0 if ($m->{scope} && $m->{name} ne 'result$');
+
+               # link success/return fields to struct/func
+               $s->{success} = $m if defined($m->{success});
+               $s->{return} = $m if ($m->{return});
+       }
+
+       $s->{static} = $static;
+
+       # TODO: default scope?  or from struct:<default> etc?
+}
+
+# transfer info from {api} to {data}
+sub processFields {
+       my $api = shift;
+       my $seen = shift;
+       my $obj = shift;
+       my $inc = shift;
+       my $s = shift;
+
+       foreach my $m (@_) {
+               next if $seen->{$m->{name}}++;
+
+               processField($api, $obj, $inc, $s, $m);
+       }
+}
+
+# process a struct/union
+# these have fields as well as potentially other included types
+sub processType {
+       my $api = shift;
+       my $seen = shift;
+       my $obj = shift;
+       my $def = $api->{index}->{"$obj->{type}:<default>"};
+
+       foreach my $s (@_) {
+               my $memberseen = {};
+
+               next if ($seen->{"$s->{type}:$s->{name}"}++);
+
+               print "process type $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
+
+               # process the struct/union fields first
+               foreach my $inc (grep { $_->{type} eq 'field' } @{$obj->{items}}) {
+                       my @list = grep { $_->{name} =~ m/$inc->{regex}/ } @{$s->{items}};
+
+                       processFields($api, $memberseen, $obj, $inc, $s, @list);
+               }
+
+               if (optionValue('default', undef, $obj, $def) eq 'all') {
+                       print " + adding all fields\n" if ($api->{vars}->{verbose} > 1);
+                       processFields($api, $memberseen, $obj, undef, $s, @{$s->{items}});
+               }
+
+               $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $def->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
+
+               # finish off
+               postProcessType($s);
+
+               # handle other types included/mark them no-output
+               $seen->{"$s->{type}:$s->{name}"} = 0;
+               processLibrary($api, $seen, $obj);
+       }
+
+}
+
+sub processLibrary {
+       my $api = shift;
+       my $seen = shift;
+       my $lib = shift;
+       my $data = $api->{data};
+
+       return if ($seen->{"$lib->{type}:$lib->{name}"}++);
+
+       print "process library $lib->{type}:$lib->{name}\n";
+       #print 'lib='.Dumper($lib);
+       #print 'lib.options='.Dumper($lib->{options});
+
+       # TODO: embedded types
+
+       foreach my $inc (@{$lib->{items}}) {
+               print " $inc->{match}\n" if ($api->{vars}->{verbose} > 1);
+
+               if ($inc->{type} =~ m/func|call/on) {
+                       my @list = findMatches($api, $inc);
+
+                       #print 'inc='.Dumper($inc);
+                       #print "match $inc->{regex} .options=".Dumper($inc->{options});
+
+                       foreach my $s (@list) {
+                               if ($seen->{"$s->{type}:$s->{name}"}++) { print "warning: seen $s->{type}:$s->{name}\n"; next; }
+
+                               #print " $s->{name}\n";
+                               processFunc($api, $lib, $inc, $s);
+
+                               #print 'func='.Dumper($s);
+                       }
+               } elsif ($inc->{type} eq 'library') {
+                       foreach my $l (grep { "$_->{type}:$_->{name}" =~ m/$inc->{regex}/ } @{$api->{api}}) {
+                               # included libraries are never output directly
+                               $l->{output} = 0;
+                               processLibrary($api, $seen, $l);
+                       }
+               } elsif ($inc->{type} =~ m/define|enum/on) {
+                       # suppress direct output of anything included
+                       foreach my $c (findMatches($api, $inc)) {
+                               $c->{output} = 0;
+                       }
+               }
+       }
+}
+
+sub postprocess {
+       my $api = shift;
+       my $seen = {};
+       my %data = %{$api->{data}};
+
+       # apply requested options to specific objects (not defaults)
+       foreach my $obj (grep {$_->{type} =~ m/^(func|call|struct|union)$/} @{$api->{api}}) {
+               my @list = findMatches($api, $obj);
+
+               if ($obj->{type} =~ m/func|call/) {
+                       processTypeFunc($api, $seen, $obj, @list);
+               } else {
+                       processType($api, $seen, $obj, @list);
+               }
+       }
+
+       # handle libraries
+       foreach my $lib (grep {$_->{type} eq 'library'} @{$api->{api}}) {
+               next if defined($lib->{output});
+               $lib->{output} = 1;
+               processLibrary($api, $seen, $lib);
+       }
+
+       # apply options for default object types
+       foreach my $obj (grep {$_->{name} eq '<default>'} @{$api->{api}}) {
+               my @list;
+
+               @list = grep { !defined($seen->{"$_->{type}:$_->{name}"}) && $_->{type} eq $obj->{type} } values %data;
+
+               print "apply $obj->{type}:$obj->{name} to ".($#list + 1)." objects\n" if ($#list >= 0);
+
+               if ($obj->{type} =~ m/func|call/) {
+                       processTypeFunc($api, $seen, $obj, @list);
+               } else {
+                       processType($api, $seen, $obj, @list);
+               }
+       }
+}
+
+1;
similarity index 79%
rename from src/code.api
rename to src/notzed.nativez/lib/code.api
index 884866a..256b74f 100644 (file)
@@ -26,12 +26,37 @@ code method {
        }
        }}
 
+       invoke-dynamic-init {{
+               {name}$FH = Memory.downcall("{name}", {function-descriptor}, resolve, scope);
+       }}
+
+       invoke-dynamic {{
+       final MethodHandle {name}$FH;
+       public {java-result} {rename}({java-arguments}) {
+               {native-output-define}
+               {native-result-define}
+               try {create-frame}{
+                       {native-output-init}
+                       {native-result-assign}{name}$FH.invokeExact({native-call});
+                       {native-output-copy}
+                       {result-test}{
+                               {java-result-assign}
+                               {on-success}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+       }}
+
        # callback function/types
        downcall {{
-       public static Memory.FunctionPointer<{rename}> downcall(MemoryAddress addr$, ResourceScope scope$) {
+       public static FunctionPointer<{rename}> downcall(MemoryAddress addr$, ResourceScope scope$) {
                NativeSymbol symbol$ = NativeSymbol.ofAddress("{rename}", addr$, scope$);
                MethodHandle {rename}$FH = Memory.downcall(symbol$, descriptor());
-               return new Memory.FunctionPointer<{rename}>(
+               return new FunctionPointer<{rename}>(
                        symbol$,
                        ({java-arguments}) -> {
                                {native-output-define}
@@ -53,7 +78,7 @@ code method {
        }}
 
        upcall {{
-       public static Memory.FunctionPointer<{rename}> upcall({rename} target$, ResourceScope scope$) {
+       public static FunctionPointer<{rename}> upcall({rename} target$, ResourceScope scope$) {
                interface Trampoline {
                        {java-result} call({native-arguments});
                }
@@ -62,14 +87,15 @@ code method {
                        {trampoline-result-define}target$.call({java-call});
                        {trampoline-result-return}
                };
-               return new Memory.FunctionPointer<>(
-                               Memory.upcall(
-                                       trampoline,
-                                       "call",
-                                       "{java-signature}",
-                                       descriptor(),
-                                       scope$),
-                               target$);
+               return new FunctionPointer<>(
+                       Memory.upcall(
+                               MethodHandles.lookup(),
+                               trampoline,
+                               "call",
+                               "{java-signature}",
+                               descriptor(),
+                               scope$),
+                       target$);
        }
        }}
 }
@@ -84,9 +110,30 @@ code class {
 package {package};
 import jdk.incubator.foreign.*;
 import java.lang.invoke.*;
-import {package}.Memory.*;
+import au.notzed.nativez.*;
+
+public class {name} {
+{defines}
+{enums}
+{funcs}
+}
+       }}
+       library-dynamic
+               func:template=code:method=invoke-dynamic
+               init:template=code:method=invoke-dynamic-init {{
+package {package};
+import jdk.incubator.foreign.*;
+import java.lang.invoke.*;
+import java.util.function.Function;
+import au.notzed.nativez.*;
 
 public class {name} {
+       {name}(Function<String,MemoryAddress> resolve, ResourceScope scope) {
+{init}
+       }
+       public static {name} create(Function<String,MemoryAddress> resolve, ResourceScope scope) {
+               return new {name}(resolve, scope);
+       }
 {defines}
 {enums}
 {funcs}
@@ -105,9 +152,9 @@ package {package};
 import jdk.incubator.foreign.*;
 import jdk.incubator.foreign.MemoryLayout.*;
 import java.lang.invoke.*;
-import {package}.Memory.*;
+import au.notzed.nativez.*;
 
-public class {rename} implements Memory.Addressable {
+public class {rename} implements Pointer {
 
        public final MemorySegment segment;
 
@@ -154,9 +201,9 @@ package {package};
 import jdk.incubator.foreign.*;
 import jdk.incubator.foreign.MemoryLayout.*;
 import java.lang.invoke.*;
-import {package}.Memory.*;
+import au.notzed.nativez.*;
 
-public class {rename} implements Memory.Addressable, Memory.Array<{rename}> {
+public class {rename} implements Pointer, Array<{rename}> {
 
        public final MemorySegment segment;
 
@@ -221,9 +268,9 @@ public class {rename} implements Memory.Addressable, Memory.Array<{rename}> {
 package {package};
 import jdk.incubator.foreign.*;
 import java.lang.invoke.*;
-import {package}.Memory.*;
+import au.notzed.nativez.*;
 
-public class {rename} implements Memory.Addressable {
+public class {rename} implements Pointer {
 
        MemoryAddress address;
        ResourceScope scope;
@@ -256,7 +303,7 @@ public class {rename} implements Memory.Addressable {
 package {package};
 import jdk.incubator.foreign.*;
 import java.lang.invoke.*;
-import {package}.Memory.*;
+import au.notzed.nativez.*;
 
 @FunctionalInterface
 public interface {rename} {
similarity index 98%
rename from src/code.pm
rename to src/notzed.nativez/lib/code.pm
index 156aeee..bfd8d6d 100644 (file)
@@ -7,7 +7,7 @@ use File::Basename;
 use Data::Dumper;
 use List::Util qw(first);
 
-require genapi;
+require api;
 
 my %typeSizes = (
        i8 => 'byte', u8 => 'byte',
@@ -109,7 +109,7 @@ sub findCode {
 
        if ($v =~ m/^(code:.+)=(.*)$/) {
                my $t = $api->{index}->{$1};
-               my $l = genapi::findItem($t, $2);
+               my $l = api::findItem($t, $2);
 
                die "Uknown template '$1.$2'" if !defined($t) || !defined($l);
                $l->{literal};
@@ -244,7 +244,7 @@ sub applyTemplate {
        my $match = shift;
        my $vars = \%{$match};
 
-       foreach my $set (genapi::optionValuesAll('set', $code)) {
+       foreach my $set (api::optionValuesAll('set', $code)) {
                $vars->{$1} = $2 if ($set =~ m/^(\w+)=(.*)/);
        }
 
similarity index 99%
rename from src/genconfig2.pm
rename to src/notzed.nativez/lib/config.pm
index c3f7af1..83f2aac 100644 (file)
@@ -1,4 +1,4 @@
-package genconfig2;
+package config;
 
 use File::Basename;
 use strict;
similarity index 90%
rename from src/method.pm
rename to src/notzed.nativez/lib/method.pm
index 490a743..9cf6f24 100644 (file)
@@ -20,9 +20,9 @@ sub fieldScopeAction {
        my $m = shift;
 
        if ($m->{field}->{scope} =~ m/^explicit,(.*)$/) {
-               return code::formatTemplate("scope\$.addCloseAction(() -> $1", { %{$m->{match}}, value => 'res$' });
+               return code::formatTemplate("scope\$.addCloseAction(() -> $1;", { %{$m->{match}}, value => 'res$' });
        } elsif ($m->{field}->{scope} =~ m/^explicit$/) {
-               'scope$.addCloseAction(() -> res$.close());';
+               return code::formatTemplate('scope$.addCloseAction(() -> close({name}));', $m->{match});
        } else {
                ();
        }
@@ -64,13 +64,13 @@ sub new {
 
                if ($m->{instance}) {
                        "(jdk.incubator.foreign.Addressable)address()";
+               } elsif ($m->{implied}) {
+                       $m->{implied};
                } else {
                        my $name = $_->{match}->{name};
 
                        if ($m->{output} == 0 && ($m->{deref} =~ m/^u64:/)) {
                                $name .= '$h';
-                       } elsif ($m->{output} == 0 && $m->{implied}) {
-                               $name = $m->{implied};
                        } elsif (defined($m->{'array-size-source'})) {
                                # or some function?
                                $name = "Memory.size($m->{'array-size-source'}->{name})";
@@ -87,7 +87,12 @@ sub new {
 
        # hidden output arguments
        # TODO: what about in/out arguments?  they just fall out by not specifying them as scoped?
-       my @output = grep {     $_->{field}->{output} == 0 && ($_->{field}->{deref} =~ m/^u64:/) && $_->{field}->{instance} == 0} @members;
+       my @output = grep {
+               !$_->{field}->{implied}
+               && $_->{field}->{output} == 0
+               && ($_->{field}->{deref} =~ m/^u64:/)
+               && $_->{field}->{instance} == 0
+       } @members;
 
        $info->{'native-output-define'} = join "\n\t\t",        map { apply('{carrieri} {name};', $_)                                                           } @output;
        $info->{'native-output-init'} = join ";\n\t\t\t",       map { apply('{type} {name}$h = {type}.createArray(1, frame$);', $_)     } @output;
@@ -95,7 +100,9 @@ sub new {
 
        # also required for some tonative types?
        my $x =  grep { $_->{match}->{type} eq 'String' } @members;
-       $info->{'create-frame'} = ($#output >= 0 || grep { $_->{match}->{type} eq 'String' } @members) ? '(Frame frame$ = Memory.createFrame()) ' : '';
+       $info->{'create-frame'} = ($#output >= 0 || grep {
+               $_->{match}->{type} eq 'String' || $_->{field}->{'raw-in'};
+       } @members) ? '(Frame frame$ = Frame.frame()) ' : '';
 
        # result code handling
        if ($c->{success}) {
similarity index 100%
rename from src/tokenise.pm
rename to