From 988e4010f0674ff902e88d3acbbfbd643a38f81f Mon Sep 17 00:00:00 2001 From: Not Zed Date: Sun, 6 Feb 2022 10:49:13 +1030 Subject: [PATCH] Moved to modular java build system. --- .gitignore | 4 + Makefile | 41 + README | 256 ++- api/Makefile | 18 - config.make.in | 49 + java.make | 416 ++++ src/Makefile | 16 - src/apigen.pm | 602 ------ src/generate-native | 1735 ----------------- {api => src/notzed.api/native}/api.c | 2 +- {api => src/notzed.api/native/libapi}/api.h | 0 src/notzed.api/native/native.make | 5 + .../classes}/api/test/TestAPI.java | 15 +- src/notzed.apiobject/classes/module-info.java | 5 + .../notzed.apiobject/gen/apiobject.api | 6 +- src/notzed.apiobject/gen/apiobject.h | 2 + src/notzed.apiobject/gen/gen.make | 3 + .../classes}/api/test/TestAPI.java | 11 +- src/notzed.apistatic/classes/module-info.java | 6 + .../notzed.apistatic/gen/apistatic.api | 2 +- src/notzed.apistatic/gen/apistatic.h | 2 + src/notzed.apistatic/gen/gen.make | 3 + src/notzed.clstatic/classes/module-info.java | 5 + .../classes/opencl/cl_event_list.java | 64 + .../classes/opencl/test/clinfo.java | 12 +- src/notzed.clstatic/gen/gen.make | 4 + src/notzed.clstatic/gen/opencl.api | 183 ++ .../notzed.clstatic/gen}/opencl.h | 0 .../classes}/ffmpeg/AVPixelReader.java | 2 +- .../classes}/ffmpeg/FramePixelReader.java | 52 +- .../classes}/ffmpeg/test/TestFFMPEG.java | 110 +- src/notzed.ffmpeg/classes/module-info.java | 7 + src/notzed.ffmpeg/gen/ffmpeg.api | 225 +++ src/notzed.ffmpeg/gen/ffmpeg.h | 27 + src/notzed.ffmpeg/gen/gen.make | 3 + src/notzed.nativez/bin/export-api | 114 ++ src/{ => notzed.nativez/bin}/export-defines | 29 +- .../bin/generate-api} | 137 +- .../classes/au/notzed/nativez/Array.java | 7 + .../classes/au/notzed/nativez/ByteArray.java | 94 + .../au/notzed/nativez/DoubleArray.java | 90 + .../classes/au/notzed/nativez/FloatArray.java | 90 + .../classes/au/notzed/nativez/Frame.java | 198 ++ .../au/notzed/nativez/FunctionPointer.java | 7 + .../au/notzed/nativez/HandleArray.java | 102 + .../classes/au/notzed/nativez/IntArray.java | 94 + .../classes/au/notzed/nativez/LongArray.java | 86 + .../classes/au/notzed/nativez/Memory.java | 128 ++ .../classes/au/notzed/nativez}/Native.java | 12 +- .../classes/au/notzed/nativez/Pointer.java | 14 + .../au/notzed/nativez/PointerArray.java | 86 + .../classes/au/notzed/nativez/ShortArray.java | 90 + src/notzed.nativez/classes/module-info.java | 5 + src/notzed.nativez/lib/api.pm | 974 +++++++++ src/{ => notzed.nativez/lib}/code.api | 85 +- src/{ => notzed.nativez/lib}/code.pm | 6 +- .../lib/config.pm} | 2 +- src/{ => notzed.nativez/lib}/method.pm | 19 +- src/{ => notzed.nativez/lib}/tokenise.pm | 0 src/{ => notzed.nativez/lib}/types.api | 44 +- src/{ => notzed.nativez/native}/export.cc | 0 src/{ => notzed.nativez/native}/list.h | 0 src/notzed.nativez/native/native.make | 7 + src/{ => notzed.nativez/native}/tree-codes.c | 0 src/notzed.vkheader/classes/module-info.java | 4 + src/notzed.vkheader/gen/gen.make | 4 + src/notzed.vkheader/gen/vkheader.api | 55 + src/notzed.vkheader/gen/vkheader.h | 2 + .../classes/module-info.java | 8 + .../PFN_vkDebugUtilsMessengerCallbackEXT.java | 17 +- .../VkPhysicalDeviceGroupProperties.java | 16 +- .../classes/vulkan}/test/TestVulkan.java | 171 +- .../notzed.vkregistry/gen}/VkDevice-part.java | 10 +- .../gen}/VkInstance-part.java | 13 +- src/notzed.vkregistry/gen/export-registry | 425 ++++ .../notzed.vkregistry/gen/export-vulkan | 91 +- src/notzed.vkregistry/gen/gen.make | 14 + .../notzed.vkregistry/gen}/mandelbrot.comp | 12 +- src/template/Frame.java | 136 -- src/template/Memory.java | 718 ------- test-api-object/Makefile | 44 - test-api-object/README | 8 - test-api-static/Makefile | 46 - test-api-static/README | 8 - test-ffmpeg/Makefile | 52 - test-ffmpeg/README | 12 - test-ffmpeg/ffmpeg.api | 222 --- test-ffmpeg/ffmpeg.h | 6 - test-opencl-basic/Makefile | 54 - test-opencl-basic/opencl.api | 164 -- test-vulkan/Makefile | 41 - test-vulkan/src/zvk/Frame.java | 136 -- test-vulkan/src/zvk/Memory.java | 560 ------ test-vulkan/src/zvk/PFN_Test.java | 107 - .../src/zvk/PFN_vkDebugReportCallbackEXT.java | 62 - .../PFN_vkDeviceMemoryReportCallbackEXT.java | 48 - 96 files changed, 4525 insertions(+), 5154 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile delete mode 100644 api/Makefile create mode 100644 config.make.in create mode 100644 java.make delete mode 100644 src/Makefile delete mode 100644 src/apigen.pm delete mode 100755 src/generate-native rename {api => src/notzed.api/native}/api.c (97%) rename {api => src/notzed.api/native/libapi}/api.h (100%) create mode 100644 src/notzed.api/native/native.make rename {test-api-object/src => src/notzed.apiobject/classes}/api/test/TestAPI.java (78%) create mode 100644 src/notzed.apiobject/classes/module-info.java rename test-api-object/api-object.api => src/notzed.apiobject/gen/apiobject.api (93%) create mode 100644 src/notzed.apiobject/gen/apiobject.h create mode 100644 src/notzed.apiobject/gen/gen.make rename {test-api-static/src => src/notzed.apistatic/classes}/api/test/TestAPI.java (83%) create mode 100644 src/notzed.apistatic/classes/module-info.java rename test-api-static/api-static.api => src/notzed.apistatic/gen/apistatic.api (95%) create mode 100644 src/notzed.apistatic/gen/apistatic.h create mode 100644 src/notzed.apistatic/gen/gen.make create mode 100644 src/notzed.clstatic/classes/module-info.java create mode 100755 src/notzed.clstatic/classes/opencl/cl_event_list.java rename test-opencl-basic/src/opencl/test/TestOpenCL.java => src/notzed.clstatic/classes/opencl/test/clinfo.java (90%) create mode 100644 src/notzed.clstatic/gen/gen.make create mode 100644 src/notzed.clstatic/gen/opencl.api rename {test-opencl-basic => src/notzed.clstatic/gen}/opencl.h (100%) rename {test-ffmpeg/src/proto => src/notzed.ffmpeg/classes}/ffmpeg/AVPixelReader.java (98%) rename {test-ffmpeg/src/proto => src/notzed.ffmpeg/classes}/ffmpeg/FramePixelReader.java (73%) rename {test-ffmpeg/src => src/notzed.ffmpeg/classes}/ffmpeg/test/TestFFMPEG.java (54%) create mode 100644 src/notzed.ffmpeg/classes/module-info.java create mode 100644 src/notzed.ffmpeg/gen/ffmpeg.api create mode 100644 src/notzed.ffmpeg/gen/ffmpeg.h create mode 100644 src/notzed.ffmpeg/gen/gen.make create mode 100755 src/notzed.nativez/bin/export-api rename src/{ => notzed.nativez/bin}/export-defines (94%) rename src/{generate-api-2 => notzed.nativez/bin/generate-api} (71%) create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/Array.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/ByteArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/DoubleArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/FloatArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/Frame.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/FunctionPointer.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/IntArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/LongArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/Memory.java rename src/{template => notzed.nativez/classes/au/notzed/nativez}/Native.java (96%) create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/Pointer.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/PointerArray.java create mode 100644 src/notzed.nativez/classes/au/notzed/nativez/ShortArray.java create mode 100644 src/notzed.nativez/classes/module-info.java create mode 100644 src/notzed.nativez/lib/api.pm rename src/{ => notzed.nativez/lib}/code.api (79%) rename src/{ => notzed.nativez/lib}/code.pm (98%) rename src/{genconfig2.pm => notzed.nativez/lib/config.pm} (99%) rename src/{ => notzed.nativez/lib}/method.pm (90%) rename src/{ => notzed.nativez/lib}/tokenise.pm (100%) rename src/{ => notzed.nativez/lib}/types.api (87%) rename src/{ => notzed.nativez/native}/export.cc (100%) rename src/{ => notzed.nativez/native}/list.h (100%) create mode 100644 src/notzed.nativez/native/native.make rename src/{ => notzed.nativez/native}/tree-codes.c (100%) create mode 100644 src/notzed.vkheader/classes/module-info.java create mode 100644 src/notzed.vkheader/gen/gen.make create mode 100644 src/notzed.vkheader/gen/vkheader.api create mode 100644 src/notzed.vkheader/gen/vkheader.h create mode 100644 src/notzed.vkregistry/classes/module-info.java rename {test-vulkan/src/zvk => src/notzed.vkregistry/classes/vulkan}/PFN_vkDebugUtilsMessengerCallbackEXT.java (76%) rename {test-vulkan/src/zvk => src/notzed.vkregistry/classes/vulkan}/VkPhysicalDeviceGroupProperties.java (86%) rename {test-vulkan/src/zvk => src/notzed.vkregistry/classes/vulkan}/test/TestVulkan.java (76%) rename {test-vulkan/template => src/notzed.vkregistry/gen}/VkDevice-part.java (82%) rename {test-vulkan/template => src/notzed.vkregistry/gen}/VkInstance-part.java (88%) create mode 100755 src/notzed.vkregistry/gen/export-registry rename test-vulkan/generate-vulkan => src/notzed.vkregistry/gen/export-vulkan (96%) create mode 100644 src/notzed.vkregistry/gen/gen.make rename {test-vulkan => src/notzed.vkregistry/gen}/mandelbrot.comp (88%) delete mode 100644 src/template/Frame.java delete mode 100644 src/template/Memory.java delete mode 100644 test-api-object/Makefile delete mode 100644 test-api-object/README delete mode 100644 test-api-static/Makefile delete mode 100644 test-api-static/README delete mode 100644 test-ffmpeg/Makefile delete mode 100644 test-ffmpeg/README delete mode 100644 test-ffmpeg/ffmpeg.api delete mode 100644 test-ffmpeg/ffmpeg.h delete mode 100644 test-opencl-basic/Makefile delete mode 100644 test-opencl-basic/opencl.api delete mode 100644 test-vulkan/Makefile delete mode 100644 test-vulkan/src/zvk/Frame.java delete mode 100644 test-vulkan/src/zvk/Memory.java delete mode 100644 test-vulkan/src/zvk/PFN_Test.java delete mode 100644 test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java delete mode 100644 test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58686c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +config.make +mandelbrot.pam +movie.avi diff --git a/Makefile b/Makefile new file mode 100644 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 --- a/README +++ b/README @@ -2,53 +2,251 @@ 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//classes - compiled java modules +bin/gen//classes - generated java +bin/gen//gen - generated intermediate non-java + +These are more or less an exploded view of all jmod files: + +bin//bin - commands for all modules +bin//lib - libraries/config and modular jar files for all modules +bin//include - header files for all modules + +Finally: + +bin//jmods - .jmod modules + + +Demos +----- + +Most examples have a demo, see the _JMAIN variables in the +Makefile for the targets. They are executed using: + +$ make run-/ + +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__. + + - 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 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 index fbe6563..0000000 --- a/api/Makefile +++ /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 index 0000000..d682dd1 --- /dev/null +++ b/config.make.in @@ -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 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 . +# + +# 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//classes. Resource files are +# stored in src//classes. Source-code +# generators must exist in src//gen. Native +# libraries must exist in src//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 + +# _JDEPMOD Lists modules which this one depends on. + +# _JAVACFLAGS Extra module-specific flags for each command. +# _JARFLAGS +# _JMODFLAGS + +# _JAVA Java sources. If not set it is found from src//classes/(*.java) +# _RESOURCES .jar resources. If not set it is found from src//classes/(not *.java) +# _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 + +# _gendir Location for files used in Java generation process (per project). +# _genjavadir Location where _JAVA_GENERATED .java files will be created (per project). +# _objdir Location for c objects (per target). +# _incdir Location for output includes, .jmod staging. +# _libdir Location for output libraries, .jmod staging. May point to _bindir. +# _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. + +# _NATIVE_LIBRARIES list of libraries to build. +# library names match System.loadLibrary(). + +# Global variables + +# _LDFLAGS +# _LDLIBS +# _CPPFLAGS +# _CFLAGS +# _CC +# _CXXFLAGS +# _CXX +# SO shared library suffix +# LIB shared library prefix + +# Utility functions +# +# $(call library-path,,) will resolve to the library file name. + +# Per library variables. + +# _SOURCES .c source files for library. Paths are relative to src//native. +# _CXXSOURCES .c source files for library. Paths are relative to src//native. +# _HEADERS header files for install/jmod +# _COMMANDS commands/bin/scripts for install/jmod + +# _LDFLAGS link flags +# _LIBADD extra objects to add to link line +# _LDLIBS link libraries +# _CPPFLAGS c and c++ pre-processor flags. "-Isrc//jni -Ibin/include/" is implicit. +# _CCFLAGS c compiler flags +# _CXXFLAGS c++ compiler flags + +# _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// .h files from javac -h +# bin/modules// .class files from javac + +# This layout is convenient for netbeans +# bin/gen//gen/ .c, exe files for generator free use +# bin/gen//classes/ .java files from generator _JAVA_GENERATED + +# Working files +# bin/status/ marker files for makefile + +# bin///lib .so librareies for jmod _LIBRARIES = libname +# bin///obj .o, .d files for library _SOURCES +# bin///include .h files for jmod _HEADERS +# bin///.jmod .jmod module + +# Output files +# bin//lib/ modular jar files and shared libraries for GNU/linux dev +# bin//include/ header files for exported shared libraries +# bin//bin/ shared libraries for microsoft dev +# bin//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 +# ###################################################################### + +# _API List of api's to include +# _APIFLAGS Extra flags to pass to export-api for every api in module +# __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 index ebab9dc..0000000 --- a/src/Makefile +++ /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 index afb5cda..0000000 --- a/src/apigen.pm +++ /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:' => { - name => '', - items => [], - options => [ 'default=none', 'access=rw' ], - regex => qr/^struct:$/, - type => 'struct' - }, - 'union:' => { - name => '', - items => [], - options => [ 'default=none', 'access=rw' ], - regex => qr/^union:$/, - type => 'union' - }, - 'call:' => { - name => '', - items => [], - options => [ 'rename=call', 'access=r' ], - regex => qr/^call:$/, - 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}:"}; - - 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:' 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}:"}, $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 '') { - 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 index b17434c..0000000 --- a/src/generate-native +++ /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 ''; - 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:\n" if $verbose && $api->{"$type:"}; - return $api->{"$type:"}; -} - -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:'}; - delete $seen{'func:'}; - delete $seen{'call:'}; - - 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 '') { - $api->{"$obj->{type}:"} = $obj; - } - } - - $api->{'call:'} = { rename => $renameTable{'identity'}, scope => 'static'} if !$api->{'call:'}; -} - -# 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 <{name}\", $m->{offset});\n"; - } -my $bytes = $s->{size}/8; - print $f <{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 ''; - - 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; -} diff --git a/api/api.c b/src/notzed.api/native/api.c similarity index 97% rename from api/api.c rename to src/notzed.api/native/api.c index 58cac5f..0a2b03b 100644 --- a/api/api.c +++ b/src/notzed.api/native/api.c @@ -2,7 +2,7 @@ #include #include #include -#include "api.h" +#include "libapi/api.h" static void funca(int a) { printf("funca: %d\n", a); diff --git a/api/api.h b/src/notzed.api/native/libapi/api.h 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 index 0000000..a5a1efb --- /dev/null +++ b/src/notzed.api/native/native.make @@ -0,0 +1,5 @@ + +notzed.api_NATIVE_LIBRARIES=api + +api_SOURCES=api.c +api_HEADERS=libapi/api.h diff --git a/test-api-object/src/api/test/TestAPI.java b/src/notzed.apiobject/classes/api/test/TestAPI.java similarity index 78% rename from test-api-object/src/api/test/TestAPI.java rename to src/notzed.apiobject/classes/api/test/TestAPI.java index cc92c00..3728cfd 100644 --- a/test-api-object/src/api/test/TestAPI.java +++ b/src/notzed.apiobject/classes/api/test/TestAPI.java @@ -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 cb = Call__i32.upcall(() -> { + FunctionPointer 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 funca = Call_i32_v.downcall(api.func("funca"), scope); + FunctionPointer 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 index 0000000..cb7ca72 --- /dev/null +++ b/src/notzed.apiobject/classes/module-info.java @@ -0,0 +1,5 @@ + +module notzed.apiobject { + requires transitive notzed.nativez; + exports api; +} diff --git a/test-api-object/api-object.api b/src/notzed.apiobject/gen/apiobject.api similarity index 93% rename from test-api-object/api-object.api rename to src/notzed.apiobject/gen/apiobject.api index 91d14b8..408166b 100644 --- a/test-api-object/api-object.api +++ b/src/notzed.apiobject/gen/apiobject.api @@ -10,14 +10,14 @@ struct default=all field:rename=studly-caps access=rw { call 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 index 0000000..32a074c --- /dev/null +++ b/src/notzed.apiobject/gen/apiobject.h @@ -0,0 +1,2 @@ + +#include diff --git a/src/notzed.apiobject/gen/gen.make b/src/notzed.apiobject/gen/gen.make new file mode 100644 index 0000000..719527c --- /dev/null +++ b/src/notzed.apiobject/gen/gen.make @@ -0,0 +1,3 @@ + +notzed.apiobject_API = apiobject +notzed.apiobject_APIFLAGS = -t api -Isrc/notzed.api/native diff --git a/test-api-static/src/api/test/TestAPI.java b/src/notzed.apistatic/classes/api/test/TestAPI.java similarity index 83% rename from test-api-static/src/api/test/TestAPI.java rename to src/notzed.apistatic/classes/api/test/TestAPI.java index 559498f..1014199 100644 --- a/test-api-static/src/api/test/TestAPI.java +++ b/src/notzed.apistatic/classes/api/test/TestAPI.java @@ -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 cb = Call__i32.upcall(() -> { + FunctionPointer 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 funca = Call_i32_v.downcall(api_func("funca"), scope); + FunctionPointer 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 index 0000000..6662845 --- /dev/null +++ b/src/notzed.apistatic/classes/module-info.java @@ -0,0 +1,6 @@ + +module notzed.apistatic { + requires notzed.nativez; + + exports api; +} diff --git a/test-api-static/api-static.api b/src/notzed.apistatic/gen/apistatic.api similarity index 95% rename from test-api-static/api-static.api rename to src/notzed.apistatic/gen/apistatic.api index e3c7eca..da1fff8 100644 --- a/test-api-static/api-static.api +++ b/src/notzed.apistatic/gen/apistatic.api @@ -20,6 +20,6 @@ call 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 index 0000000..32a074c --- /dev/null +++ b/src/notzed.apistatic/gen/apistatic.h @@ -0,0 +1,2 @@ + +#include diff --git a/src/notzed.apistatic/gen/gen.make b/src/notzed.apistatic/gen/gen.make new file mode 100644 index 0000000..9a17dc5 --- /dev/null +++ b/src/notzed.apistatic/gen/gen.make @@ -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 index 0000000..4b7b5db --- /dev/null +++ b/src/notzed.clstatic/classes/module-info.java @@ -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 index 0000000..f53b441 --- /dev/null +++ b/src/notzed.clstatic/classes/opencl/cl_event_list.java @@ -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; + } +} diff --git a/test-opencl-basic/src/opencl/test/TestOpenCL.java b/src/notzed.clstatic/classes/opencl/test/clinfo.java similarity index 90% rename from test-opencl-basic/src/opencl/test/TestOpenCL.java rename to src/notzed.clstatic/classes/opencl/test/clinfo.java index 2f1e429..1856499 100644 --- a/test-opencl-basic/src/opencl/test/TestOpenCL.java +++ b/src/notzed.clstatic/classes/opencl/test/clinfo.java @@ -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 platforms; int res; diff --git a/src/notzed.clstatic/gen/gen.make b/src/notzed.clstatic/gen/gen.make new file mode 100644 index 0000000..43303c7 --- /dev/null +++ b/src/notzed.clstatic/gen/gen.make @@ -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 index 0000000..11126ea --- /dev/null +++ b/src/notzed.clstatic/gen/opencl.api @@ -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= { + type {{ 'cl_event_list' }} + tonative {{ 'cl_event_list.address({value})' }} +} + +include types.api + +struct 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: {{ + 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; + +} diff --git a/test-opencl-basic/opencl.h b/src/notzed.clstatic/gen/opencl.h similarity index 100% rename from test-opencl-basic/opencl.h rename to src/notzed.clstatic/gen/opencl.h diff --git a/test-ffmpeg/src/proto/ffmpeg/AVPixelReader.java b/src/notzed.ffmpeg/classes/ffmpeg/AVPixelReader.java similarity index 98% rename from test-ffmpeg/src/proto/ffmpeg/AVPixelReader.java rename to src/notzed.ffmpeg/classes/ffmpeg/AVPixelReader.java index 6ca4a3c..f2132c3 100644 --- a/test-ffmpeg/src/proto/ffmpeg/AVPixelReader.java +++ b/src/notzed.ffmpeg/classes/ffmpeg/AVPixelReader.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package proto.ffmpeg; +package ffmpeg; import java.nio.ByteBuffer; diff --git a/test-ffmpeg/src/proto/ffmpeg/FramePixelReader.java b/src/notzed.ffmpeg/classes/ffmpeg/FramePixelReader.java similarity index 73% rename from test-ffmpeg/src/proto/ffmpeg/FramePixelReader.java rename to src/notzed.ffmpeg/classes/ffmpeg/FramePixelReader.java index 26ec177..1a382b9 100644 --- a/test-ffmpeg/src/proto/ffmpeg/FramePixelReader.java +++ b/src/notzed.ffmpeg/classes/ffmpeg/FramePixelReader.java @@ -16,13 +16,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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() { diff --git a/test-ffmpeg/src/ffmpeg/test/TestFFMPEG.java b/src/notzed.ffmpeg/classes/ffmpeg/test/TestFFMPEG.java similarity index 54% rename from test-ffmpeg/src/ffmpeg/test/TestFFMPEG.java rename to src/notzed.ffmpeg/classes/ffmpeg/test/TestFFMPEG.java index cbec649..7e8d73b 100644 --- a/test-ffmpeg/src/ffmpeg/test/TestFFMPEG.java +++ b/src/notzed.ffmpeg/classes/ffmpeg/test/TestFFMPEG.java @@ -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 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 index 0000000..a156a85 --- /dev/null +++ b/src/notzed.ffmpeg/classes/module-info.java @@ -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 index 0000000..a003ecb --- /dev/null +++ b/src/notzed.ffmpeg/gen/ffmpeg.api @@ -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 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: {{ + 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: {{ + 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 index 0000000..9d1868b --- /dev/null +++ b/src/notzed.ffmpeg/gen/ffmpeg.h @@ -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 +#include +#include +#include +#include diff --git a/src/notzed.ffmpeg/gen/gen.make b/src/notzed.ffmpeg/gen/gen.make new file mode 100644 index 0000000..4b7855a --- /dev/null +++ b/src/notzed.ffmpeg/gen/gen.make @@ -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 index 0000000..5823932 --- /dev/null +++ b/src/notzed.nativez/bin/export-api @@ -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"); diff --git a/src/export-defines b/src/notzed.nativez/bin/export-defines similarity index 94% rename from src/export-defines rename to src/notzed.nativez/bin/export-defines index 1774d21..7b986e0 100755 --- a/src/export-defines +++ b/src/notzed.nativez/bin/export-defines @@ -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>) { diff --git a/src/generate-api-2 b/src/notzed.nativez/bin/generate-api similarity index 71% rename from src/generate-api-2 rename to src/notzed.nativez/bin/generate-api index df765b2..9d6a74f 100755 --- a/src/generate-api-2 +++ b/src/notzed.nativez/bin/generate-api @@ -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:'}; + 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:') { + 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}:"}; - 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:'}; 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 index 0000000..30d0f7c --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/Array.java @@ -0,0 +1,7 @@ + +package au.notzed.nativez; + +public interface Array { + 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 index 0000000..bfe21da --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/ByteArray.java @@ -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 . + */ + +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 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 index 0000000..9a0bfce --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/DoubleArray.java @@ -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 . + */ + +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 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 index 0000000..e81a57e --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/FloatArray.java @@ -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 . + */ + +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 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 index 0000000..e45032e --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/Frame.java @@ -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 . + */ +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. + *
+ * try (Frame f = Memory.createFrame()) {
+ *		MemorySegment a = f.allocate(size);
+ * }
+ * 
+ * Any memory allocated is freed when the frame is closed. + *

