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