+ * 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 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 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 MemorySegment copy(T value) { + return copy(value.address()); + } + + public 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 index 0000000..dd36722 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/FunctionPointer.java @@ -0,0 +1,7 @@ + +package au.notzed.nativez; + +import jdk.incubator.foreign.NativeSymbol; + +public record FunctionPointer(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 index 0000000..3f7d299 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java @@ -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 . + */ + +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 extends AbstractList implements Pointer { + public final MemorySegment segment; + final ResourceScope scope; + BiFunction create; + + private HandleArray(MemorySegment segment, BiFunction create, ResourceScope scope) { + this.segment = segment; + this.create = create; + this.scope = scope; + } + + public static HandleArray create(MemorySegment segment, BiFunction create) { + return new HandleArray<>(segment, create, segment.scope()); + } + + public static HandleArray create(MemorySegment segment, BiFunction create, ResourceScope scope) { + return new HandleArray<>(segment, create, scope); + } + + public static HandleArray createArray(long size, SegmentAllocator alloc, BiFunction create) { + return create(alloc.allocateArray(Memory.POINTER, size), create); + } + + public static HandleArray createArray(long size, SegmentAllocator alloc, BiFunction create, ResourceScope scope) { + return create(alloc.allocateArray(Memory.POINTER, size), create, scope); + } + + public static HandleArray createArray(MemoryAddress address, long size, BiFunction 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 index 0000000..0b82479 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/IntArray.java @@ -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 . + */ + +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 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 index 0000000..9885ced --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/LongArray.java @@ -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 . + */ + +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 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 index 0000000..c935f22 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/Memory.java @@ -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 . + */ + +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 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 MemoryAddress address(FunctionPointer 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; + } +} diff --git a/src/template/Native.java b/src/notzed.nativez/classes/au/notzed/nativez/Native.java similarity index 96% rename from src/template/Native.java rename to src/notzed.nativez/classes/au/notzed/nativez/Native.java index d214edc..191728b 100644 --- a/src/template/Native.java +++ b/src/notzed.nativez/classes/au/notzed/nativez/Native.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package api; +package au.notzed.nativez; import java.io.StringReader; import java.lang.invoke.*; @@ -45,7 +45,7 @@ import java.lang.System.Logger.Level; *

* 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 resolve(MemoryAddress p, Function create) { + public static T resolve(Class jtype, MemoryAddress p, Function 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 index 0000000..cf93675 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java @@ -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 index 0000000..cdc4189 --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/PointerArray.java @@ -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 . + */ + +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 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 index 0000000..f93599d --- /dev/null +++ b/src/notzed.nativez/classes/au/notzed/nativez/ShortArray.java @@ -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 . + */ + +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 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 index 0000000..7edcf0a --- /dev/null +++ b/src/notzed.nativez/classes/module-info.java @@ -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 index 0000000..ec07956 --- /dev/null +++ b/src/notzed.nativez/lib/api.pm @@ -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:' => { + name => '', + items => [], + options => [ 'default=none', 'access=rw', 'field:rename=studly-caps' ], + regex => qr/^struct:$/, + type => 'struct' + }, + 'union:' => { + name => '', + items => [], + options => [ 'default=all', 'access=rw', 'field:rename=studly-caps' ], + regex => qr/^union:$/, + type => 'union' + }, + 'call:' => { + name => '', + items => [], + options => [ 'call:rename=call', 'access=r' ], + regex => qr/^call:$/, + type => 'call' + }, + 'func:' => { + name => '', + items => [], + options => [], + regex => qr/^func:$/, + type => 'func' + }, + 'enum:' => { + name => '', + items => [], + options => [], + regex => qr/^enum:$/, + 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}:"}; + + 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:'}) 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:') { + 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}:"}, $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}:"}; + + #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}:"}; + + 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}:"}; + 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: 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}:"}; + + 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 ''} @{$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; diff --git a/src/code.api b/src/notzed.nativez/lib/code.api similarity index 79% rename from src/code.api rename to src/notzed.nativez/lib/code.api index 884866a..256b74f 100644 --- a/src/code.api +++ b/src/notzed.nativez/lib/code.api @@ -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 resolve, ResourceScope scope) { +{init} + } + public static {name} create(Function 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} { diff --git a/src/code.pm b/src/notzed.nativez/lib/code.pm similarity index 98% rename from src/code.pm rename to src/notzed.nativez/lib/code.pm index 156aeee..bfd8d6d 100644 --- a/src/code.pm +++ b/src/notzed.nativez/lib/code.pm @@ -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+)=(.*)/); } diff --git a/src/genconfig2.pm b/src/notzed.nativez/lib/config.pm similarity index 99% rename from src/genconfig2.pm rename to src/notzed.nativez/lib/config.pm index c3f7af1..83f2aac 100644 --- a/src/genconfig2.pm +++ b/src/notzed.nativez/lib/config.pm @@ -1,4 +1,4 @@ -package genconfig2; +package config; use File::Basename; use strict; diff --git a/src/method.pm b/src/notzed.nativez/lib/method.pm similarity index 90% rename from src/method.pm rename to src/notzed.nativez/lib/method.pm index 490a743..9cf6f24 100644 --- a/src/method.pm +++ b/src/notzed.nativez/lib/method.pm @@ -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}) { diff --git a/src/tokenise.pm b/src/notzed.nativez/lib/tokenise.pm similarity index 100% rename from src/tokenise.pm rename to src/notzed.nativez/lib/tokenise.pm diff --git a/src/types.api b/src/notzed.nativez/lib/types.api similarity index 87% rename from src/types.api rename to src/notzed.nativez/lib/types.api index 094311a..633d554 100644 --- a/src/types.api +++ b/src/notzed.nativez/lib/types.api @@ -63,7 +63,7 @@ type /^\[(?\d+)u64:(?[uif]\d+)\]$/ typei {{ ucfirst($typeSizes{$match->{ctype}}).'Array' }} getnativei {{ '{typei}.createArray((MemoryAddress){name}$EH.get({segment}, {index}), Long.MAX_VALUE, {scope})' }} - setnativei {{ '{name}$EH.set({segment}, {index}, (jdk.incubator.foreign.Addressable)Memory.address({value}))' }} + setnativei {{ '{name}$EH.set({segment}, {index}, (Addressable)Memory.address({value}))' }} } @@ -85,7 +85,7 @@ type /^\[(?\d+)(?[uif]\d+)\]$/ layout {{ "MemoryLayout.sequenceLayout({length}, Memory.".uc($typeSizes{$match->{ctype}}).")" }} type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }} tojava {{ "{type}.createArray({value}, $match->{length}, {scope})" }} - tonative {{ "(jdk.incubator.foreign.Addressable)Memory.address({value}" }} + tonative {{ "(Addressable)Memory.address({value}" }} getnative {{ "{type}.create((MemorySegment)$m->{name}\$SH.invokeExact({segment}))" }} setnative; @@ -105,7 +105,7 @@ type /^\[(?\d+)\[(?\d+)(?[uif]\d+)\]\]$/ layout {{ "MemoryLayout.sequenceLayout({length0}, MemoryLayout.sequenceLayout({length1}, Memory.".uc($typeSizes{$match->{ctype}})."))" }} type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }} tojava {{ "{type}.create({value})" }} - tonative {{ "(jdk.incubator.foreign.Addressable)Memory.address({value})" }} + tonative {{ "(Addressable)Memory.address({value})" }} getnative {{ "{type}.create((MemorySegment)$m->{name}\$SH.invokeExact({segment}))" }} setnative; @@ -120,6 +120,15 @@ type /^\[(?\d+)\[(?\d+)(?[uif]\d+)\]\]$/ }} } +# any pointer with 'raw' option +type /^u64:/ select=raw copy= { +} + +# pointer is an 'in' only pointer to pointer +type /^u64:u64:/ select=raw-in copy= { + tonative {{ '(Addressable)frame$.allocate(Memory.POINTER, {value})' }} +} + # function pointer type /^u64:\(/ copy= { type {{ "FunctionPointer<$data->{$m->{type}}->{rename}>" }} @@ -155,17 +164,18 @@ type /^u64:u64:\$\{(\w+)\}$/ copy= { # **void type /^u64:u64:v$/ copy= { - typei {{ 'PointerArray' }} - type {{ "HandleArray<{typei}>" }} - tojava {{ "HandleArray.createArray({value}, Long.MAX_VALUE, PointerArray"."::create, {scope})" }} + length {{ $m->{'array-size'} ? 'get'.($m->{'array-size'}->{rename}).'()' : 'Long.MAX_VALUE' }} + type {{ !$m->{array} ? 'PointerArray' : 'HandleArray' }} + tojava {{ + !$m->{array} + ? 'PointerArray.createArray({value}, {length}, {scope})' + : 'HandleArray.createArray({value}, {length}, PointerArray::create, {scope})' + }} } -# *primitive, or string for [iNNu8] - -# idea for when i implement 'flag' matching for types -# *i8 with 'array' flag +# *primitive, or string for [iNNi8] # TODO: length, multiple flags? -type /^u64:(?i8)$/ select=array copy= { +type /^u64:(?[ui]8)$/ select=array copy= { type {{ ucfirst($typeSizes{$match->{ctype}}).'Array' }} tojava {{ '{type}.createArray({value}, Long.MAX_VALUE, {scope})' }} carrieri {{ "{typei}" }} @@ -174,7 +184,7 @@ type /^u64:(?i8)$/ select=array copy= { } # *i8 with 'segment' flag, length must be supplied (somewhere??) -type /^u64:(?i8)$/ select=segment copy= { +type /^u64:(?[ui]8)$/ select=segment copy= { type {{ 'MemorySegment' }} tojava {{ '{value}' }} carrieri {{ "{typei}" }} @@ -185,7 +195,7 @@ type /^u64:(?i8)$/ select=segment copy= { # *i8 with no flag = String type /^u64:(?i8)$/ copy= { type {{ 'String' }} - tonative {{ '(jdk.incubator.foreign.Addressable)frame$.copy({value})' }} + tonative {{ '(Addressable)frame$.copy({value})' }} setnative {{ '{name}$VH.set({segment}, {copynative})' }} copynative {{ 'SegmentAllocator.nativeAllocator(segment.scope()).allocateUtf8String({value})' }} @@ -196,7 +206,7 @@ type /^u64:(?i8)$/ copy= { # *Primitive fallback type /^u64:(?[uif]\d+)$/ copy= { type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }} - tonative {{ '(jdk.incubator.foreign.Addressable)Memory.address({value})' }} + tonative {{ '(Addressable)Memory.address({value})' }} tojava {{ ucfirst($typeSizes{$match->{ctype}})."Array.createArray({value}, Long.MAX_VALUE, {scope})" }} carrieri {{ "{typei}" }} typei {{ $typeSizes{$match->{ctype}} }} @@ -234,8 +244,8 @@ type /^\$\{(\w+)\}$/ byvalue template=code:getbyvalue { carrier {{ 'MemoryAddress' }} type {{ "$data->{$m->{type}}->{rename}" }} tojava {{ '{type}.create({value}, {scope})' }} - tonative {{ '(jdk.incubator.foreign.Addressable)Memory.address({value})' }} - getnative {{ '{type}.create(({carrier}){name}$SH.invokeExact({segment}), {scope})' }} + tonative {{ '(Addressable)Memory.address({value})' }} + getnative {{ '{type}.create((MemorySegment){name}$SH.invokeExact({segment}))' }} setnative; varhandle {{ 'final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));'."\n" }} } @@ -252,5 +262,5 @@ type copy= { layout {{ 'Memory.POINTER' }} carrier {{ 'MemoryAddress' }} type {{ 'MemoryAddress' }} - tonative {{ '(jdk.incubator.foreign.Addressable)Memory.address({value})' }} + tonative {{ '(Addressable)Memory.address({value})' }} } diff --git a/src/export.cc b/src/notzed.nativez/native/export.cc similarity index 100% rename from src/export.cc rename to src/notzed.nativez/native/export.cc diff --git a/src/list.h b/src/notzed.nativez/native/list.h similarity index 100% rename from src/list.h rename to src/notzed.nativez/native/list.h diff --git a/src/notzed.nativez/native/native.make b/src/notzed.nativez/native/native.make new file mode 100644 index 0000000..0664cf9 --- /dev/null +++ b/src/notzed.nativez/native/native.make @@ -0,0 +1,7 @@ + +notzed.nativez_NATIVE_LIBRARIES = export + +export_CXXSOURCES = export.cc +export_SOURCES = tree-codes.c +export_CXXFLAGS = -Wno-switch -g +export_CPPFLAGS=-I. -I$(GCCPLUGINDIR)/include diff --git a/src/tree-codes.c b/src/notzed.nativez/native/tree-codes.c similarity index 100% rename from src/tree-codes.c rename to src/notzed.nativez/native/tree-codes.c diff --git a/src/notzed.vkheader/classes/module-info.java b/src/notzed.vkheader/classes/module-info.java new file mode 100644 index 0000000..7550c21 --- /dev/null +++ b/src/notzed.vkheader/classes/module-info.java @@ -0,0 +1,4 @@ + +module notzed.vkheader { + requires notzed.nativez; +} diff --git a/src/notzed.vkheader/gen/gen.make b/src/notzed.vkheader/gen/gen.make new file mode 100644 index 0000000..cf30e7d --- /dev/null +++ b/src/notzed.vkheader/gen/gen.make @@ -0,0 +1,4 @@ + + +notzed.vkheader_API = vkheader +notzed.vkheader_APIFLAGS = -t vulkan -Isrc/notzed.vkheader/gen diff --git a/src/notzed.vkheader/gen/vkheader.api b/src/notzed.vkheader/gen/vkheader.api new file mode 100644 index 0000000..d8551f2 --- /dev/null +++ b/src/notzed.vkheader/gen/vkheader.api @@ -0,0 +1,55 @@ +# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*- + +include types.api +include code.api + +struct rename=s/_T$// { +} + +struct VkInstance_T { + func:vkGetInstanceProcAddr raw:result$ instance:0; + + # be nice if findMatches() took the name of the target then it could handle pattern matching struct // automatically + # todo: check this works for extension functions + func: instance:0 {{ + # another way perhaps? func: match:0=u64:${VkInstance_T}; + sub { + my $s = shift; + return $s->{type} eq 'func' && defined($s->{items}->[0]) && $s->{items}->[0]->{deref} eq 'u64:${VkInstance_T}'; + } + }} + + # another version for extension functions - uses a dynamic func resolution + #func: instance:0 func:template=code:vulkan-method=invoke-dynamic {{ + #}} +} + +struct // { +} + +code vulkan-method { + + invoke-dynamic {{ + MethodHandle {name}$FH; + public {java-result} {rename}({java-arguments}) { + {native-output-define} + {native-result-define} + try {create-frame}{ + if ({name}$FH == null) + {name}$FH = Memory.downcall("{name}", vkGetInstanceProcAddr("{name}"), {function-descriptor}, scope()); + + {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} + } + }} +} diff --git a/src/notzed.vkheader/gen/vkheader.h b/src/notzed.vkheader/gen/vkheader.h new file mode 100644 index 0000000..3c9b0dd --- /dev/null +++ b/src/notzed.vkheader/gen/vkheader.h @@ -0,0 +1,2 @@ + +#include diff --git a/src/notzed.vkregistry/classes/module-info.java b/src/notzed.vkregistry/classes/module-info.java new file mode 100644 index 0000000..ccc933f --- /dev/null +++ b/src/notzed.vkregistry/classes/module-info.java @@ -0,0 +1,8 @@ + +module notzed.vkregistry { + requires transitive notzed.nativez; + + requires java.desktop; + + exports vulkan; +} diff --git a/test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java b/src/notzed.vkregistry/classes/vulkan/PFN_vkDebugUtilsMessengerCallbackEXT.java similarity index 76% rename from test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java rename to src/notzed.vkregistry/classes/vulkan/PFN_vkDebugUtilsMessengerCallbackEXT.java index 873d694..eacc838 100644 --- a/test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java +++ b/src/notzed.vkregistry/classes/vulkan/PFN_vkDebugUtilsMessengerCallbackEXT.java @@ -1,10 +1,11 @@ -package zvk; +package vulkan; import java.lang.invoke.*; import jdk.incubator.foreign.*; +import au.notzed.nativez.*; -public class PFN_vkDebugUtilsMessengerCallbackEXT implements Memory.Addressable { +public class PFN_vkDebugUtilsMessengerCallbackEXT implements Pointer { NativeSymbol symbol; @@ -20,11 +21,11 @@ public class PFN_vkDebugUtilsMessengerCallbackEXT implements Memory.Addressable int call(int severity, int flags, VkDebugUtilsMessengerCallbackDataEXT data); } - private interface Trampoline { - int call(int severity, int flags, MemoryAddress addr, MemoryAddress userData); - } - public static NativeSymbol of(Callback target, ResourceScope scope) { + interface Trampoline { + int call(int severity, int flags, MemoryAddress addr, MemoryAddress userData); + } + Trampoline trampline = (severity, flags, addr, userData) -> { try { return target.call(severity, flags, @@ -36,7 +37,9 @@ public class PFN_vkDebugUtilsMessengerCallbackEXT implements Memory.Addressable } return 0; }; - return Memory.upcall(trampline, "call", + return Memory.upcall( + MethodHandles.lookup(), + trampline, "call", "(IILjdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)I", FunctionDescriptor.of( Memory.INT, diff --git a/test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java b/src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java similarity index 86% rename from test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java rename to src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java index 48bd7e6..f3f596e 100644 --- a/test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java +++ b/src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java @@ -1,7 +1,10 @@ -package zvk; +package vulkan; + import jdk.incubator.foreign.*; import java.lang.invoke.*; -public final class VkPhysicalDeviceGroupProperties implements Memory.Addressable { +import au.notzed.nativez.*; + +public final class VkPhysicalDeviceGroupProperties implements Pointer { final MemorySegment segment; final DispatchInstance instanceDispatch; @@ -11,6 +14,7 @@ public final class VkPhysicalDeviceGroupProperties implements Memory.Addressable segment.set(Memory.INT, 0, VkStructureType.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES); } public final MemoryAddress address() { return segment.address(); } + public final ResourceScope scope() { return segment.scope(); } /* public static VkPhysicalDeviceGroupProperties create(Frame frame) { return new VkPhysicalDeviceGroupProperties(frame.allocate(LAYOUT)); @@ -40,15 +44,15 @@ public final class VkPhysicalDeviceGroupProperties implements Memory.Addressable return (int)physicalDeviceCount$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize())); } // len= altLen= - Memory.HandleArray getPhysicalDevices() { + HandleArray getPhysicalDevices() { MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement("physicalDevices"); MemorySegment seg = segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize()); - return new Memory.HandleArray((addr)->new VkPhysicalDevice(addr, instanceDispatch), seg); + return HandleArray.create(seg, (addr, scope)-> VkPhysicalDevice.create(addr, instanceDispatch)); } - Memory.HandleArray getPhysicalDevices(long index) { + HandleArray getPhysicalDevices(long index) { MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement("physicalDevices"); MemorySegment seg = segment.asSlice(index * LAYOUT.byteSize() + LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize()); - return new Memory.HandleArray((addr)->new VkPhysicalDevice(addr, instanceDispatch), seg); + return HandleArray.create(seg, (addr, scope)-> VkPhysicalDevice.create(addr, instanceDispatch)); } // len= altLen= int getSubsetAllocation() { diff --git a/test-vulkan/src/zvk/test/TestVulkan.java b/src/notzed.vkregistry/classes/vulkan/test/TestVulkan.java similarity index 76% rename from test-vulkan/src/zvk/test/TestVulkan.java rename to src/notzed.vkregistry/classes/vulkan/test/TestVulkan.java index cafef8c..be3f524 100644 --- a/test-vulkan/src/zvk/test/TestVulkan.java +++ b/src/notzed.vkregistry/classes/vulkan/test/TestVulkan.java @@ -31,7 +31,7 @@ THE SOFTWARE. * It's been simplified a bit and converted to the 'zvk' api. */ -package zvk.test; +package vulkan.test; import java.io.InputStream; import java.io.FileOutputStream; @@ -40,25 +40,41 @@ import java.nio.channels.Channels; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.image.MemoryImageSource; +import javax.swing.AbstractAction; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + +import java.lang.ref.WeakReference; + import java.lang.invoke.*; import jdk.incubator.foreign.*; import jdk.incubator.foreign.MemoryLayout.PathElement; +import au.notzed.nativez.*; -import zvk.*; +import vulkan.*; -import static zvk.VkBufferUsageFlagBits.*; -import static zvk.VkMemoryPropertyFlagBits.*; -import static zvk.VkSharingMode.*; -import static zvk.VkDescriptorType.*; -import static zvk.VkShaderStageFlagBits.*; -import static zvk.VkCommandBufferLevel.*; -import static zvk.VkCommandBufferUsageFlagBits.*; -import static zvk.VkPipelineBindPoint.*; +import static vulkan.VkBufferUsageFlagBits.*; +import static vulkan.VkMemoryPropertyFlagBits.*; +import static vulkan.VkSharingMode.*; +import static vulkan.VkDescriptorType.*; +import static vulkan.VkShaderStageFlagBits.*; +import static vulkan.VkCommandBufferLevel.*; +import static vulkan.VkCommandBufferUsageFlagBits.*; +import static vulkan.VkPipelineBindPoint.*; -import static zvk.VkDebugUtilsMessageSeverityFlagBitsEXT.*; -import static zvk.VkDebugUtilsMessageTypeFlagBitsEXT.*; +import static vulkan.VkDebugUtilsMessageSeverityFlagBitsEXT.*; +import static vulkan.VkDebugUtilsMessageTypeFlagBitsEXT.*; public class TestVulkan { + static final boolean debug = true; ResourceScope scope = ResourceScope.newSharedScope(); int WIDTH = 1920*1; @@ -70,34 +86,36 @@ public class TestVulkan { VkDevice device; VkQueue computeQueue; - long dstBufferSize = WIDTH * HEIGHT * 4 * 4; + long dstBufferSize = WIDTH * HEIGHT * 4; //VkBuffer dstBuffer; //VkDeviceMemory dstMemory; BufferMemory dst; VkDescriptorSetLayout descriptorSetLayout; VkDescriptorPool descriptorPool; - Memory.HandleArray descriptorSets = VkDescriptorSet.createArray(scope, 1); + HandleArray descriptorSets = VkDescriptorSet.createArray(1, (SegmentAllocator)scope); int computeQueueIndex; VkPhysicalDeviceMemoryProperties deviceMemoryProperties; String mandelbrot_entry = "main"; - Memory.IntArray mandelbrot_cs; + IntArray mandelbrot_cs; VkShaderModule mandelbrotShader; VkPipelineLayout pipelineLayout; - Memory.HandleArray computePipeline = VkPipeline.createArray(scope, 1); + HandleArray computePipeline = VkPipeline.createArray(1, (SegmentAllocator)scope); VkCommandPool commandPool; - Memory.HandleArray commandBuffers; + HandleArray commandBuffers; record BufferMemory ( VkBuffer buffer, VkDeviceMemory memory ) {}; VkDebugUtilsMessengerEXT logger; void init_debug() throws Exception { - try (Frame frame = Memory.createFrame()) { + if (!debug) + return; + try (Frame frame = Frame.frame()) { NativeSymbol cb = PFN_vkDebugUtilsMessengerCallbackEXT.of((severity, flags, data) -> { System.out.printf("Debug: %d: %s\n", severity, data.getMessage()); return 0; @@ -119,12 +137,12 @@ public class TestVulkan { } void init_instance() throws Exception { - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { VkInstanceCreateInfo info = VkInstanceCreateInfo.create(frame, 0, VkApplicationInfo.create(frame, "test", 1, "test-engine", 2, VK_MAKE_API_VERSION(0, 1, 0, 0)), new String[] { "VK_LAYER_KHRONOS_validation" }, - null //new String[] { "VK_EXT_debug_utils" } + debug ? new String[] { "VK_EXT_debug_utils" } : null ); instance = VkInstance.vkCreateInstance(info, null); @@ -132,9 +150,9 @@ public class TestVulkan { } void init_device() throws Exception { - try (Frame frame = Memory.createFrame()) { - Memory.IntArray count$h = new Memory.IntArray(frame, 1); - Memory.HandleArray devs; + try (Frame frame = Frame.frame()) { + IntArray count$h = IntArray.create(frame, 1); + HandleArray devs; int count; int res; @@ -177,7 +195,7 @@ public class TestVulkan { computeQueueIndex = queueid; physicalDevice = devs.getAtIndex(devid); - Memory.FloatArray qpri = new Memory.FloatArray(frame, 0.0f); + FloatArray qpri = FloatArray.create(frame, 0.0f); VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create( frame, 0, @@ -213,7 +231,7 @@ public class TestVulkan { * */ BufferMemory init_buffer(long dataSize, int usage, int properties) throws Exception { - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { VkMemoryRequirements req = VkMemoryRequirements.create(frame); VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(frame, 0, @@ -248,7 +266,7 @@ public class TestVulkan { * The descriptors describe individually-addressable blocks. */ void init_descriptor() throws Exception { - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { /* Create descriptorset layout */ VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(frame, 0, @@ -278,7 +296,7 @@ public class TestVulkan { descriptorPool = device.vkCreateDescriptorPool(descriptor_pool, null); /* Allocate from pool */ - Memory.HandleArray layout_table = VkDescriptorSetLayout.createArray(frame, 1); + HandleArray layout_table = VkDescriptorSetLayout.createArray(1, frame); layout_table.setAtIndex(0, descriptorSetLayout); @@ -313,7 +331,7 @@ public class TestVulkan { * Create the compute pipeline. This is the shader and data layouts for it. */ void init_pipeline() throws Exception { - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { /* Set shader code */ VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(frame, 0, @@ -323,7 +341,7 @@ public class TestVulkan { mandelbrotShader = device.vkCreateShaderModule(vsInfo, null); /* Link shader to layout */ - Memory.HandleArray layout_table = VkDescriptorSetLayout.createArray(frame, 1); + HandleArray layout_table = VkDescriptorSetLayout.createArray(1, frame); layout_table.setAtIndex(0, descriptorSetLayout); @@ -352,11 +370,12 @@ public class TestVulkan { device.vkCreateComputePipelines(null, 1, pipeline, null, computePipeline); } } + /** * Create a command buffer, this is somewhat like a display list. */ void init_command_buffer() throws Exception { - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(frame, 0, computeQueueIndex); @@ -395,7 +414,7 @@ public class TestVulkan { * A fence is used to wait for completion. */ void execute() throws Exception { - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { VkSubmitInfo submitInfo = VkSubmitInfo.create(frame); submitInfo.setCommandBufferCount(0, 1); @@ -403,7 +422,7 @@ public class TestVulkan { /* Create fence to mark the task completion */ VkFence fence; - Memory.HandleArray fences = VkFence.createArray(frame, 1); + HandleArray fences = VkFence.createArray(1, frame); VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame); // maybe this should take a HandleArray rather than being a constructor @@ -436,6 +455,8 @@ public class TestVulkan { device.vkDestroyBuffer(dst.buffer(), null); device.vkDestroyDevice(null); + if (logger != null) + instance.vkDestroyDebugUtilsMessengerEXT(logger, null); instance.vkDestroyInstance(null); } @@ -447,11 +468,12 @@ public class TestVulkan { MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope); byte[] pixels = new byte[WIDTH * HEIGHT * 3]; - // this is super-slow! + System.out.printf("map %d bytes\n", dstBufferSize); + for (int i = 0; i < WIDTH * HEIGHT; i++) { - pixels[i * 3 + 0] = (byte)(255.0f * mem.getAtIndex(Memory.FLOAT, i * 4 + 0)); - pixels[i * 3 + 1] = (byte)(255.0f * mem.getAtIndex(Memory.FLOAT, i * 4 + 1)); - pixels[i * 3 + 2] = (byte)(255.0f * mem.getAtIndex(Memory.FLOAT, i * 4 + 2)); + pixels[i * 3 + 0] = mem.get(Memory.BYTE, i * 4 + 0); + pixels[i * 3 + 1] = mem.get(Memory.BYTE, i * 4 + 1); + pixels[i * 3 + 2] = mem.get(Memory.BYTE, i * 4 + 2); } device.vkUnmapMemory(dst.memory()); @@ -460,6 +482,20 @@ public class TestVulkan { } } + void show_result() throws Exception { + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope); + int[] pixels = new int[WIDTH * HEIGHT]; + + System.out.printf("map %d bytes\n", dstBufferSize); + + MemorySegment.ofArray(pixels).copyFrom(mem); + + device.vkUnmapMemory(dst.memory()); + + swing_show(WIDTH, HEIGHT, pixels); + } + } /** * Trivial pnm format image output. @@ -472,7 +508,43 @@ public class TestVulkan { } } - static Memory.IntArray loadSPIRV(String name) throws IOException { + 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, int[] pixels) { + this.w = w; + this.h = h; + this.stride = w; + this.pixels = pixels; + this.source = new MemoryImageSource(w, h, pixels, 0, w); + 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); + } + } + + void swing_show(int w, int h, int[] pixels) { + JFrame window; + DataImage image = new DataImage(w, h, pixels); + + window = new JFrame("mandelbrot"); + window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + window.setContentPane(image); + window.setSize(w, h); + window.setVisible(true); + } + + IntArray loadSPIRV0(String name) throws IOException { // hmm any way to just load this directly? try (InputStream is = TestVulkan.class.getResourceAsStream(name)) { ByteBuffer bb = ByteBuffer.allocateDirect(8192).order(ByteOrder.nativeOrder()); @@ -480,7 +552,17 @@ public class TestVulkan { bb.position(0); bb.limit(length); - return new Memory.IntArray(MemorySegment.ofByteBuffer(bb)); + + return IntArray.create(MemorySegment.ofByteBuffer(bb)); + } + } + + IntArray loadSPIRV(String name) throws IOException { + try (InputStream is = TestVulkan.class.getResourceAsStream(name)) { + MemorySegment seg = ((SegmentAllocator)scope).allocateArray(Memory.INT, 2048); + int length = Channels.newChannel(is).read(seg.asByteBuffer()); + + return IntArray.create(seg.asSlice(0, length)); } } @@ -498,14 +580,14 @@ public class TestVulkan { } public static int VK_MAKE_API_VERSION(int variant, int major, int minor, int patch) { - return (variant << 29) | (major << 22) | (minor << 12) | patch; - } + return (variant << 29) | (major << 22) | (minor << 12) | patch; + } void demo() throws Exception { mandelbrot_cs = loadSPIRV("mandelbrot.bin"); init_instance(); - //init_debug(); + init_debug(); init_device(); dst = init_buffer(dstBufferSize, @@ -513,13 +595,16 @@ public class TestVulkan { VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); init_descriptor(); + init_pipeline(); init_command_buffer(); System.out.printf("Calculating %dx%d\n", WIDTH, HEIGHT); execute(); - System.out.println("Saving ..."); - save_result(); + //System.out.println("Saving ..."); + //save_result(); + System.out.println("Showing ..."); + show_result(); System.out.println("Done."); shutdown(); diff --git a/test-vulkan/template/VkDevice-part.java b/src/notzed.vkregistry/gen/VkDevice-part.java similarity index 82% rename from test-vulkan/template/VkDevice-part.java rename to src/notzed.vkregistry/gen/VkDevice-part.java index 0de01d2..7b6d5e0 100644 --- a/test-vulkan/template/VkDevice-part.java +++ b/src/notzed.vkregistry/gen/VkDevice-part.java @@ -14,12 +14,12 @@ ResourceScope deviceScope = ResourceScope.newSharedScope(); * Success Codes: VK_SUCCESS * Error Codes: VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY */ - public Memory.HandleArray vkAllocateCommandBuffers(VkCommandBufferAllocateInfo pAllocateInfo)throws Exception { + public HandleArray vkAllocateCommandBuffers(VkCommandBufferAllocateInfo pAllocateInfo)throws Exception { int res$code; try { - Memory.HandleArray pCommandBuffers = - new Memory.HandleArray<>((addr)->new VkCommandBuffer(addr, instanceDispatch, deviceDispatch), - MemorySegment.allocateNative(pAllocateInfo.getCommandBufferCount() * Memory.POINTER.byteSize(), Memory.POINTER.bitAlignment(), deviceScope)); + HandleArray pCommandBuffers = + HandleArray.createArray(pAllocateInfo.getCommandBufferCount(), (SegmentAllocator)Memory.sharedScope(), + (addr, scope)-> VkCommandBuffer.create(addr, instanceDispatch, deviceDispatch)); res$code = (int)vkAllocateCommandBuffers$FH.invokeExact( (Addressable)address(), @@ -47,7 +47,7 @@ ResourceScope deviceScope = ResourceScope.newSharedScope(); */ public MemorySegment vkMapMemory(VkDeviceMemory memory, long offset, long size, int flags, ResourceScope scope)throws Exception { int res$code; - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { MemorySegment ppData = frame.allocatePointer(); res$code = (int)vkMapMemory$FH.invokeExact( (Addressable)address(), diff --git a/test-vulkan/template/VkInstance-part.java b/src/notzed.vkregistry/gen/VkInstance-part.java similarity index 88% rename from test-vulkan/template/VkInstance-part.java rename to src/notzed.vkregistry/gen/VkInstance-part.java index dcd50e6..b2de68a 100644 --- a/test-vulkan/template/VkInstance-part.java +++ b/src/notzed.vkregistry/gen/VkInstance-part.java @@ -12,9 +12,9 @@ ResourceScope instanceScope = ResourceScope.newSharedScope(); /** * VkResult vkEnumeratePhysicalDevices ( VkInstance uint32_t* VkPhysicalDevice* ) */ - public Memory.HandleArray vkEnumeratePhysicalDevices()throws Exception { + public HandleArray vkEnumeratePhysicalDevices()throws Exception { int res$code; - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { MemorySegment count = frame.allocateInt(); res$code = (int)vkEnumeratePhysicalDevices$FH.invokeExact( @@ -23,9 +23,10 @@ ResourceScope instanceScope = ResourceScope.newSharedScope(); (Addressable)MemoryAddress.NULL); if (res$code == VkResult.VK_SUCCESS) { - Memory.HandleArray devices = - new Memory.HandleArray<>((addr)->new VkPhysicalDevice(addr, instanceDispatch), - MemorySegment.allocateNative(count.get(Memory.INT, 0) * Memory.POINTER.byteSize(), Memory.POINTER.bitAlignment(), instanceScope)); + HandleArray devices = + HandleArray.create( + MemorySegment.allocateNative(count.get(Memory.INT, 0) * Memory.POINTER.byteSize(), Memory.POINTER.bitAlignment(), instanceScope), + (addr, scope)-> VkPhysicalDevice.create(addr, instanceDispatch)); res$code = (int)vkEnumeratePhysicalDevices$FH.invokeExact( (Addressable)address(), @@ -51,7 +52,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope(); */ public VkPhysicalDeviceGroupProperties vkEnumeratePhysicalDeviceGroups() throws Exception { int res$code; - try (Frame frame = Memory.createFrame()) { + try (Frame frame = Frame.frame()) { MemorySegment count = frame.allocateInt(); res$code = (int)vkEnumeratePhysicalDeviceGroups$FH.invokeExact( diff --git a/src/notzed.vkregistry/gen/export-registry b/src/notzed.vkregistry/gen/export-registry new file mode 100755 index 0000000..4b45b14 --- /dev/null +++ b/src/notzed.vkregistry/gen/export-registry @@ -0,0 +1,425 @@ +#!/usr/bin/perl + +# convert vulkan registry into nativez perl format description +# ?? + +use strict; + +use Data::Dumper; +use File::Path qw(make_path); + +use XML::Parser; + +$Data::Dumper::Indent = 1; + +my $xml = XML::Parser->new(Style => 'Objects'); +my $registry = $xml->parsefile('/usr/share/vulkan/registry/vk.xml')->[0]; + +# get something of the form +# ... xxx ... xxx +sub scanMember { + my $n = shift @_; + my $baseType = ""; + my $fullType = ""; + my $name = ""; + + # enum is for array sizes + foreach my $p (@{$n->{Kids}}) { + if ($p->isa('Characters')) { + $fullType .= $p->{Text}; + } elsif ($p->isa('type')) { + $baseType = @{$p->{Kids}}[0]->{Text}; + $fullType .= $baseType; + } elsif ($p->isa('name')) { + $name = @{$p->{Kids}}[0]->{Text}; + } elsif ($p->isa('enum')) { + $fullType .= @{$p->{Kids}}[0]->{Text}; + } + } + + $fullType =~ s/^\s+|\s+$//g; + + my $member = { + name => $name, + baseType => $baseType, + fullType => $fullType + }; + + $member->{len} = $n->{len} if (defined $n->{len}); + $member->{altlen} = $n->{altlen} if (defined $n->{altlen}); + $member->{optional} = $n->{optional} if (defined $n->{optional}); + $member->{values} = $n->{values} if (defined $n->{values}); + + return $member; +} + +my %data = ( + 'VisualID' => { category => 'basetype', name => 'VisualID', type => 'uint64_t' }, + 'Window' => { category => 'basetype', name => 'Window', type => 'uint64_t' }, + 'xcb_visualid_t' => { category => 'basetype', name => 'xcb_visualid_t', type => 'uint32_t' }, + 'xcb_window_t' => { category => 'basetype', name => 'xcb_window_t', type => 'uint32_t' }, + 'HANDLE' => { category => 'basetype', name => 'HANDLE', type => 'VK_DEFINE_NON_DISPATCHABLE_HANDLE' }, + 'RROutput' => { category => 'basetype', name => 'RROutput', type => 'uint32_t' }, + 'zx_handle_t' => { category => 'basetype', name => 'zx_handle_t', type => 'uint64_t' }, + ); + +my %apiConstants = (); +my %alias = (); +my %commands = (); +my @commandsList = (); +my @features = (); +my @extensions = (); +my @bitmaskTypes = (); + +foreach my $x (grep { $_->isa('types') } @{$registry->{Kids}}) { + foreach my $t (grep { $_->isa('type') } @{$x->{Kids}}) { + if (!defined($t->{alias})) { + my $category = $t->{category}; + + if ($category eq 'struct' || $category eq 'union') { + my @members = (); + + foreach my $m (grep { $_->isa('member') } @{$t->{Kids}}) { + push @members,scanMember($m); + } + + my %struct = ( + category => $category, + name => $t->{name}, + members => \@members, + bitAlignment => 64 + ); + + $data{$struct{name}} = \%struct; + } elsif ($category eq "handle") { + my $info = scanMember($t); + my %struct = ( + category => $category, + name => $info->{name}, + parent => $t->{parent}, + objtypeenum => $t->{objtypeenum}, + type => $info->{baseType}, + + bitSize => 64, + bitAlignment => 64 + ); + + $data{$struct{name}} = \%struct; + } elsif ($category eq "bitmask") { + # these map enums to the basic types but we can't use it yet, save for later + my %struct = (); + my $info = scanMember($t); + + $struct{category} = "enum:bitmask"; + $struct{name} = $info->{name}; + $struct{type} = $info->{baseType}; + # fuck knows what the difference is + $struct{requires} = $t->{requires} if (defined $t->{requires}); + $struct{bitvalues} = $t->{bitvalues} if (defined $t->{bitvalues}); + + push @bitmaskTypes, \%struct; + # added to %data later + } elsif ($category eq "basetype") { + my %struct = (); + my $info = scanMember($t); + + if ($info->{baseType}) { + $struct{category} = $category; + $struct{name} = $info->{name}; + $struct{type} = $info->{baseType}; + + # set holders here? + # TODO: fuck this off i think, it's not useful enough? + if ($info->{baseType} eq "uint32_t") { + $struct{bitSize} = 32; + $struct{bitAlignment} = 32; + } elsif ($info->{baseType} eq "uint64_t") { + $struct{bitSize} = 64; + $struct{bitAlignment} = 64; + } elsif ($info->{fullType} eq 'typedef void* ;') { + $struct{bitSize} = 64; + $struct{bitAlignment} = 64; + } else { + print "$info->{name} '$info->{baseType}' '$info->{fullType}'\n"; + die(); + } + + print "basetype=".Dumper(\%struct); + $data{$struct{name}} = \%struct; + } + } elsif ($category eq 'funcpointer') { + # typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( + # void* pUserData, + # size_t size, + # VkInternalAllocationType allocationType, + # VkSystemAllocationScope allocationScope); + my %struct = (); + my $fullType = ""; + my @paramTypes = (); + + foreach my $p (@{$t->{Kids}}) { + if ($p->isa('Characters')) { + $fullType .= $p->{Text}; + } elsif ($p->isa('type')) { + push @paramTypes, @{$p->{Kids}}[0]->{Text}; + $fullType .= @{$p->{Kids}}[0]->{Text}; + } elsif ($p->isa('name')) { + $struct{name} = @{$p->{Kids}}[0]->{Text}; + $fullType .= @{$p->{Kids}}[0]->{Text}; + } + } + $fullType =~ s/^\s+|\s+$//g; + $fullType =~ s/\n|VKAPI_PTR//g; + $fullType =~ s/ +/ /g; + $fullType =~ s/\( +/\(/g; + + $struct{prototype} = $fullType; + $data{$struct{name}} = \%struct; + + %struct = (); + $struct{prototype} = $fullType; + $fullType =~ m/typedef (.*) \(\*(.*)\)\((.*)\)/; + $struct{result} = $1; + $struct{name} = $2; + $struct{args} = $3; + foreach my $arg (split /,/,$struct{args}) { + $arg =~ m/^([^\*]+)(\*)? (.+)$/; + push @{$struct{params}}, { name => $3, fullType => $1.$2, baseType=>$1 }; + } + #print Dumper(\%struct); + } + } else { + $alias{$t->{name}} = $t->{alias}; + } + } +} + +foreach my $x (grep { $_->isa('enums') } @{$registry->{Kids}}) { + if ($x->{type} eq "enum") { + my @members = (); + foreach my $t (grep { $_->isa('enum') } @{$x->{Kids}}) { + my %info = ( + name => $t->{name} + ); + $info{value} = $t->{value} if (defined($t->{value})); + $info{comment} = $t->{comment} if (defined($t->{comment})); + $info{alias} = $t->{alias} if (defined($t->{alias})); + push @members, \%info; + } + my %enum = ( + category => 'enum', + name => $x->{name}, + members => \@members + ); + $data{$enum{name}} = \%enum; + } elsif ($x->{type} eq 'bitmask') { + my @members = (); + foreach my $t (grep { $_->isa('enum') } @{$x->{Kids}}) { + my %info = ( name => $t->{name} ); + + # FIXME: handle alias + $info{alias} = $t->{alias} if (defined($t->{alias})); + $info{comment} = $t->{comment} if (defined($t->{comment})); + $info{value} = $t->{value} if (defined($t->{value})); + $info{value} = "".(1<<$t->{bitpos}) if (defined($t->{bitpos})); + + push @members, \%info; + } + my %enum = ( + category => "enum:bitmask", + name => $x->{name}, + members => \@members + ); + $data{$enum{name}} = \%enum; + } else { + #defines here + foreach my $t (grep { $_->isa('enum') } @{$x->{Kids}}) { + if (!defined($t->{alias})) { + my %enum = ( + category => 'enum:define', + name => $t->{name}, + value => $t->{value}, + type => $t->{type} + ); + $enum{comment} = $t->{comment} if (defined($t->{comment})); + $data{$enum{name}} = \%enum; + $apiConstants{$enum{name}} = \%enum; + } else { + $alias{$t->{name}} = $t->{alias}; + } + } + } +} + +# fix up bitmask type bases +foreach my $x (@bitmaskTypes) { + if (defined $x->{requires}) { + if (defined $data{$x->{requires}}) { + my $struct = $data{$x->{requires}}; + $struct->{type} = $x->{type}; + } else { + print "unknown bitmask enum requires $x->{requires}\n"; + } + # somehow redirect flags to requires? + $alias{$x->{name}} = $x->{requires}; + } elsif (defined $x->{bitvalues}) { + if (defined $data{$x->{bitvalues}}) { + my $struct = $data{$x->{bitvalues}}; + $struct->{type} = $x->{type}; + } else { + print "unknown bitmask enum bitvalues $x->{bitvalues}\n"; + } + # somehow redirect flags to requires? + $alias{$x->{name}} = $x->{bitvalues}; + } elsif (defined $data{$x->{name}}) { + my $struct = $data{$x->{name}}; + $struct->{type} = $x->{type}; + } else { + # these are referenced but don't have any definitions + my %enum = ( + category => $x->{category}, + name => $x->{name}, + members => [], + type => $x->{type} + ); + $data{$enum{name}} = \%enum; + } +} + +#print Dumper(\@bitmaskTypes); +#print Dumper($data{VkExtent3D}); + +my $s = $data{VkBufferMemoryBarrier}; + +my $typeMap = { + 'uint32_t' => { alias => 'u32', size => 32, alignment => 32, }, + 'int32_t' => { alias => 'i32', size => 32, alignment => 32, }, + 'uint64_t' => { alias => 'u64', size => 64, alignment => 64, }, + 'size_t' => { alias => 'u64', size => 64, alignment => 64, }, + 'float' => { alias => 'f32', size => 32, alignment => 32, }, + 'double' => { alias => 'f64', size => 64, alignment => 64, }, + 'const void*' => { alias => 'void', size => 64, alignment => 64, }, +}; + +#print Dumper($s); + +sub findBaseType { + my $name = shift; + my $t; + + return $name if $typeMap->{$name}; + + while (defined($alias{$name})) { + $name = $alias{$name}; + } + + if ($name eq 'const void*') { + return "const void*"; + } + + while (defined($data{$name})) { + print "lookup $name\n"; + my $t = $data{$name}; + + if (defined $t->{type}) { + $name = $t->{type}; + } elsif ($t->{category} eq "enum" || $t->{category} eq "enum:bitmask") { + return 'int32_t'; + } + } + + if ($name eq 'VK_DEFINE_NON_DISPATCHABLE_HANDLE') { + return 'const void*'; + } + + return $name; + +# print Dumper($t); + +# if (defined $t->{type}) { +# $name = $t->{type}; +# } elsif ($t->{category} eq "enum" || $t->{category} eq "enum:bitmask") { +# return 'int32_t'; +# } +} + +my $d = { + name => $s->{name}, + type => $s->{category}, + size => 0, + fields => [], +}; +{ + + use integer; + my $offset = 0; + my $alignment = 8; + foreach my $m (@{$s->{members}}) { + my $name = findBaseType($m->{fullType}); + + print " $m->{fullType} -> $name\n"; + my $t = $typeMap->{$name}; + + # TODO: flags + if (defined($t)) { + $offset = ($offset + $t->{alignment} - 1) & ~($t->{alignment} - 1); + push @{$d->{fields}}, { + name => $m->{name}, + size => $t->{size}, + offset => $offset, + type => $t->{alias}, + ctype => $m->{fullType}, + }; + $offset += $t->{size}; + $alignment = $t->{alignment} if $t->{alignment} > $alignment; + } elsif ($m->{fullType} =~ m/\*/) { + die; + } else { + die; + } + } + $d->{size} = ($offset + $alignment - 1) & ~($alignment-1); +} + +print Dumper($d); + +# 'struct:VkExtent3D' => { name => 'VkExtent3D', type => 'struct', size => 96, fields => [ +# { name => 'width', size => 32, offset => 0, ctype => 'unsigned int', type => 'u32',}, +# { name => 'height', size => 32, offset => 32, ctype => 'unsigned int', type => 'u32',}, +# { name => 'depth', size => 32, offset => 64, ctype => 'unsigned int', type => 'u32',}, +# ]}, + +# $VAR1 = { +# 'members' => [ +# { +# 'fullType' => 'uint32_t', +# 'name' => 'width', +# 'baseType' => 'uint32_t' +# }, +# { +# 'fullType' => 'uint32_t', +# 'name' => 'height', +# 'baseType' => 'uint32_t' +# }, +# { +# 'fullType' => 'uint32_t', +# 'name' => 'depth', +# 'baseType' => 'uint32_t' +# } +# ], +# 'bitAlignment' => 64, +# 'name' => 'VkExtent3D', +# 'category' => 'struct' +# }; + +# 'struct:VkBufferMemoryBarrier' => { name => 'VkBufferMemoryBarrier', type => 'struct', size => 448, fields => [ +# { name => 'sType', size => 32, offset => 0, type => 'u32', ctype => 'enum VkStructureType',}, +# { name => 'pNext', size => 64, offset => 64, deref => 'u64:v', type => 'void', ctype => 'void',}, +# { name => 'srcAccessMask', size => 32, offset => 128, ctype => 'unsigned int', type => 'u32',}, +# { name => 'dstAccessMask', size => 32, offset => 160, ctype => 'unsigned int', type => 'u32',}, +# { name => 'srcQueueFamilyIndex', size => 32, offset => 192, ctype => 'unsigned int', type => 'u32',}, +# { name => 'dstQueueFamilyIndex', size => 32, offset => 224, ctype => 'unsigned int', type => 'u32',}, +# { name => 'buffer', size => 64, offset => 256, deref => 'u64:${VkBuffer_T}', type => 'struct:VkBuffer_T',}, +# { name => 'offset', size => 64, offset => 320, ctype => 'long unsigned int', type => 'u64',}, +# { name => 'size', size => 64, offset => 384, ctype => 'long unsigned int', type => 'u64',}, +# ]}, diff --git a/test-vulkan/generate-vulkan b/src/notzed.vkregistry/gen/export-vulkan similarity index 96% rename from test-vulkan/generate-vulkan rename to src/notzed.vkregistry/gen/export-vulkan index 7409ca4..375dff2 100755 --- a/test-vulkan/generate-vulkan +++ b/src/notzed.vkregistry/gen/export-vulkan @@ -10,12 +10,12 @@ use Data::Dumper; use File::Path qw(make_path); +use FindBin; -require XML::Parser; +use XML::Parser; -# these can't really be changed yet -$targetDirectory = "api"; -$targetPackage = "zvk"; +$targetDirectory = "bin/gen/notzed.vkregistry/classes"; +$targetPackage = "vulkan"; while (@ARGV) { my $cmd = shift(@ARGV); @@ -260,7 +260,7 @@ foreach $x (grep { $_->isa('types') } @{$registry->{Kids}}) { # VkSystemAllocationScope allocationScope); my %struct = (); my $fullType = ""; - my @oaramTypes = (); + my @paramTypes = (); foreach $p (@{$t->{Kids}}) { if ($p->isa('Characters')) { @@ -743,11 +743,11 @@ sub functionJavaType { } # alias? - return 'Memory.'.ucfirst($typeToJavaPrimitive{$baseType}).'Array' if (defined $typeToJavaPrimitive{$baseType}); + return ucfirst($typeToJavaPrimitive{$baseType}).'Array' if (defined $typeToJavaPrimitive{$baseType}); # handles and some hacks if ($baseType eq "void") { - return "Memory.PointerArray"; + return "PointerArray"; #return "void"; } elsif ($baseType =~ m/^PFN_/) { return "MemoryAddress"; @@ -755,17 +755,17 @@ sub functionJavaType { } elsif ($baseType eq "VK_DEFINE_NON_DISPATCHABLE_HANDLE") { # typed array? # We actually just want the same type since it's also an array if required - return "Memory.HandleArray<$p->{baseType}>"; + return "HandleArray<$p->{baseType}>"; #return $type->{name}; } elsif ($baseType eq "VK_DEFINE_HANDLE") { # could be more typed - return "Memory.HandleArray<$p->{baseType}>"; + return "HandleArray<$p->{baseType}>"; #return "Memory.PointerArray"; #return $type->{name}; } elsif ($baseType eq "VkFlags") { - return 'Memory.IntArray'; + return 'IntArray'; } elsif ($baseType eq "VkFlags64") { - return 'Memory.LongArray'; + return 'LongArray'; } if (defined($alias{$baseType})) { @@ -1129,7 +1129,7 @@ sub exportFunction { # TODO: only create frame if required? print $f "\t\t$jrtype res\$code;\n" if ($jrtype ne 'void'); - print $f "\t\ttry (Frame frame = Memory.createFrame()) {\n"; + print $f "\t\ttry (Frame frame = Frame.frame()) {\n"; # setup foreach $param (@params[($static ? 0 : 1) .. $#params]) { @@ -1161,7 +1161,7 @@ sub exportFunction { if ($tname eq 'String') { print $f "\t\t\t\t(Addressable)Memory.address($param->{name}\$h)"; - } elsif ($tname =~ m/Memory\..*Array/) { + } elsif ($tname =~ m/Array$|^HandleArray/) { print $f "\t\t\t\t(Addressable)Memory.address($param->{name})"; } elsif ($data{$tname}) { print $f "\t\t\t\t(Addressable)Memory.address($param->{name})"; @@ -1225,7 +1225,7 @@ sub exportCreateFunction { print $f " {\n"; print $f "\t\t$jrtype res\$code;\n" if ($jrtype ne 'void'); - print $f "\t\ttry (Frame frame = Memory.createFrame()) {\n"; + print $f "\t\ttry (Frame frame = Frame.frame()) {\n"; # setup # CHANGE: return holder @@ -1263,7 +1263,7 @@ sub exportCreateFunction { if ($tname eq 'String') { print $f "\t\t\t\t(Addressable)Memory.address($param->{name}\$h)"; - } elsif ($tname =~ m/Memory\..*Array/) { + } elsif ($tname =~ m/Array$|^HandleArray/) { print $f "\t\t\t\t(Addressable)Memory.address($param->{name})"; } elsif ($data{$tname}) { print $f "\t\t\t\t(Addressable)Memory.address($param->{name})"; @@ -1725,8 +1725,8 @@ foreach $x (sort keys %dump) { open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java"); # look for complete override - if (open(my $template, "<", "template/$s->{name}.java")) { - print $f "// << inserted from: 'template/$s->{name}.java'\n"; + if (open(my $template, "<", "$FindBin::Bin/$s->{name}.java")) { + print $f "// << inserted from: '$FindBin::Bin/$s->{name}.java'\n"; while (<$template>) { print $f $_; } @@ -1737,8 +1737,9 @@ foreach $x (sort keys %dump) { print $f "package $targetPackage;\n"; print $f "import jdk.incubator.foreign.*;\n"; print $f "import java.lang.invoke.*;\n"; + print $f "import au.notzed.nativez.*;\n"; - print $f "public final class $s->{name} implements Memory.Addressable {\n"; + print $f "public final class $s->{name} implements Pointer {\n"; print $f "\tfinal MemorySegment segment;\n"; @@ -1750,14 +1751,15 @@ foreach $x (sort keys %dump) { print $f "\t}\n"; print $f "\tpublic final MemoryAddress address() { return segment.address(); }\n"; + print $f "\tpublic final ResourceScope scope() { return segment.scope(); }\n"; # basic factory methods - print $f "\tpublic static $s->{name} create(Frame frame) {\n"; + print $f "\tpublic static $s->{name} create(SegmentAllocator frame) {\n"; print $f "\t\treturn new $s->{name}(frame.allocate(LAYOUT));\n"; print $f "\t}\n\n"; if (!$isTyped) { - print $f "\tpublic static $s->{name} createArray(Frame frame, long count) {\n"; + print $f "\tpublic static $s->{name} createArray(SegmentAllocator frame, long count) {\n"; print $f "\t\treturn new $s->{name}(frame.allocateArray(LAYOUT, count));\n"; print $f "\t}\n\n"; } @@ -1864,7 +1866,7 @@ foreach $x (sort keys %dump) { for (my $i = 0;$i<$m->{shortPrimitiveArrayLength};$i++) { print $f "$m->{name}\$array.setAtIndex($i, $m->{name}\$$i);\n"; } - } elsif ($jtype =~ m/$Memory.*Array/) { + } elsif ($jtype =~ m/Array$|^HandleArray/) { print $f "\t\t$m->{name}\$VH.set(self.segment, Memory.address($m->{name}));\n"; } elsif ($jtype =~ m/^MemoryAddress|MemorySegment$/) { # or maybe not, force the caller to use MemoryAddress.NULL? @@ -1885,7 +1887,7 @@ foreach $x (sort keys %dump) { next if ($m->{fullType} =~ m/\[.*\]/ && !$m->{shortPrimitiveArrayLength}); print $f "\tpublic static $s->{name} create".ucfirst($m->{name})."(\n"; - print $f "\t\tFrame frame"; + print $f "\t\tSegmentAllocator frame"; my $jtype = functionJavaType($m); if ($m->{shortPrimitiveArrayLength}) { @@ -1914,7 +1916,7 @@ foreach $x (sort keys %dump) { for (my $i = 0;$i<$m->{shortPrimitiveArrayLength};$i++) { print $f "array.setAtIndex($i, $m->{name}\$$i);\n"; } - } elsif ($jtype =~ m/$Memory.*Array/) { + } elsif ($jtype =~ m/^.*Array/) { print $f "\t\t$m->{name}\$VH.set(self.segment, Memory.address($m->{name}));\n"; } elsif ($jtype =~ m/^MemoryAddress|MemorySegment$/) { # or maybe not, force the caller to use MemoryAddress.NULL? @@ -1969,7 +1971,7 @@ foreach $x (sort keys %dump) { } } elsif (defined($data{$jtype})) { print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n"; - } elsif ($jtype =~ m/$Memory.*Array/) { + } elsif ($jtype =~ m/Array$|^HandleArray/) { print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n"; } else { print $f "\t\t$m->{name}\$VH.set(segment, value);\n"; @@ -1990,7 +1992,7 @@ foreach $x (sort keys %dump) { } } elsif (defined($data{$jtype})) { print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n"; - } elsif ($jtype =~ m/$Memory.*Array/) { + } elsif ($jtype =~ m/Array$|^HandleArray/) { print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n"; } else { print $f "\t\t$m->{name}\$VH.set(segment, value);\n"; @@ -2012,10 +2014,10 @@ foreach $x (sort keys %dump) { } elsif ($m->{fullType} =~ m/\[.+\]/) { print $f "\t\tMemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n"; print $f "\t\tMemorySegment seg = segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n"; - if ($jtype =~ m/^Memory.HandleArray/) { - print $f "\t\treturn new $jtype($m->{baseType}::new, seg);\n"; + if ($jtype =~ m/^HandleArray/) { + print $f "\t\treturn $jtype.create($m->{baseType}::new, seg);\n"; } else { - print $f "\t\treturn new $jtype(seg);\n"; + print $f "\t\treturn $jtype.create(seg);\n"; } } else { print $f "\t\treturn ($jtype)$m->{name}\$VH.get(segment);\n"; @@ -2041,10 +2043,10 @@ foreach $x (sort keys %dump) { } elsif ($m->{fullType} =~ m/\[.+\]/) { print $f "\t\tMemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n"; print $f "\t\tMemorySegment seg = segment.asSlice(index * LAYOUT.byteSize() + LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n"; - if ($jtype =~ m/^Memory.HandleArray/) { - print $f "\t\treturn new $jtype($m->{baseType}::new, seg);\n"; + if ($jtype =~ m/^HandleArray/) { + print $f "\t\treturn $jtype.create($m->{baseType}::new, seg);\n"; } else { - print $f "\t\treturn new $jtype(seg);\n"; + print $f "\t\treturn $jtype.create(seg);\n"; } } else { print $f "\t\treturn ($jtype)$m->{name}\$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize()));\n"; @@ -2054,8 +2056,8 @@ foreach $x (sort keys %dump) { } # insert template parts - if (open(my $template, "<", "template/$s->{name}-part.java")) { - print $f "// << inserted from: 'template/$s->{name}-part.java'\n"; + if (open(my $template, "<", "$FindBin::Bin/$s->{name}-part.java")) { + print $f "// << inserted from: '$FindBin::Bin/$s->{name}-part.java'\n"; while (<$template>) { print $f $_; } @@ -2091,6 +2093,7 @@ foreach $x (sort keys %dump) { open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java"); print $f "package $targetPackage;\n"; + print $f "import au.notzed.nativez.Memory;\n"; print $f "public class $s->{name} {\n"; my $tries = 0; @@ -2150,8 +2153,9 @@ foreach $x (sort keys %dump) { print $f "import jdk.incubator.foreign.*;\n"; print $f "import java.lang.invoke.*;\n"; + print $f "import au.notzed.nativez.*;\n"; - print $f "public final class $s->{name} implements Memory.Addressable {\n"; + print $f "public final class $s->{name} implements Pointer {\n"; print $f "\tfinal MemoryAddress address;\n"; @@ -2208,13 +2212,13 @@ foreach $x (sort keys %dump) { print $f "\t\treturn address != MemoryAddress.NULL ? new $s->{name}(address) : null;\n"; print $f "\t}\n\n"; - print $f "\tpublic static Memory.HandleArray<$s->{name}> createArray(Frame frame, long count) {\n"; - print $f "\t\treturn new Memory.HandleArray<>(frame, $s->{name}::new, count);\n"; + print $f "\tpublic static HandleArray<$s->{name}> createArray(long count, SegmentAllocator frame) {\n"; + print $f "\t\treturn HandleArray.createArray(count, frame, (a,s) -> $s->{name}.create(a));\n"; print $f "\t}\n\n"; - print $f "\tpublic static Memory.HandleArray<$s->{name}> createArray(ResourceScope scope, long count) {\n"; - print $f "\t\treturn new Memory.HandleArray<>($s->{name}::new, MemorySegment.allocateNative(Memory.POINTER.byteSize() * count, Memory.POINTER.byteAlignment(), scope));\n"; - print $f "\t}\n\n"; + #print $f "\tpublic static HandleArray<$s->{name}> createArray(ResourceScope scope, long count) {\n"; + #print $f "\t\treturn new HandleArray<>($s->{name}::new, MemorySegment.allocateNative(Memory.POINTER.byteSize() * count, Memory.POINTER.byteAlignment(), scope));\n"; + #print $f "\t}\n\n"; } print $f "\tpublic final MemoryAddress address() { return address; }\n"; @@ -2250,7 +2254,7 @@ foreach $x (sort keys %dump) { print $f "\t */\n"; if ($functionCreate{$c}) { - - exportCreateFunction($f, $cmd, 0) + exportCreateFunction($f, $cmd, 0) } else { exportFunction($f, $cmd, 0); } @@ -2300,8 +2304,8 @@ foreach $x (sort keys %dump) { } } - if (open(my $template, "<", "template/$s->{name}-part.java")) { - print $f "// << inserted from: 'template/$s->{name}-part.java'\n"; + if (open(my $template, "<", "$FindBin::Bin/$s->{name}-part.java")) { + print $f "// << inserted from: '$FindBin::Bin/$s->{name}-part.java'\n"; while (<$template>) { print $f $_; } @@ -2358,8 +2362,10 @@ foreach $ext (sort keys %functionByExtensionType) { print $f "import jdk.incubator.foreign.*;\n"; print $f "import java.lang.invoke.*;\n"; + print $f "import au.notzed.nativez.*;\n"; print $f "final class $className {\n"; + print $f " final ResourceScope scope;\n"; my @inits = (); my $index = 'a'; @@ -2373,6 +2379,7 @@ foreach $ext (sort keys %functionByExtensionType) { my $count = 0; print $f " $className(Vk$typeName o, ResourceScope scope) {\n"; + print $f " this.scope = scope;\n"; foreach $c (sort @list) { my $cmd = $commands{$c}; diff --git a/src/notzed.vkregistry/gen/gen.make b/src/notzed.vkregistry/gen/gen.make new file mode 100644 index 0000000..9b120b3 --- /dev/null +++ b/src/notzed.vkregistry/gen/gen.make @@ -0,0 +1,14 @@ + +G=src/notzed.vkregistry/gen + +bin/status/notzed.vkregistry.classes: bin/status/notzed.vkregistry.export +bin/status/notzed.vkregistry.classes: bin/modules/notzed.vkregistry/vulkan/test/mandelbrot.bin + +bin/status/notzed.vkregistry.export: $(G)/export-vulkan $(G)/VkDevice-part.java $(G)/VkInstance-part.java + $(G)/export-vulkan -t vulkan -d bin/gen/notzed.vkregistry/classes + mkdir -p $(@D) + touch $@ + +bin/modules/notzed.vkregistry/vulkan/test/mandelbrot.bin: $(G)/mandelbrot.comp + mkdir -p $(@D) + glslangValidator --target-env vulkan1.0 -V -o $@ $< diff --git a/test-vulkan/mandelbrot.comp b/src/notzed.vkregistry/gen/mandelbrot.comp similarity index 88% rename from test-vulkan/mandelbrot.comp rename to src/notzed.vkregistry/gen/mandelbrot.comp index 86e2a85..6a45590 100644 --- a/test-vulkan/mandelbrot.comp +++ b/src/notzed.vkregistry/gen/mandelbrot.comp @@ -8,13 +8,8 @@ layout (local_size_x = LWS_X, local_size_y = LWS_Y, local_size_z = 1 ) in; -struct Pixel{ - vec4 value; -}; - -layout(std140, binding = 0) buffer buf -{ - Pixel imageData[]; +layout(std430, binding = 0) buffer buf { + uint imageData[]; }; void main() { @@ -60,5 +55,6 @@ void main() { color = vec4(0, 0, 0, 1); // store the rendered mandelbrot set into a storage buffer: - imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color; + imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x] = packUnorm4x8(color); + //imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color; } diff --git a/src/template/Frame.java b/src/template/Frame.java deleted file mode 100644 index 5eafe57..0000000 --- a/src/template/Frame.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2021 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 . - */ -package api; - -import jdk.incubator.foreign.*; -import static jdk.incubator.foreign.ValueLayout.OfAddress; - -public interface Frame extends AutoCloseable, SegmentAllocator { - - @Override - MemorySegment allocate(long size, long alignment); - - @Override - public void close(); - - default MemorySegment allocateInt() { - return allocate(Memory.INT); - } - - default MemorySegment allocateInt(int count) { - return allocate(Memory.INT, count); - } - - default MemorySegment allocateLong() { - return allocate(Memory.LONG); - } - - default MemorySegment allocateLong(int count) { - return allocateArray(Memory.LONG, count); - } - - default MemorySegment allocatePointer() { - return allocate(Memory.POINTER); - } - - default MemorySegment allocatePointer(int count) { - return allocateArray(Memory.POINTER, count); - } - - default MemorySegment allocateArray(OfAddress type, MemoryAddress[] value) { - MemorySegment m = allocateArray(type, value.length); - for (int i=0;i 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; - } - - default MemorySegment copy(T value) { - return copy(value.address()); - } - - default MemorySegment copy(MemoryAddress value) { - MemorySegment mem = allocateAddress(); - MemoryAccess.setAddress(mem, value); - return mem; - } - */ - // create an array pointing to strings - default 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/template/Memory.java b/src/template/Memory.java deleted file mode 100644 index b720f99..0000000 --- a/src/template/Memory.java +++ /dev/null @@ -1,718 +0,0 @@ -/* - * Copyright (C) 2020 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 . - */ - -package api; - -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; - -/** - * A utility for memory operations including a stack allocator. - *

- * The stack allocator works like this - *

- * try (Frame f = Memory.createFrame()) {
- *		MemorySegment a = f.allocate(size);
- * }
- * 
- * Any memory allocated is freed when the frame is closed. - *

- * This is MUCH faster than using MemorySegment.allocateNative(). - */ -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; - } - - 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(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); - } - } - - static final ResourceScope scope = ResourceScope.newSharedScope(Cleaner.create()); - private static final ThreadLocal stacks = ThreadLocal.withInitial(() -> new Stack(scope)); - - public static Frame createFrame() { - return stacks.get().createFrame(); - } - - 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() { - private final long tos = sp; - private Thread self = thread; - private ResourceScope scope; - - @Override - public MemorySegment allocate(long size, long alignment) { - if (self != Thread.currentThread()) - throw new IllegalStateException(); - if (alignment != Long.highestOneBit(alignment)) - throw new IllegalArgumentException(); - if (sp >= size) { - sp = (sp - size) & ~(alignment - 1); - return stack.asSlice(sp, size).fill((byte)0); - } else { - if (scope == null) - scope = ResourceScope.newConfinedScope(); - return MemorySegment.allocateNative(size, alignment, scope); - } - } - - @Override - public void close() { - sp = tos; - self = null; - if (scope != null) { - scope.close(); - scope = null; - } - } - }; - } - } - - public interface Addressable { - MemoryAddress address(); - ResourceScope scope(); - } - - public interface Array { - long length(); - T getAtIndex(long i); - } - - public record FunctionPointer(NativeSymbol symbol, T function) { - } - - public static MemoryAddress address(jdk.incubator.foreign.Addressable v) { - return v != null ? v.address() : MemoryAddress.NULL; - } - - public static MemoryAddress address(Memory.Addressable v) { - return v != null ? v.address() : MemoryAddress.NULL; - } - - public static MemoryAddress address(FunctionPointer 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; - } - - // hmm do i want this or not? - // -> added 'type safety' - // -> load of crap to be written - public static class ByteArray extends AbstractList implements Memory.Addressable { - 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); - } - } - - public static class ShortArray extends AbstractList implements Memory.Addressable { - 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); - } - } - - public static class IntArray extends AbstractList implements Memory.Addressable { - 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 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); - } - } - - public static class LongArray extends AbstractList implements Memory.Addressable { - 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); - } - } - - public static class FloatArray extends AbstractList implements Memory.Addressable { - 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 * 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); - } - } - - public static class DoubleArray extends AbstractList implements Memory.Addressable { - 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); - } - } - - public static class PointerArray extends AbstractList implements Memory.Addressable { - 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); - } - } - - // This needs a separate scope from the array itself - public static class HandleArray extends AbstractList implements Memory.Addressable { - public final MemorySegment segment; - final ResourceScope scope; - BiFunction create; - - private HandleArray(MemorySegment segment, BiFunction create, ResourceScope scope) { - this.segment = segment; - this.create = create; - this.scope = scope; - } - - public static HandleArray create(MemorySegment segment, BiFunction create) { - return new HandleArray<>(segment, create, segment.scope()); - } - - public static HandleArray create(MemorySegment segment, BiFunction create, ResourceScope scope) { - return new HandleArray<>(segment, create, scope); - } - - public static HandleArray createArray(long size, SegmentAllocator alloc, BiFunction create) { - return create(alloc.allocateArray(Memory.POINTER, size), create); - } - - public static HandleArray createArray(long size, SegmentAllocator alloc, BiFunction create, ResourceScope scope) { - return create(alloc.allocateArray(Memory.POINTER, size), create, scope); - } - - public static HandleArray createArray(MemoryAddress address, long size, BiFunction 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/test-api-object/Makefile b/test-api-object/Makefile deleted file mode 100644 index e18fc77..0000000 --- a/test-api-object/Makefile +++ /dev/null @@ -1,44 +0,0 @@ - -JAVA_HOME?=/opt/jdk-foreign/jvm/openjdk-19-internal -JAVAC=$(JAVA_HOME)/bin/javac -JAVA=$(JAVA_HOME)/bin/java - -JAVACFLAGS=--add-modules jdk.incubator.foreign - -api_SOURCES := $(wildcard ../src/api/*.java) -api_demo_SOURCES := $(wildcard src/api/test/*.java) -apigen_DEPS := $(api_SOURCES) $(wildcard ../src/*.api) $(wildcard ../src/*.pm ../src/generate-api-2) - -all:: - mkdir -p bin - -all:: bin/demo.built - -bin/api.built: bin/api-object.gen - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \ - $(shell find bin/java -name '*.java') - touch $@ - -bin/api-object.gen: bin/api.pm bin/api-defines.pm $(apigen_DEPS) - ../src/generate-api-2 -v -d bin/java -t proto.apiobject -a ./bin/api.pm -a ./bin/api-defines.pm api-object.api - touch $@ - -bin/api-defines.pm: ../api/api.h ../src/export-defines api-object.api - ../src/export-defines --hack-new-format-2 -v -I.. -d $@ api-object.api - -bin/api.pm: ../api/api.h ../src/export.so - gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@ ./$< -o /dev/null - -bin/demo.built: $(api_demo_SOURCES) bin/api.built - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(api_demo_SOURCES) - touch $@ - -demo: bin/demo.built - $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ - -Djava.library.path=../api/bin -cp bin/classes \ - api.test.TestAPI - -clean: - rm -rf bin - -.PHONY: demo clean all diff --git a/test-api-object/README b/test-api-object/README deleted file mode 100644 index 59a3438..0000000 --- a/test-api-object/README +++ /dev/null @@ -1,8 +0,0 @@ - -Introduction ------------- - -Small generator and prototyping area for a c library. - -This is mostly to test out ideas like pattern matching for mostly -automatic api conversion. diff --git a/test-api-static/Makefile b/test-api-static/Makefile deleted file mode 100644 index 05df38a..0000000 --- a/test-api-static/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -CFLAGS=-g -fPIC - -JAVA_HOME?=/opt/jdk-foreign/jvm/openjdk-19-internal -JAVAC=$(JAVA_HOME)/bin/javac -JAVA=$(JAVA_HOME)/bin/java - -JAVACFLAGS=--add-modules jdk.incubator.foreign - -api_SOURCES := $(wildcard ../src/api/*.java) -api_demo_SOURCES := $(wildcard src/api/test/*.java) -apigen_DEPS := $(api_SOURCES) $(wildcard ../src/*.api) $(wildcard ../src/*.pm ../src/generate-api-2) - -all:: - mkdir -p bin - -all:: bin/demo.built - -bin/api.built: bin/api-static.gen - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \ - $(shell find bin/java -name '*.java') - touch $@ - -bin/api-static.gen: bin/api.pm bin/api-defines.pm api-static.api $(apigen_DEPS) - ../src/generate-api-2 -v -d bin/java -t proto.apistatic -a ./bin/api.pm -a ./bin/api-defines.pm api-static.api - touch $@ - -bin/api-defines.pm: ../api/api.h ../src/export-defines api-static.api - ../src/export-defines --hack-new-format-2 -I.. -d $@ api-static.api - -bin/api.pm: ../api/api.h ../src/export.so - gcc -fplugin=../src/export.so -I.. -fplugin-arg-export-output=$@ $< -o /dev/null - -bin/demo.built: $(api_demo_SOURCES) bin/api.built - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(api_demo_SOURCES) - touch $@ - -demo: bin/demo.built - $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ - -Djava.library.path=../api/bin -cp bin/classes \ - api.test.TestAPI - -clean: - rm -rf bin - -.PHONY: demo clean all diff --git a/test-api-static/README b/test-api-static/README deleted file mode 100644 index 59a3438..0000000 --- a/test-api-static/README +++ /dev/null @@ -1,8 +0,0 @@ - -Introduction ------------- - -Small generator and prototyping area for a c library. - -This is mostly to test out ideas like pattern matching for mostly -automatic api conversion. diff --git a/test-ffmpeg/Makefile b/test-ffmpeg/Makefile deleted file mode 100644 index e1403fc..0000000 --- a/test-ffmpeg/Makefile +++ /dev/null @@ -1,52 +0,0 @@ - -CFLAGS=-g -fPIC -HOST_CC=gcc - -JAVA_HOME=/opt/jdk-foreign/jvm/openjdk-19-internal -JAVAC=$(JAVA_HOME)/bin/javac -JAVA=$(JAVA_HOME)/bin/java - -JAVACFLAGS=--add-modules jdk.incubator.foreign - -api_SOURCES := $(wildcard ../src/api/*.java) -ffmpeg_SOURCES := $(wildcard src/proto/ffmpeg/*.java) -ffmpeg_demo_SOURCES := $(wildcard src/ffmpeg/test/*.java) - -all:: - mkdir -p bin - -all:: bin/demo.built - -bin/ffmpeg.built: bin/ffmpeg.gen $(ffmpeg_SOURCES) - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \ - $(shell find bin/java -name '*.java') \ - $(ffmpeg_SOURCES) - touch $@ - -bin/ffmpeg.gen: bin/ffmpeg.pm bin/ffmpeg-defines.pm ../src/generate-native $(api_SOURCES) - ../src/generate-native -d bin/java -t proto.ffmpeg -a ./bin/ffmpeg.pm -a ./bin/ffmpeg-defines.pm ffmpeg.api - touch $@ - -bin/ffmpeg-defines.pm: ffmpeg.h ../src/export-defines ffmpeg.api - ../src/export-defines -d bin/ffmpeg-defines.c ffmpeg.api - $(HOST_CC) -o bin/ffmpeg-defines -I. bin/ffmpeg-defines.c - bin/ffmpeg-defines $@~ - mv $@~ $@ - -bin/ffmpeg.pm: ffmpeg.h ../src/export.so - gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@~ ./$< -o /dev/null - mv $@~ $@ - -bin/demo.built: $(ffmpeg_demo_SOURCES) bin/ffmpeg.built - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(ffmpeg_demo_SOURCES) - touch $@ - -demo: all - $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ - -cp bin/classes \ - ffmpeg.test.TestFFMPEG - -clean: - rm -rf bin 000[0-9].pnm - -.PHONY: demo clean all diff --git a/test-ffmpeg/README b/test-ffmpeg/README deleted file mode 100644 index e22691f..0000000 --- a/test-ffmpeg/README +++ /dev/null @@ -1,12 +0,0 @@ - -Introduction ------------- - -Small demonstrator of generate-native on a complex and messy api -wrapped in a Java "object oriented" one. - -make demo ---------- - -Copy some video file to 'movie.avi' to run the demo. It will output -the first 10 frames to "\d{5}.pnm". diff --git a/test-ffmpeg/ffmpeg.api b/test-ffmpeg/ffmpeg.api deleted file mode 100644 index 777d287..0000000 --- a/test-ffmpeg/ffmpeg.api +++ /dev/null @@ -1,222 +0,0 @@ - -# access=rwi -# r read (get) -# w write (set) -# i read/write indexed (get/setAtIndex) - -struct field:rename=studly-caps 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 - - start_time access=r - duration access=r - bit_rate access=r - - interrupt_callback - - func:avformat_open_input static constructor=byref constructor-result=0 success=0 - func:avformat_find_stream_info rename=findStreamInfo instance=0 - - func:av_read_frame -} - -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 static rename=next - func:avcodec_find_decoder static - func:avcodec_find_decoder_by_name static - # here or on AVCodecContext? - func:avcodec_alloc_context3 rename=allocContext - - 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 static rename=alloc - func:avcodec_open2 rename=open - func:avcodec_send_packet - func:avcodec_receive_packet - func:avcodec_send_frame - func:avcodec_receive_frame - - define:AVCodecContextBits -} - -struct AVFrame default=all func:rename=s/^av_frame_//,camel-case field:rename=studly-caps { - func:av_frame_alloc static - func:av_frame_free -} - -struct AVFrameSideData default=none { -} - -struct AVRational field:rename=studly-caps { -} - -struct AVCodecParameters func:rename=s/^avcodec_parameters_//,camel-case field:rename=studly-caps { - codec_id rename=CodecID - func:avcodec_parameters_alloc static - func:avcodec_parameters_free - func:avcodec_parameters_copy - func:avcodec_parameters_to_context instance=1 - func:avcodec_parameters_from_context -} - -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 static - func:av_packet_free - func:av_init_packet rename=init -} - -struct SwsContext func:rename=s/^sws_// { -# func:/^sws_/ - func:sws_getContext static - func:sws_freeContext - func:sws_scale - - define:sws -} - -struct SwsFilter { -} -struct SwsVector { -} -struct AVPixFmtDescriptor { -} -struct AVComponentDescriptor access=rwi { -} - -library AVUtil { - func:/^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 - mime_type - flags - extensions -# want to define functions here - func:av_iformat_next static rename=next - func:av_register_input_format instance=0 rename=register -} - -struct AVOutputFormat default=none field:rename=studly-caps { - name - long_name - mime_type - flags - extensions - audio_codec - video_codec - subtitle_codec -} - -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_/ -} - -# This is used to define 'char *' arguments which are not strings. -# ... a bit clumsy - -func av_image_copy_plane { - src|dst array -} -func av_image_copy_to_buffer { - dst array -} -func av_image_fill_arrays { - src array -} -func av_image_fill_pointers { - ptr array -} diff --git a/test-ffmpeg/ffmpeg.h b/test-ffmpeg/ffmpeg.h deleted file mode 100644 index 1d3a58e..0000000 --- a/test-ffmpeg/ffmpeg.h +++ /dev/null @@ -1,6 +0,0 @@ - -#include -#include -#include -#include -#include diff --git a/test-opencl-basic/Makefile b/test-opencl-basic/Makefile deleted file mode 100644 index 913527e..0000000 --- a/test-opencl-basic/Makefile +++ /dev/null @@ -1,54 +0,0 @@ - -CFLAGS=-g -fPIC -HOST_CC=gcc - -JAVA_HOME=/opt/jdk-foreign/jvm/openjdk-19-internal -JAVAC=$(JAVA_HOME)/bin/javac -JAVA=$(JAVA_HOME)/bin/java - -JAVACFLAGS=--add-modules jdk.incubator.foreign - -api_SOURCES := $(wildcard ../src/api/*.java) -opencl_SOURCES := $(wildcard src/proto/opencl/*.java) -opencl_demo_SOURCES := $(wildcard src/opencl/test/*.java) - -$(info $(opencl_demo_SOURCES)) - -all:: - mkdir -p bin - -all:: bin/demo.built - -bin/opencl.built: bin/opencl.gen $(opencl_SOURCES) - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \ - $(shell find bin/java -name '*.java') \ - $(opencl_SOURCES) - touch $@ - -bin/opencl.gen: bin/opencl.pm bin/opencl-defines.pm ../src/generate-native $(api_SOURCES) - ../src/generate-native -d bin/java -t proto.opencl -a ./bin/opencl.pm -a ./bin/opencl-defines.pm opencl.api - touch $@ - -bin/opencl-defines.pm: opencl.h ../src/export-defines opencl.api - ../src/export-defines -d bin/opencl-defines.c opencl.api - $(HOST_CC) -o bin/opencl-defines -I. bin/opencl-defines.c - bin/opencl-defines $@~ - mv $@~ $@ - -bin/opencl.pm: opencl.h ../src/export.so - gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@~ ./$< -o /dev/null - mv $@~ $@ - -bin/demo.built: $(opencl_demo_SOURCES) bin/opencl.built - $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(opencl_demo_SOURCES) - touch $@ - -demo: all - $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ - -cp bin/classes \ - opencl.test.TestOpenCL - -clean: - rm -rf bin - -.PHONY: demo clean all diff --git a/test-opencl-basic/opencl.api b/test-opencl-basic/opencl.api deleted file mode 100644 index eeb8308..0000000 --- a/test-opencl-basic/opencl.api +++ /dev/null @@ -1,164 +0,0 @@ - -struct cl_image_format access=rwi { -} - -struct /^_cl/ access= rename=s/^_cl/cl/ { -} - - -# idea: dynamic will have a constructor that takes a SymbolLookup -# and ResourceScope -#library CLExt dynamic { -# clIcdGetPlatformIDsKHR -#} - -library CL { - define:CLConstants - - # core functions, resolved by dlopen - clGetPlatformIDs - clGetPlatformInfo - clGetDeviceIDs - clGetDeviceInfo - clCreateSubDevices - clRetainDevice - clReleaseDevice - clSetDefaultDeviceCommandQueue - clGetDeviceAndHostTimer - clGetHostTimer - clCreateContext - clCreateContextFromType - clRetainContext - clReleaseContext - clGetContextInfo - clCreateCommandQueueWithProperties - clRetainCommandQueue - clReleaseCommandQueue - clGetCommandQueueInfo - clCreateBuffer - clCreateSubBuffer - clCreateImage - clCreatePipe - clRetainMemObject - clReleaseMemObject - clGetSupportedImageFormats - clGetMemObjectInfo - clGetImageInfo - clGetPipeInfo - clSetMemObjectDestructorCallback - clSVMAlloc - 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 - clEnqueueReadBuffer - clEnqueueReadBufferRect - clEnqueueWriteBuffer - clEnqueueWriteBufferRect - clEnqueueFillBuffer - clEnqueueCopyBuffer - clEnqueueCopyBufferRect - clEnqueueReadImage - clEnqueueWriteImage - clEnqueueFillImage - clEnqueueCopyImage - clEnqueueCopyImageToBuffer - clEnqueueCopyBufferToImage - clEnqueueMapBuffer - clEnqueueMapImage - clEnqueueUnmapMemObject - clEnqueueMigrateMemObjects - clEnqueueNDRangeKernel - clEnqueueNativeKernel - clEnqueueMarkerWithWaitList - clEnqueueBarrierWithWaitList - clEnqueueSVMFree - clEnqueueSVMMemcpy - clEnqueueSVMMemFill - clEnqueueSVMMap - clEnqueueSVMUnmap - clEnqueueSVMMigrateMem - clGetExtensionFunctionAddressForPlatform - clCreateImage2D - clCreateImage3D - clEnqueueMarker - clEnqueueWaitForEvents - clEnqueueBarrier - clUnloadCompiler - clGetExtensionFunctionAddress - clCreateCommandQueue - clCreateSampler - clEnqueueTask -} - - -# base constants -define CLConstants opencl.h { - /.*/cl.h/ file-include -} - -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 - -} - -func clGetPlatformIDs { - num_entries implied=Memory.length(platforms) -} - -func clGetDeviceIDs { - num_entries implied=Memory.length(devices) -} - -func clCreateContext { - num_devices implied=Memory.length(devices) - errcode_ret result_code success=CL_SUCCESS -} - -func /clCreate/ { - errcode_ret result_code success=CL_SUCCESS -} - -func clGetSupportedImageFormats { - num_entries implied=(image_formats!=null?image_formats.length():0) -} diff --git a/test-vulkan/Makefile b/test-vulkan/Makefile deleted file mode 100644 index 2ecde13..0000000 --- a/test-vulkan/Makefile +++ /dev/null @@ -1,41 +0,0 @@ - -JAVA_HOME=/opt/jdk-foreign/jvm/openjdk-19-internal -JAVAC=$(JAVA_HOME)/bin/javac -JAVA=$(JAVA_HOME)/bin/java - -zvk_TEMPLATES := $(wildcard template/*.java) -zvk_SOURCES := $(wildcard src/zvk/*.java) -zvk_demo_SOURCES := $(wildcard src/zvk/test/*.java) - -all:: - mkdir -p bin - -all:: bin/demo.built bin/classes/zvk/test/mandelbrot.bin - -bin/api.built: bin/api.gen $(zvk_SOURCES) - $(JAVAC) --add-modules jdk.incubator.foreign -d bin/classes \ - $(shell find bin/gen -name '*.java') \ - $(zvk_SOURCES) - touch $@ - -bin/api.gen: /usr/share/vulkan/registry/vk.xml generate-vulkan $(zvk_TEMPLATES) - PERL_HASH_SEED=0 ./generate-vulkan -d bin/gen - touch $@ - -bin/demo.built: bin/api.built $(zvk_demo_SOURCES) - $(JAVAC) --add-modules jdk.incubator.foreign -d bin/classes -cp bin/classes \ - $(zvk_demo_SOURCES) - touch $@ - -bin/classes/zvk/test/mandelbrot.bin: mandelbrot.comp - mkdir -p $(@D) - glslangValidator --target-env vulkan1.0 -V -o $@ $< - -demo: bin/demo.built bin/classes/zvk/test/mandelbrot.bin - $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \ - -cp bin/classes zvk.test.TestVulkan - -clean: - rm -rf api bin mandelbrot.pam - -.PHONY: demo clean all diff --git a/test-vulkan/src/zvk/Frame.java b/test-vulkan/src/zvk/Frame.java deleted file mode 100644 index 0514252..0000000 --- a/test-vulkan/src/zvk/Frame.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2021 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 . - */ -package zvk; - -import jdk.incubator.foreign.*; -import static jdk.incubator.foreign.ValueLayout.OfAddress; - -public interface Frame extends AutoCloseable, SegmentAllocator { - - @Override - MemorySegment allocate(long size, long alignment); - - @Override - public void close(); - - default MemorySegment allocateInt() { - return allocate(Memory.INT); - } - - default MemorySegment allocateInt(int count) { - return allocate(Memory.INT, count); - } - - default MemorySegment allocateLong() { - return allocate(Memory.LONG); - } - - default MemorySegment allocateLong(int count) { - return allocateArray(Memory.LONG, count); - } - - default MemorySegment allocatePointer() { - return allocate(Memory.POINTER); - } - - default MemorySegment allocatePointer(int count) { - return allocateArray(Memory.POINTER, count); - } - - default MemorySegment allocateArray(OfAddress type, MemoryAddress[] value) { - MemorySegment m = allocateArray(type, value.length); - for (int i=0;i 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; - } - - default MemorySegment copy(T value) { - return copy(value.address()); - } - - default MemorySegment copy(MemoryAddress value) { - MemorySegment mem = allocateAddress(); - MemoryAccess.setAddress(mem, value); - return mem; - } - */ - // create an array pointing to strings - default 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/test-vulkan/src/zvk/Memory.java b/test-vulkan/src/zvk/Memory.java deleted file mode 100644 index ac24cdb..0000000 --- a/test-vulkan/src/zvk/Memory.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright (C) 2020 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 . - */ - -package zvk; - -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; - -/** - * A utility for memory operations including a stack allocator. - *

- * The stack allocator works like this - *

- * try (Frame f = Memory.createFrame()) {
- *		MemorySegment a = f.allocate(size);
- * }
- * 
- * Any memory allocated is freed when the frame is closed. - *

- * This is MUCH faster than using MemorySegment.allocateNative(). - */ -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; - } - - 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(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); - } - } - -static final ResourceScope scope = ResourceScope.newSharedScope(Cleaner.create()); - private static final ThreadLocal stacks = ThreadLocal.withInitial(() -> new Stack(scope)); - - public static Frame createFrame() { - return stacks.get().createFrame(); - } - - 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() { - private final long tos = sp; - private Thread self = thread; - private ResourceScope scope; - - @Override - public MemorySegment allocate(long size, long alignment) { - if (self != Thread.currentThread()) - throw new IllegalStateException(); - if (alignment != Long.highestOneBit(alignment)) - throw new IllegalArgumentException(); - if (sp >= size) { - sp = (sp - size) & ~(alignment - 1); - return stack.asSlice(sp, size).fill((byte)0); - } else { - if (scope == null) - scope = ResourceScope.newConfinedScope(); - return MemorySegment.allocateNative(size, alignment, scope); - } - } - - @Override - public void close() { - sp = tos; - self = null; - if (scope != null) { - scope.close(); - scope = null; - } - } - }; - } - } - - public static MemoryAddress address(jdk.incubator.foreign.Addressable v) { - return v != null ? v.address() : MemoryAddress.NULL; - } - - public static MemoryAddress address(Memory.Addressable v) { - return v != null ? v.address() : MemoryAddress.NULL; - } - - public interface Addressable { - MemoryAddress address(); - } - - // hmm do i want this or not? - // -> added 'type safety' - // -> load of crap to be written - public static class ByteArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public ByteArray(MemorySegment segment) { - this.segment = segment; - } - - public ByteArray(Frame frame, long size) { - this(frame.allocateArray(Memory.BYTE, size)); - } - - public ByteArray(Frame frame, byte... values) { - this(frame.allocateArray(Memory.BYTE, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class ShortArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public ShortArray(MemorySegment segment) { - this.segment = segment; - } - - public ShortArray(Frame frame, long size) { - this(frame.allocateArray(Memory.SHORT, size)); - } - - public ShortArray(Frame frame, short... values) { - this(frame.allocateArray(Memory.SHORT, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class IntArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public IntArray(MemorySegment segment) { - this.segment = segment; - } - - public IntArray(Frame frame, long size) { - this(frame.allocateArray(Memory.INT, size)); - } - - public IntArray(Frame frame, int... values) { - this(frame.allocateArray(Memory.INT, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class LongArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public LongArray(MemorySegment segment) { - this.segment = segment; - } - - public LongArray(Frame frame, long size) { - this(frame.allocateArray(Memory.LONG, size)); - } - - public LongArray(Frame frame, long... values) { - this(frame.allocateArray(Memory.LONG, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class FloatArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public FloatArray(MemorySegment segment) { - this.segment = segment; - } - - public FloatArray(Frame frame, long size) { - this(frame.allocateArray(Memory.FLOAT, size)); - } - - public FloatArray(Frame frame, float... values) { - this(frame.allocateArray(Memory.FLOAT, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class DoubleArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public DoubleArray(MemorySegment segment) { - this.segment = segment; - } - - public DoubleArray(Frame frame, long size) { - this(frame.allocateArray(Memory.DOUBLE, size)); - } - - public DoubleArray(Frame frame, double... values) { - this(frame.allocateArray(Memory.DOUBLE, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class PointerArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - - public PointerArray(MemorySegment segment) { - this.segment = segment; - } - - public PointerArray(Frame frame, long size) { - this(frame.allocateArray(Memory.POINTER, size)); - } - - public PointerArray(Frame frame, MemoryAddress... values) { - this(frame.allocateArray(Memory.POINTER, values)); - } - - public final MemoryAddress address() { - return segment.address(); - } - - @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); - } - } - - public static class HandleArray extends AbstractList implements Memory.Addressable { - final MemorySegment segment; - Function create; - - public HandleArray(Function create, MemorySegment segment) { - this.segment = segment; - this.create = create; - } - - public HandleArray(Frame frame, Function create, long size) { - this(create, frame.allocateArray(Memory.POINTER, size)); - } - - @Override - public final MemoryAddress address() { - return segment.address(); - } - - @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) : null; - } - - public void setAtIndex(long index, T value) { - segment.setAtIndex(Memory.POINTER, index, value != null ? value.address() : MemoryAddress.NULL); - } - } - -} diff --git a/test-vulkan/src/zvk/PFN_Test.java b/test-vulkan/src/zvk/PFN_Test.java deleted file mode 100644 index d8327c5..0000000 --- a/test-vulkan/src/zvk/PFN_Test.java +++ /dev/null @@ -1,107 +0,0 @@ - -package zvk; - -import java.lang.invoke.*; -import jdk.incubator.foreign.*; - -/* -typedef void (*PFN_vkDeviceMemoryReportCallbackEXT)(const VkDeviceMemoryReportCallbackDataEXT* pCallbackData, void* pUserData); -*/ - -// or just a record? does it need to do anything fancy? -/* -interface FunctionPointer { - NativeSymbol symbol(); - T function(); -} -*/ -record FunctionPointer(NativeSymbol symbol, T function) { } - -@FunctionalInterface -public interface PFN_Test extends Memory.Addressable { - - void call(VkDeviceMemoryReportCallbackDataEXT info); - - default MemoryAddress address() { - throw new UnsupportedOperationException("Non-native method"); - } - - @FunctionalInterface - interface Trampoline { - void call(MemoryAddress info, MemoryAddress userData); - }; - - static FunctionDescriptor DESCRIPTOR() { - return FunctionDescriptor.ofVoid( - Memory.POINTER, - Memory.POINTER); - } - - public static FunctionPointer upcall(PFN_Test target, ResourceScope scope) { - return new FunctionPointer<> ( - Memory.upcall((Trampoline)(info, userData)->{ - try { - target.call( - new VkDeviceMemoryReportCallbackDataEXT( - MemorySegment.ofAddress(info, - VkDeviceMemoryReportCallbackDataEXT.LAYOUT.byteSize(), - ResourceScope.globalScope()))); - } catch (Exception x) { - } - }, - "call", - "(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)I", - DESCRIPTOR(), - scope), - target); - } - - public static FunctionPointer downcall(String name, MemoryAddress addr, ResourceScope scope) { - NativeSymbol symbol = NativeSymbol.ofAddress(name, addr, scope); - MethodHandle fp = Memory.downcall(symbol, DESCRIPTOR()); - - return new FunctionPointer<>( - symbol, - (info) -> { - try { - fp.invokeExact((Addressable)Memory.address(info)); - } catch (Throwable t) { - throw new RuntimeException(t); - } - }); - } - - public static NativeSymbol ofUpcall(PFN_Test target, ResourceScope scope) { - Trampoline trampline = (info, userData) -> { - try { - target.call( - new VkDeviceMemoryReportCallbackDataEXT( - MemorySegment.ofAddress(info, - VkDeviceMemoryReportCallbackDataEXT.LAYOUT.byteSize(), - ResourceScope.globalScope()))); - } catch (Exception x) { - } - }; - return Memory.upcall(trampline, "call", - "(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)I", - DESCRIPTOR(), - scope); - } - - public static PFN_Test ofDowncall(String name, MemoryAddress addr, ResourceScope scope) { - NativeSymbol symbol = NativeSymbol.ofAddress(name, addr, scope); - MethodHandle fp = Memory.downcall(symbol, DESCRIPTOR()); - - return new PFN_Test() { - public MemoryAddress address() { return symbol.address(); }; - - public void call(VkDeviceMemoryReportCallbackDataEXT info) { - try { - fp.invokeExact((Addressable)Memory.address(info)); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - }; - } -} diff --git a/test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java b/test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java deleted file mode 100644 index 226ea28..0000000 --- a/test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java +++ /dev/null @@ -1,62 +0,0 @@ - -package zvk; - -import java.lang.invoke.*; -import jdk.incubator.foreign.*; - -/* -callback: typedef VkBool32 (*PFN_vkDebugReportCallbackEXT)( - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, - size_t location, - int32_t messageCode, - const char* pLayerPrefix, - const char* pMessage, - void* pUserData); -*/ -public class PFN_vkDebugReportCallbackEXT implements Memory.Addressable { - - NativeSymbol symbol; - - public PFN_vkDebugReportCallbackEXT(NativeSymbol symbol) { - this.symbol = symbol; - } - - public MemoryAddress address() { - return symbol.address(); - } - - public interface Callback { - int call(int flags, int type, long thing, long location, int code, String layer, String message); - } - - private interface Trampoline { - int call(int flags, int type, long thing, long location, int code, MemoryAddress layer, MemoryAddress message, MemoryAddress userData); - } - - public static NativeSymbol of(Callback target, ResourceScope scope) { - Trampoline trampline = (int flags, int type, long thing, long location, int code, MemoryAddress layer, MemoryAddress message, MemoryAddress userData) -> { - try { - return target.call(flags, type, thing, location, code, - layer.getUtf8String(0), - message.getUtf8String(0)); - } catch (Exception x) { - } - return 0; - }; - return Memory.upcall(trampline, "call", - "(IIJJILjdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress)I", - FunctionDescriptor.of( - Memory.INT, - Memory.INT, - Memory.INT, - Memory.LONG, - Memory.LONG, - Memory.INT, - Memory.POINTER, - Memory.POINTER, - Memory.POINTER), - scope); - } -} diff --git a/test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java b/test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java deleted file mode 100644 index dfda985..0000000 --- a/test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java +++ /dev/null @@ -1,48 +0,0 @@ - -package zvk; - -import java.lang.invoke.*; -import jdk.incubator.foreign.*; - -/* -typedef void (*PFN_vkDeviceMemoryReportCallbackEXT)(const VkDeviceMemoryReportCallbackDataEXT* pCallbackData, void* pUserData); -*/ -public class PFN_vkDeviceMemoryReportCallbackEXT implements Memory.Addressable { - - NativeSymbol symbol; - - public PFN_vkDeviceMemoryReportCallbackEXT(NativeSymbol symbol) { - this.symbol = symbol; - } - - public MemoryAddress address() { - return symbol.address(); - } - - public interface Callback { - void call(VkDeviceMemoryReportCallbackDataEXT info); - } - - private interface Trampoline { - void call(MemoryAddress info, MemoryAddress userData); - } - - public static NativeSymbol of(Callback target, ResourceScope scope) { - Trampoline trampline = (info, userData) -> { - try { - target.call( - new VkDeviceMemoryReportCallbackDataEXT( - MemorySegment.ofAddress(info, - VkDeviceMemoryReportCallbackDataEXT.LAYOUT.byteSize(), - ResourceScope.globalScope()))); - } catch (Exception x) { - } - }; - return Memory.upcall(trampline, "call", - "(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)I", - FunctionDescriptor.ofVoid( - Memory.POINTER, - Memory.POINTER), - scope); - } -} -- 2.39.5