Updated for openjdk-19-internal
authorNot Zed <notzed@gmail.com>
Wed, 22 Dec 2021 01:15:59 +0000 (11:45 +1030)
committerNot Zed <notzed@gmail.com>
Wed, 22 Dec 2021 01:15:59 +0000 (11:45 +1030)
Wrote entirely new generator for c header files.
Added vulkan demo.  Generates api from api specification.
Makefile tweaks.

25 files changed:
src/export.cc
src/generate [deleted file]
src/generate-api [deleted file]
src/generate-native [new file with mode: 0755]
src/template/Frame.java [new file with mode: 0644]
src/template/Memory.java [new file with mode: 0644]
src/template/Native.java [new file with mode: 0644]
test-api/Makefile
test-api/api.c
test-api/api.h
test-api/api/test/TestAPI.java [deleted file]
test-api/src/api/test/TestAPI.java [new file with mode: 0644]
test-vulkan/Makefile [new file with mode: 0644]
test-vulkan/generate-vulkan [new file with mode: 0755]
test-vulkan/mandelbrot.comp [new file with mode: 0644]
test-vulkan/src/zvk/Frame.java [new file with mode: 0644]
test-vulkan/src/zvk/Memory.java [new file with mode: 0644]
test-vulkan/src/zvk/PFN_Test.java [new file with mode: 0644]
test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java [new file with mode: 0644]
test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java [new file with mode: 0644]
test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java [new file with mode: 0644]
test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java [new file with mode: 0644]
test-vulkan/src/zvk/test/TestVulkan.java [new file with mode: 0644]
test-vulkan/template/VkDevice-part.java [new file with mode: 0644]
test-vulkan/template/VkInstance-part.java [new file with mode: 0644]

index 17d4dad..4a34c58 100644 (file)
    https://blog.adacore.com/bindings-gcc-plugins
  */
 
+/*
+  TODO: get header name from tree
+ */
+
 /*
 
 function declarations, i think
@@ -46,6 +50,7 @@ FUNCTION_DECL     DECL_ARGUENTS    TREE_TYPE(item):PARM_DECL   DECL_NAME(item)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <gcc-plugin.h>
 #include <tree.h>
@@ -68,6 +73,8 @@ extern const char *tree_codes[];
 int plugin_is_GPL_compatible; // must be defined for the plugin to run
 
 static FILE *output_file;
+static int output_enabled = 1;
+
 static int debug_level = 0;
 
 static void debug_tree_helper(tree t, const char *msg) {
@@ -89,6 +96,17 @@ static struct list parameters;       // last list of params
 
 static struct hash forward_types;
 
+static int generate(const char *fmt, ...) {
+       int res = 0;
+       if (output_enabled) {
+               va_list ap;
+               va_start(ap, fmt);
+               res = vfprintf(output_file, fmt, ap);
+               va_end(ap);
+       }
+       return res;
+}
+
 /*
   Join all names in the stack, in reverse order.
 */
@@ -127,8 +145,10 @@ static bool is_struct_or_union(const_tree type) {
 }
 
 static void print_spaces(int n) {
-       for (int i = 0; i < n; ++i)
-               fputc('\t', output_file);
+       if (output_enabled) {
+               for (int i = 0; i < n; ++i)
+                       fputc('\t', output_file);
+       }
 }
 
 static int is_ref_type(tree type) {
@@ -409,7 +429,7 @@ static void export_param(tree field, tree field_type, size_t field_size) {
 
                buffer_init(&b, 256);
                export_desc(field, field_type, &b);
-               fprintf(output_file, " deref => '%s',", b.data);
+               generate(" deref => '%s',", b.data);
                free(b.data);
 
                field_type = simple_type(field_type);
@@ -419,17 +439,17 @@ static void export_param(tree field, tree field_type, size_t field_size) {
                break;
        }
        case VOID_TYPE:
-               fprintf(output_file, " type => 'void',");
-               fprintf(output_file, " ctype => 'void',");
+               generate(" type => 'void',");
+               generate(" ctype => 'void',");
                break;
        case ENUMERAL_TYPE: {
 #if defined(TYPED_ENUMS)
                const char *names = TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum";
-               fprintf(output_file, " type => 'enum:%s',", names);
+               generate(" type => 'enum:%s',", names);
 #else
-               fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
+               generate(" type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
 #endif
-               fprintf(output_file, " ctype => 'enum %s',", value_name(field_type));
+               generate(" ctype => 'enum %s',", value_name(field_type));
                break;
        }
        case FUNCTION_TYPE: {
@@ -438,13 +458,14 @@ static void export_param(tree field, tree field_type, size_t field_size) {
 
                // If this is a typedef we might have a name for the type, otherwise it's a signature based name
                if (root_type && TYPE_IDENTIFIER(root_type)) {
-                       fprintf(output_file, " type => 'call:%s', ", value_name(root_type));
+                       generate(" type => 'call:%s', ", value_name(root_type));
                } else {
-                       fprintf(stderr, "save for later param type %p\n", field_type);
+                       if (debug_level > 0)
+                               fprintf(stderr, "save for later param type %p\n", field_type);
                        buffer_init(&b, 256);
                        export_desc(field, field_type, &b);
                        list_add(&todump, node_alloc(field_type, b.data));
-                       fprintf(output_file, " type => 'call:%s', ", b.data);
+                       generate(" type => 'call:%s', ", b.data);
                }
 
                buffer_init(&b, 256);
@@ -454,16 +475,16 @@ static void export_param(tree field, tree field_type, size_t field_size) {
                break;
        }
        case REAL_TYPE:
-               fprintf(output_file, " ctype => '%s',", value_name(field_type));
-               fprintf(output_file, " type => 'f%zu',", field_size);
+               generate(" ctype => '%s',", value_name(field_type));
+               generate(" type => 'f%zu',", field_size);
                break;
        case INTEGER_TYPE:
                if (TREE_CODE(field) == FIELD_DECL && DECL_BIT_FIELD(field)) {
-                       fprintf(output_file, " ctype => 'bitfield',");
-                       fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', value_size(DECL_SIZE(field)));
+                       generate(" ctype => 'bitfield',");
+                       generate(" type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', value_size(DECL_SIZE(field)));
                } else {
-                       fprintf(output_file, " ctype => '%s',", value_name(field_type));
-                       fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
+                       generate(" ctype => '%s',", value_name(field_type));
+                       generate(" type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
                }
                break;
        case RECORD_TYPE:
@@ -471,12 +492,12 @@ static void export_param(tree field, tree field_type, size_t field_size) {
                const char *us = TREE_CODE(field_type) == RECORD_TYPE ? "struct" : "union";
 
                if (TYPE_IDENTIFIER(field_type)) {
-                       fprintf(output_file, " type => '%s:%s',", us, value_name(field_type));
+                       generate(" type => '%s:%s',", us, value_name(field_type));
                } else {
                        char *name = stack_path(&context_stack, "_");
 
                        list_add(&todump, node_alloc(field_type, name));
-                       fprintf(output_file, " type => '%s:%s',", us, name);
+                       generate(" type => '%s:%s',", us, name);
                        free(name);
                }
                break;
@@ -499,7 +520,8 @@ static void export_params(tree func) {
 
        if (fwd) {
                // use the forward reference to find the names
-               fprintf(stderr, "found forward reference @ %p\n", fwd);
+               if (debug_level > 0)
+                       fprintf(stderr, "found forward reference @ %p\n", fwd);
                name = fwd->list.head;
        } else {
                // paramter names are in the paramters list
@@ -513,7 +535,8 @@ static void export_params(tree func) {
 
                        struct node *decl = stack_pull(&parameters);
                        if (decl) {
-                               fprintf(stderr, "(pull parameter '%s')\n", decl->name);
+                               if (debug_level > 0)
+                                       fprintf(stderr, "(pull parameter '%s')\n", decl->name);
                                stack_push(&args, decl);
                        } else
                                fprintf(stderr, "ERROR: parameter %d missing parameter declaration\n", id);
@@ -531,10 +554,10 @@ static void export_params(tree func) {
                if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
                        break;
 
-               fprintf(output_file, "\t\t{");
+               generate("\t\t{");
 
                // size: do we need it?
-               fprintf(output_file, " size => %zu,", data_size);
+               generate(" size => %zu,", data_size);
 
                if (name) {
                        // this should be a parm_decl with an identifier of the name
@@ -545,11 +568,11 @@ static void export_params(tree func) {
                }
 
                if (!names || !names[0]) {
-                       sprintf(nameb, "arg_%d", id);
+                       sprintf(nameb, "arg$%d", id);
                        names = nameb;
                }
 
-               fprintf(output_file, " name => '%s',", names);
+               generate(" name => '%s',", names);
                stack_push(&context_stack, node_alloc(param, names));
 
                // value: details
@@ -557,7 +580,7 @@ static void export_params(tree func) {
 
                free(stack_pull(&context_stack));
 
-               fprintf(output_file, "},\n");
+               generate("},\n");
                id++;
        }
 
@@ -585,7 +608,7 @@ static void export_fields(tree first_field, size_t base_offset, int indent) {
                        if (debug_level > 1)
                                fprintf(stderr, "   field: %s\n", names);
                        print_spaces(indent+1);
-                       fprintf(output_file, "{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
+                       generate("{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
                        stack_push(&context_stack, node_alloc(field, names));
 
                        // value: details
@@ -593,7 +616,7 @@ static void export_fields(tree first_field, size_t base_offset, int indent) {
 
                        free(stack_pull(&context_stack));
 
-                       fprintf(output_file, "},\n");
+                       generate("},\n");
                }
        }
 }
@@ -630,7 +653,7 @@ static void export_type(tree type, const char *names) {
                case FUNCTION_TYPE: {
                        // function pointer typdef
                        // I don't know if i even want this
-                       fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
+                       generate("'call:%s' => { name => '%s', type => 'call',", names, names);
 
                        // the deftype is always a pointer for a function_type
 
@@ -638,24 +661,24 @@ static void export_type(tree type, const char *names) {
 
                        buffer_init(&b, 256);
                        export_desc(type, deftype, &b);
-                       fprintf(output_file, " deref => '%s',", b.data);
+                       generate(" deref => '%s',", b.data);
                        free(b.data);
 
-                       fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(target));
+                       generate(" ctype => '%s',", print_generic_expr_to_str(target));
 
                        // TODO: cleanup
                        {
                                tree result_type = TREE_TYPE(target);
                                const size_t data_size = value_size(TYPE_SIZE(result_type));
 
-                               fprintf(output_file, "\n\tresult => {");
+                               generate("\n\tresult => {");
                                export_param(target, result_type, data_size);
-                               fprintf(output_file, " },");
+                               generate(" },");
                        }
 
-                       fprintf(output_file, "\n\targuments => [\n");
+                       generate("\n\targuments => [\n");
                        export_params(target);
-                       fprintf(output_file, "]},\n");
+                       generate("]},\n");
                        break;
                }
                case ENUMERAL_TYPE: {
@@ -670,15 +693,15 @@ static void export_type(tree type, const char *names) {
                                return;
                        }
 
-                       fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
+                       generate("'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
                                names, names, size, TYPE_UNSIGNED(target) ? 'u' : 'i', size);
 
                        for (tree v = TYPE_VALUES(target); v != NULL; v = TREE_CHAIN (v)) {
-                               fprintf(output_file, "\t{ label => '%s', value => '%ld' },\n",
+                               generate("\t{ label => '%s', value => '%ld' },\n",
                                        IDENTIFIER_POINTER(TREE_PURPOSE(v)),
                                        tree_to_shwi(TREE_VALUE(v)));
                        }
-                       fprintf(output_file, "]},\n");
+                       generate("]},\n");
                        break;
                }
                case RECORD_TYPE: // forward declaration or opaque types
@@ -710,12 +733,12 @@ static void export_type(tree type, const char *names) {
                if (debug_level > 1)
                        fprintf(stderr, "export type func decl %s\n", names);
 
-               fprintf(output_file, "'func:%s' => { name => '%s', type => 'func',", names, names);
+               generate("'func:%s' => { name => '%s', type => 'func',", names, names);
 
                // FUNCTION_DECL -> FUNCTION_TYPE -> RESULT_TYPE, get FUNCTION_TYPE
                type = TREE_TYPE(type);
 
-               fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(type));
+               generate(" ctype => '%s',", print_generic_expr_to_str(type));
 
                // TODO: cleanup
                debug_tree_helper(type, "function 1");
@@ -723,15 +746,15 @@ static void export_type(tree type, const char *names) {
                        tree result_type = TREE_TYPE(type);
                        const size_t data_size = value_size(TYPE_SIZE(result_type));
 
-                       fprintf(output_file, "\n\tresult => {");
+                       generate("\n\tresult => {");
                        export_param(type, result_type, data_size);
-                       fprintf(output_file, " },");
+                       generate(" },");
                }
 
-               fprintf(output_file, "\n\targuments => [\n");
+               generate("\n\targuments => [\n");
                //export_decl_params(DECL_ARGUMENTS(type), 0);
                export_params(type);
-               fprintf(output_file, "]},\n");
+               generate("]},\n");
                break;
        }
        case FUNCTION_TYPE: {
@@ -750,8 +773,8 @@ static void export_type(tree type, const char *names) {
                if (debug_level > 1)
                        fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
 
-               fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
-               fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(type));
+               generate("'call:%s' => { name => '%s', type => 'call',", names, names);
+               generate(" ctype => '%s',", print_generic_expr_to_str(type));
 
                debug_tree_helper(type, "function type");
 
@@ -765,16 +788,16 @@ static void export_type(tree type, const char *names) {
 
                        if (debug_level > 2)
                                fprintf(stderr, " result size %zu\n", data_size);
-                       fprintf(output_file, "\n\tresult => {");
+                       generate("\n\tresult => {");
                        export_param(type, result, data_size);
-                       fprintf(output_file, " },");
+                       generate(" },");
                }
 
                stack_push(&context_stack, node_alloc(type, names));
-               fprintf(output_file, "\n\targuments => [\n");
+               generate("\n\targuments => [\n");
                export_params(type);
                free(stack_pull(&context_stack));
-               fprintf(output_file, "]},\n");
+               generate("]},\n");
                break;
        }
        case RECORD_TYPE: // struct
@@ -798,14 +821,14 @@ static void export_type(tree type, const char *names) {
                if (debug_level > 1)
                        fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
 
-               fprintf(output_file, "'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
+               generate("'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
                        su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
 
                stack_push(&context_stack, node_alloc(type, names));
                export_fields(TYPE_FIELDS(type), 0, 0);
                free(stack_pull(&context_stack));
 
-               fprintf(output_file, "]},\n");
+               generate("]},\n");
                break;
        }
        case ENUMERAL_TYPE: {
@@ -846,15 +869,15 @@ static void export_type(tree type, const char *names) {
                if (debug_level > 1)
                        fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
 
-               fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
+               generate("'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
                        names, names, size, TYPE_UNSIGNED(type) ? 'u' : 'i', size);
 
                for (tree v = TYPE_VALUES(type); v != NULL; v = TREE_CHAIN (v)) {
-                       fprintf(output_file, "\t{ label => '%s', value => '%ld' },\n",
+                       generate("\t{ label => '%s', value => '%ld' },\n",
                                IDENTIFIER_POINTER(TREE_PURPOSE(v)),
                                tree_to_shwi(TREE_VALUE(v)));
                }
-               fprintf(output_file, "]},\n");
+               generate("]},\n");
                break;
        }
        case FIELD_DECL:
@@ -879,7 +902,8 @@ static void export_type(tree type, const char *names) {
                        // it's keyed on target
                        struct node *fwd = node_alloc(target, NULL);
 
-                       fprintf(stderr, "save forward reference function type %p\n", target);
+                       if (debug_level > 0)
+                               fprintf(stderr, "save forward reference function type %p\n", target);
 
                        for (tree param = TYPE_ARG_TYPES(target); param != NULL; param = TREE_CHAIN(param)) {
                                tree param_type = TREE_VALUE(param);
@@ -889,16 +913,18 @@ static void export_type(tree type, const char *names) {
 
                                struct node *decl = stack_pull(&parameters);
                                if (decl) {
-                                       fprintf(stderr, "(pull parameter '%s')\n", decl->name);
+                                       if (debug_level > 0)
+                                               fprintf(stderr, "(pull parameter '%s')\n", decl->name);
                                        stack_push(&fwd->list, decl);
                                } else
-                                       fprintf(stderr, " missing parameter name\n");
+                                       fprintf(stderr, "WARNING: stack is missing parameter name function %s\n", names);
                        }
 
                        hash_put_bytype(&forward_types, fwd);
                }
 
-               fprintf(stderr, "(push parameter '%s')\n", names);
+               if (debug_level > 0)
+                       fprintf(stderr, "(push parameter '%s')\n", names);
                stack_push(&parameters, node_alloc(type, names));
 
                break; }
@@ -935,7 +961,8 @@ static void plugin_finish_decl(void *event_data, void *user_data) {
 }
 
 static void plugin_finish(void *event_data, void *user_data) {
-       fprintf(stderr, "plugin finish\n");
+       if (debug_level > 0)
+               fprintf(stderr, "plugin finish\n");
        for (struct node *n = todump.head; n; n=n->next) {
                if (COMPLETE_TYPE_P(n->type)) {
                        if (n->name[0]) {
@@ -946,14 +973,15 @@ static void plugin_finish(void *event_data, void *user_data) {
                }
        }
 
-       fprintf(output_file, "# dumped structs:\n");
+       generate("# dumped structs:\n");
        for (struct node *n = dumped.list.head; n; n=n->next)
-               fprintf(output_file, "# %s\n", n->name);
+               generate("# %s\n", n->name);
 
-       fprintf(output_file, ");\n");
+       generate(");\n");
        fclose(output_file);
 
-       fprintf(stderr, "unhandled paramters:\n");
+       if (debug_level > 0)
+               fprintf(stderr, "unhandled paramters:\n");
        list_clear(&parameters);
 }
 
@@ -979,7 +1007,7 @@ int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version
                exit(EXIT_FAILURE);
        }
 
-       fprintf(output_file, "%%data = (\n");
+       generate("%%data = (\n");
 
        register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
        register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
diff --git a/src/generate b/src/generate
deleted file mode 100755 (executable)
index e6eaf8c..0000000
+++ /dev/null
@@ -1,852 +0,0 @@
-#!/usr/bin/perl
-
-# usage
-# generate [-d dir] [-t package] [--enclosing-type type ] [-s struct-root-pattern]* [--struct-file file]* [-c class [-llib]* [-f func-pattern]* [--func-file file]* [-e enum-pattern]]*
-#  -d dir
-#    root output directory
-#  -t package
-#    output package
-#  --enclosing-type type
-#    If supplied, all structures and classes are written to an enclosing class
-#  -s struct-root-pattern
-#    provide one or more patterns for matching structure roots.
-#    all dependencies are automatically included.
-#    if no pattern is provided, all match.
-#  --struct-file file
-#    provide a filename with exact structure names in it.  These are
-#    used as roots(?)
-#  -c class
-#    specify class name to generate
-#  -llib
-#    specify link library used by class
-#  -f func-pattern
-#    function name pattern to include for the current class
-#  --func-file file
-#    point to a filename with exact function names in it, one per line ('#' is a comment).
-#  -e enum-pattern
-#    enum name pattern to include for the current class
-#  --enum-file file
-#    filename with enums in it
-
-# TODO: scan all functions and include any types they use as struct roots
-# TODO: some way to specify external types
-
-@matchStruct = ();
-$meta = "";
-# @classes = ( { name => 'class', match => [ func-pattern, ... ], match_file => [ file, ... ], enum => [ enum-pattern, ... ], enum_file => [ file, ...] } )
-@classes = ();
-%class = ();
-$output = ".";
-# map call signatures to a class name
-%callMap = ();
-$package = "";
-
-while (@ARGV) {
-    my $cmd = shift(@ARGV);
-
-    if ($cmd eq "-f") {
-       my $v = shift(@ARGV);
-       push @{$class{match}}, qr/$v/;
-    } elsif ($cmd eq "--func-file") {
-       my $file = shift(@ARGV);
-
-       push @{$class{match_file}}, $file;
-       push @{$class{match}}, readMatchFile($file);
-    } elsif ($cmd eq "-e") {
-       my $v = shift(@ARGV);
-       push @{$class{enum}}, qr/$v/;
-    } elsif ($cmd eq "--enum-file") {
-       my $file = shift(@ARGV);
-       push @{$class{enum_file}}, $file;
-       push @{$class{enum}}, readMatchFile($file);
-    } elsif ($cmd eq "-s") {
-       my $v = shift(@ARGV);
-       push @matchStruct, qr/$v/;
-    } elsif ($cmd eq "--struct-file") {
-       my $file = shift(@ARGV);
-       push @matchStruct, readMatchFile($file);
-    } elsif ($cmd eq "-t") {
-       $package = shift(@ARGV);
-    } elsif ($cmd eq "-c") {
-       my %new = (
-           name => shift(@ARGV),
-           match => [],
-           match_file => [],
-           enum => [],
-           enum_file => [],
-           libs => []);
-       push @classes, \%new;
-       %class = %new;
-       print "new:\n".Dumper(\%class);
-    } elsif ($cmd =~ m/^-l(.*)/) {
-       push @{$class{libs}}, $1;
-    } elsif ($cmd eq "-d") {
-       $output = shift(@ARGV);
-    } elsif ($cmd eq "--enclosing-type") {
-       $enclosingType = shift(@ARGV);
-    } else {
-       $meta = $cmd;
-    }
-}
-
-use Data::Dumper;
-#exit 0;
-
-require $meta;
-
-# box types for primitives
-%map_box = (
-    "long" => "Long",
-    "int" => "Integer",
-    "short" => "Short",
-    "char" => "Character",
-    "float" => "Float",
-    "double" => "Double",
-    "byte" => "Byte",
-    "void" => "Void"
-    );
-
-sub readMatchFile {
-    my $path = shift @_;
-    my @lines = ();
-
-    open(my $f,"<$path");
-    while (<$f>) {
-       chop;
-       next if m/^#/;
-
-       #push @lines, qr/\^$_\$/;
-       push @lines, $_;
-    }
-    close($f);
-
-    my $all = join ('|', @lines);
-
-    return qr/^($all)$/;
-}
-
-sub camelCase {
-    my $name = shift @_;
-
-    $name =~ s/_(.)/uc($1)/eg;
-
-    return $name;
-}
-
-sub StudlyCaps {
-    my $name = shift @_;
-
-    $name =~ s/^(.)/uc($1)/e;
-    $name =~ s/_(.)/uc($1)/eg;
-
-    return $name;
-}
-
-
-sub structSignature {
-    my %struct = %{shift(@_)};
-    my $union = shift(@_);
-    my $sig = "";
-    my @fields = @{$struct{fields}};
-    my $offset = 0;
-
-    my $inbf = 0;
-    my $bfoffset = 0;
-    my $bfstart = 0;
-    my $bfsig = "";
-
-    for $fi (@fields) {
-       my %field = %{$fi};
-       my $off = $field{offset};
-
-       # bitfields, this only handles 1x u64 bitfield section
-       #  They need to: align to u32/u64
-       #  Group fields into one full u32/u64
-       # TODO: check alignment @ start?
-       # TODO: clean up and complete
-       # TODO: bitfields in unions are probably broken
-       if ($field{ctype} eq 'bitfield') {
-           if ($inbf) {
-               if ($off - $offset) {
-                   $bfsig .= "x";
-                   $bfsig .= ($off - $offset);
-               }
-               $bfsig .= $field{type};
-               $bfsig .= "($field{name})";
-               $offset = $off + $field{size};
-           } else {
-               $inbf = 1;
-               $bfsig = $field{type};
-               $bfsig .= "($field{name})";
-               $offset = $off + $field{size};
-               $bfstart = $field{offset};
-           }
-
-           if ($union) {
-               $inbf = 0;
-
-               if (($offset - $bfstart) == 32) {
-                   $bfsig = "u32=[$bfsig]";
-               } elsif (($offset - $bfstart) < 32) {
-                   $bfsig .= "x";
-                   $bfsig .= 32 - ($offset - $bfstart);
-                   $offset = $bfstart + 32;
-                   $bfsig = "u32=[$bfsig]";
-               } elsif (($offset - $bfstart) == 64) {
-                   $bfsig = "u64=[$bfsig]";
-               } elsif (($offset - $bfstart) < 64) {
-                   $bfsig .= "x";
-                   $bfsig .= 64 - ($offset - $bfstart);
-                   $offset = $bfstart + 64;
-                   $bfsig = "u64=[$bfsig]";
-               }
-
-               $sig .= $bfsig;
-               $sig .= "|" if ($union && $fi != @fields[$#fields]);
-           }
-           next;
-       } elsif ($inbf) {
-           if (($offset - $bfstart) == 32) {
-               $bfsig = "u32=[$bfsig]";
-           } elsif (($offset - $bfstart) < 32) {
-               $bfsig .= "x";
-               $bfsig .= 32 - ($offset - $bfstart);
-               $offset = $bfstart + 32;
-               $bfsig = "u32=[$bfsig]";
-           } elsif (($offset - $bfstart) == 64) {
-               $bfsig = "u64=[$bfsig]";
-           } elsif (($offset - $bfstart) < 64) {
-               $bfsig .= "x";
-               $bfsig .= 64 - ($offset - $bfstart);
-               $offset = $bfstart + 64;
-               $bfsig = "u64=[$bfsig]";
-           }
-           $sig .= $bfsig;
-           $inbf = 0;
-       }
-
-       # skip to next offset if necessary
-       if ($off > $offset) {
-           $sig .= "x";
-           $sig .= ($off - $offset);
-       }
-       $offset = $off + $field{size};
-
-       # normal field processing
-       if ($field{deref}) {
-           my $deref = $field{deref};
-
-           # HACK: function -> Void
-       #   if ($field{debug} eq 'function') {
-       #       $sig .= "u64($field{name}):v";
-       #    } els
-               if ($deref =~ m/^(u\d\d)(:.*)/) {
-               $sig .= "$1($field{name})$2";
-           } else {
-               $sig .= "$deref($field{name})";
-           }
-       } else {
-           if ($field{type} =~ m/(struct|union):(.*)/) {
-               $sig .= "\${$2}";
-           } elsif ($field{type} =~ m/([iuf])(\d+)/) {
-               $sig .= $1;
-               $sig .= $2;
-           } elsif ($field{type} eq 'void') {
-               $sig .= "v";
-           } elsif ($field{type} eq 'enum') {
-               # FIXME: set type in compiler
-               $sig .= "u32";
-           }
-
-           $sig .= "($field{name})";
-       }
-
-       $sig .= "|" if ($union && $fi != @fields[$#fields]);
-    }
-
-    # finish any trailing bitfield
-    # TODO: cleanup
-    if ($inbf) {
-       if (($offset - $bfstart) == 32) {
-           $bfsig = "u32=[$bfsig]";
-       } elsif (($offset - $bfstart) < 32) {
-           $bfsig .= "x";
-           $bfsig .= 32 - ($offset - $bfstart);
-           $offset = $bfstart + 32;
-           $bfsig = "u32=[$bfsig]";
-       } elsif (($offset - $bfstart) == 64) {
-           $bfsig = "u64=[$bfsig]";
-       } elsif (($offset - $bfstart) < 64) {
-           $bfsig .= "x";
-           $bfsig .= 64 - ($offset - $bfstart);
-           $offset = $bfstart + 64;
-           $bfsig = "u64=[$bfsig]";
-       }
-       #$bfsig .= "]";
-       $sig .= $bfsig;
-    }
-
-    return "[".$sig."]";
-}
-
-sub funcSignature {
-    my %func = %{shift(@_)};
-    my $sig = "";
-    my @params = @{$func{arguments}};
-
-    for $pi (@params) {
-       my %param = %{$pi};
-
-       if ($param{deref}) {
-           # HACK: function to void
-           if ($param{debug} eq "function") {
-               $sig .= "u64:v";
-           } else {
-               $sig .= $param{deref};
-           }
-       } else {
-           if ($param{type} =~ m/struct:(.*)/) {
-               $sig .= "\${$1}";
-           } elsif ($param{type} =~ m/([iuf])(\d*)/) {
-               $sig .= $1;
-               $sig .= $2;
-           } elsif ($param{type} eq "void") {
-               $sig .= "v";
-           }
-       }
-    }
-
-    my %result = %{$func{result}};
-    my $ret = "";
-
-    if ($result{deref}) {
-       $ret .= $result{deref};
-    } else {
-       if ($result{type} =~ m/^struct:(.*)/) {
-           $ret .= "\${$1}";
-       } elsif ($result{type} =~ m/^([iuf])(\d+)/) {
-           $ret .= $1;
-           $ret .= $2;
-       } elsif ($result{type} eq "void") {
-           $ret .= "v";
-       }
-    }
-
-    return "($sig)$ret";
-}
-
-sub deref {
-    my $type = shift @_;
-    my $ref = shift @_;
-
-    while ($ref) {
-       if ($ref =~ m/\[\d*(.*)\]/) {
-           my $sub = deref($type, $1);
-
-           return "Array<$sub>";
-       } elsif ($ref =~ m/^u64:(.*)/) {
-           $type = "Pointer<$type>";
-           $ref = $1;
-       } else {
-           last;
-       }
-    }
-    return $type;
-}
-
-sub typeToJava {
-    my %param = %{shift(@_)};
-    my $type = $param{type};
-    my $ref = $param{deref};
-
-    if ($type =~ m/^struct:(.*)/) {
-       $type = StudlyCaps($1);
-    } elsif ($type =~ m/call:/) {
-       # this re-writes ref to remove one pointer-to as the Callback absorbs it.
-       $type = "Callback<".$callMap{$type}.">";
-       $type || die ("No mapping for type ".Dumper(\%param));
-       $ref =~ s/^u(32|64)://;
-    } elsif ($type =~ m/^enum:(.*)/) {
-       # TODO: other enum options
-       $type = "int";
-    } elsif ($type eq "void") {
-       $type = "void";
-    } elsif ($type =~ m/^([iu])(\d*)/) {
-       my $sign = $1;
-       my $size = $2;
-
-       if ($size <= 8) {
-           $type = "byte";
-       } elsif ($size <= 16) {
-           if ($sign eq "i") {
-               $type = "short";
-           } else {
-               $type = "char";
-           }
-       } elsif ($size <= 32) {
-           $type = "int";
-       } else {
-           $type = "long";
-       }
-    } elsif ($type =~ m/^[f](\d*)$/) {
-       my $size = $1;
-
-       if ($size == 32) {
-           $type = "float";
-       } elsif ($size == 64) {
-           $type = "double";
-       }
-    }
-
-    if ($ref) {
-       $type = $map_box{$type} if ($map_box{$type});
-       $type = deref($type, $ref);
-    }
-
-    return $type;
-}
-
-sub testMatch {
-    my $name = shift @_;
-
-    if (@_) {
-       for $pat (@_) {
-           if ($name =~ /$pat/) {
-               return 1;
-           }
-       }
-       return 0;
-    } else {
-       return 1;
-    }
-}
-
-# find all matching structures and then all that they require
-sub findStructs {
-    my %all = %{shift @_};
-    my @match = @_;
-    my @stack = grep {
-       my %e = %{$all{$_}};
-       $e{type} =~ m/(struct|union)/ && testMatch($e{name}, @match);
-    } keys %all;
-    my %visit = ();
-
-    while (@stack) {
-       my $test = shift @stack;
-
-       if (!$visit{$test}) {
-           my %struct = %{$all{$test}};
-
-           $visit{$test} = 1;
-
-           if (%struct) {
-               print "class: $struct{name}\n";
-               # find all types this one uses
-               for $f (@{$struct{fields}}) {
-                   my %field = %{$f};
-
-                   if ($field{type} =~ m/^(struct|union):(.*)/) {
-                       if (!$set{$field{type}}) {
-                           $set{$field{type}} = $all{$field{type}};
-                           push @stack, $field{type};
-                       }
-                   }
-               }
-           } else {
-               # this is an anon type, typically used for handles
-               $test =~ m/^(struct|union):(.*)/;
-               print " anon: $2\n";
-               my %rec = (
-                   type => 'struct',
-                   name => $2,
-                   size => 0
-                   );
-               $data{$test} = \%rec;
-           }
-       }
-    }
-    return keys %visit;
-}
-
-sub findDefinition {
-    my %all = %{shift @_};
-    my $type = shift @_;
-    my @match = @_;
-    my @stack = grep {
-       my %e = %{$all{$_}};
-       $e{type} eq $type && testMatch($e{name}, @match);
-    } keys %all;
-
-    return @stack;
-}
-
-# ######################################################################
-
-# setup section
-
-# find all classes used by functions
-my %roots = ();
-for $c (@classes) {
-    my %class = %{$c};
-    my @libs = @{$class{libs}};
-    my @match = @{$class{match}};
-
-    for $k (findDefinition(\%data, 'func', @match)) {
-       my %func = %{$data{$k}};
-       my @params = @{$func{arguments}};
-
-       for $pi (@params) {
-           my %param = %{$pi};
-
-           if ($param{type} =~ m/^(struct|union):(.*)/) {
-               $roots{$2} = 1;
-           }
-       }
-
-       my %result = %{$func{result}};
-
-       if ($result{type} =~ m/^(struct|union):(.*)/) {
-           $roots{$2} = 1;
-       }
-    }
-}
-
-# add roots for any types used by calls
-# FIXME: only include ones used elsewhere
-for $k (grep { $_ =~ m/^call:/n } keys %data) {
-    my %func = %{$data{$k}};
-    my @params = @{$func{arguments}};
-
-    for $pi (@params) {
-       my %param = %{$pi};
-
-       if ($param{type} =~ m/^(struct|union):(.*)/) {
-           $roots{$2} = 1;
-       }
-    }
-
-    my %result = %{$func{result}};
-
-    if ($result{type} =~ m/^(struct|union):(.*)/) {
-       $roots{$2} = 1;
-    }
-}
-
-# Create anonymous structs for anything missing
-for $k (keys %roots) {
-    my $s = 'struct:'.$k;
-    my $u = 'union:'.$k;
-
-    if (!$data{$u} && !$data{$s}) {
-       print " anon: $s\n";
-       my %rec = (
-           type => 'struct',
-           name => $k,
-           size => 0
-           );
-       $data{$s} = \%rec;
-    }
-}
-
-$all = join ('|', keys %roots);
-if ($all) {
-    push @matchStruct, qr/^($all)$/;
-}
-print "structures:\n";
-print Dumper(@matchStruct);
-
-# make a map for all callbacks (call: type) to generated names
-for $c (grep { $_ =~ m/^call:/n } keys %data) {
-    my $name = $c;
-
-    print "$c\n";
-    # enum maybe to int?
-
-    $name =~ s/^call:/Call/;
-    $name =~ s/\$\{[^\}]*\}/L/g;
-    $name =~ s/[ui](64|32):/p/g;
-    $name =~ s/[ui]64/J/g;
-    $name =~ s/[ui]32/I/g;
-    $name =~ s/[ui]8/B/g;
-    $name =~ s/f32/F/g;
-    $name =~ s/f64/D/g;
-    $name =~ s/[\[\]\(\)]/_/g;
-
-    $callMap{$c} = "$name";
-}
-
-print "call mappings\n";
-print Dumper(\%callMap);
-
-# ######################################################################
-# Start output
-my $dst;
-
-use File::Basename;
-use File::Path qw(make_path);
-
-if ($package ne "") {
-    $packagePrefix = $package.".";
-}
-
-if ($enclosingType) {
-    my $classname = $packagePrefix.$enclosingType;
-
-    $classname =~ s@\.@/@g;
-
-    my $path = $output."/".$classname.".java";
-    my $dir = dirname($path);
-    my $class = basename($path, ".java");
-
-    print "path $path\n";
-    print "dirname $dir\n";
-
-    make_path($dir);
-    open ($dst, ">$path");
-
-    if ($package ne "") {
-       print $dst "package $package;\n";
-    }
-
-    print $dst <<END;
-import java.foreign.Libraries;
-import java.foreign.annotations.*;
-import java.foreign.memory.*;
-import java.lang.invoke.MethodHandles;
-END
-    print $dst "public class $class {\n";
-}
-
-# Dump structures
-for $k (findStructs(\%data, @matchStruct)) {
-    my %struct = %{$data{$k}};
-    my @fields = @{$struct{fields}};
-    my $signature = structSignature(\%struct, ($struct{type} eq "union"));
-    my $name = StudlyCaps($struct{name});
-
-    if (!$enclosingType) {
-       my $classname = $packagePrefix.$name;
-
-       open ($dst, ">$path");
-       $classname =~ s@\.@/@g;
-
-       my $path = $output."/".$classname.".java";
-       my $dir = dirname($path);
-       my $class = basename($path, ".java");
-       make_path($dir);
-       open ($dst, ">$path");
-
-       if ($package ne "") {
-           print $dst "package $package;\n";
-       }
-       print $dst <<END;
-import java.foreign.annotations.*;
-import java.foreign.memory.*;
-END
-    }
-
-    # any in-line structures need to be added to the resolutionContext
-    # TODO: only include actual inline, not pointers
-    my %resolve = ();
-    for $fi (@fields) {
-       my %field = %{$fi};
-
-       if ($field{type} =~ m/^(struct|union):(.*)/) {
-           $resolve{StudlyCaps($2).".class"} = 1;
-       }
-    }
-    my $resolve = join (",", keys %resolve);
-
-    print $dst "\@NativeStruct(value=\"$signature($struct{name})\"";
-    print $dst ", resolutionContext={$resolve}" if ($resolve);
-    print $dst ")\n";
-    print $dst "public interface $name extends Struct<$name> {\n";
-
-    for $fi (@fields) {
-       my %field = %{$fi};
-       my $type = typeToJava(\%field);
-       my $cc = StudlyCaps($field{name});
-
-       print $dst "\t\@NativeGetter(value=\"$field{name}\")\n";
-       print $dst "\tpublic $type get$cc();\n";
-
-       print $dst "\t\@NativeSetter(value=\"$field{name}\")\n";
-       print $dst "\tpublic void set$cc($type value);\n";
-    }
-
-    print $dst "}\n";
-
-    if (!$enclosingType) {
-       close($dst);
-    }
-}
-
-# Dump classes for library linkage
-for $c (@classes) {
-    my %class = %{$c};
-    my @libs = @{$class{libs}};
-    my @match = @{$class{match}};
-
-    if (!$enclosingType) {
-       my $classname = $packagePrefix.$class{name};
-
-       open ($dst, ">$path");
-       $classname =~ s@\.@/@g;
-
-       my $path = $output."/".$classname.".java";
-       my $dir = dirname($path);
-       my $class = basename($path, ".java");
-       make_path($dir);
-       open ($dst, ">$path");
-
-       if ($package ne "") {
-           print $dst "package $package;\n";
-       }
-       print $dst <<END;
-import java.foreign.Libraries;
-import java.foreign.annotations.*;
-import java.foreign.memory.*;
-import java.lang.invoke.MethodHandles;
-END
-    }
-
-    print $dst "\@NativeHeader(libraries={";
-    print $dst join(",", map { "\"$_\"" } @libs);
-    print $dst "})\n";
-    print $dst "public interface $class{name} {\n";
-
-    # enums to ints
-    # TODO: interfaces?
-    # TODO: static lib class?
-    # typedef enums might appear twice in the data, so ignore duplicates
-    # also, some api's have multiple definitions (?)
-    my %visited = ();
-    my @match_enum = @{$class{enum}};
-    for $k (sort(findDefinition(\%data, 'enum', @match_enum))) {
-       my %enum = %{$data{$k}};
-       my @values = @{$enum{values}};
-       my $type = "int";
-
-       if ($enum{value_type} =~ m/^[ui](\d+)/) {
-           $type = "long" if ($1 > 32)
-       }
-
-       print $dst "\n\t// enum $enum{name}\n";
-       for $vi (@values) {
-           my %value = %{$vi};
-
-           if (!$visited{$value{label}}) {
-               #print $dst "\tpublic static final $type $value{label} = ($type)$value{value};\n";
-               print $dst "\tpublic static final $type $value{label} = $value{value};\n";
-               $visited{$value{label}} = 1;
-           }
-       }
-    }
-
-    # functions
-    print "class $class{name} -> match:\n".Dumper(\@match);
-    
-    for $k (sort(findDefinition(\%data, 'func', @match))) {
-       my %func = %{$data{$k}};
-       my @params = @{$func{arguments}};
-       my $signature = funcSignature(\%func);
-       my $name = ($func{name});
-       my $result = typeToJava(\%{$func{result}});
-
-       print $dst "\n\t\@NativeFunction(value=\"$signature\")\n";
-       print $dst "\tpublic $result $name(";
-
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToJava($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-
-       print $dst ");\n";
-    }
-
-    print $dst "\n";
-    print $dst "\tpublic static final $class{name} bind = Libraries.bind(MethodHandles.lookup(), $class{name}.class);\n";
-
-    print $dst "}\n";
-
-    if (!$enclosingType) {
-       close($dst);
-    }
-}
-
-# Dump callbacks
-# TODO: only those used by classes and functions that were exported
-for $c (keys %callMap) {
-    my %call = %{$data{$c}};
-    my $name = $callMap{$c};
-    my @params = @{$call{arguments}};
-    my $result = typeToJava(\%{$call{result}});
-
-    if (!$enclosingType) {
-       my $classname = $packagePrefix.$name;
-
-       open ($dst, ">$path");
-       $classname =~ s@\.@/@g;
-
-       my $path = $output."/".$classname.".java";
-       my $dir = dirname($path);
-       my $class = basename($path, ".java");
-       make_path($dir);
-       open ($dst, ">$path");
-
-       if ($package ne "") {
-           print $dst "package $package;\n";
-       }
-       print $dst <<END;
-import java.foreign.Libraries;
-import java.foreign.annotations.*;
-import java.foreign.memory.*;
-END
-    }
-
-    # any in-line structures need to be added to the resolutionContext
-    # TODO: only include actual inline, not pointers
-    my %resolve = ();
-    my @list = @params;
-    unshift(@list,$call{result});
-    for $pi (@list) {
-       my %param = %{$pi};
-
-       if ($param{type} =~ m/^(struct|union):(.*)/) {
-           $resolve{StudlyCaps($2).".class"} = 1;
-       }
-    }
-    my $resolve = join (",", keys %resolve);
-
-    # FIXME: use something other than name to store this
-    print $dst "\@FunctionalInterface\n";
-    print $dst "\@NativeCallback(value=\"$call{name}\"";
-    print $dst ", resolutionContext={$resolve}" if ($resolve);
-    print $dst ")\n";
-    print $dst "public interface $name {\n";
-    print $dst "\tpublic $result fn(";
-
-    for $pi (@params) {
-       my %param = %{$pi};
-       my $type = typeToJava($pi);
-
-       print $dst "$type $param{name}";
-       print $dst ", " if ($pi != $params[$#params]);
-    }
-
-    print $dst ");\n";
-    print $dst "}\n";
-
-    if (!$enclosingType) {
-       close($dst);
-    }
-}
-
-# Finish off
-if ($enclosingType) {
-    print $dst "}\n";
-    close($dst);
-}
diff --git a/src/generate-api b/src/generate-api
deleted file mode 100755 (executable)
index 86f5296..0000000
+++ /dev/null
@@ -1,1395 +0,0 @@
-#!/usr/bin/perl
-
-# replace a datatype with another, do not generate any code for it
-# -r name=new
-
-@matchStruct = ();
-$meta = "";
-# @classes = ( { name => 'class', match => [ func-pattern, ... ], match_file => [ file, ... ], enum => [ enum-pattern, ... ], enum_file => [ file, ...] } )
-@classes = ();
-%class = ();
-$output = ".";
-# map call signatures to a class name
-%callMap = ();
-$package = "";
-%replace = ();
-# calls take raw types and throw Throwable
-$rawCalls = 0;
-
-while (@ARGV) {
-    my $cmd = shift(@ARGV);
-
-    if ($cmd eq "-f") {
-       my $v = shift(@ARGV);
-       push @{$class{match}}, qr/$v/;
-    } elsif ($cmd eq "--func-file") {
-       my $file = shift(@ARGV);
-
-       push @{$class{match_file}}, $file;
-       push @{$class{match}}, readMatchFile($file);
-    } elsif ($cmd eq "-e") {
-       my $v = shift(@ARGV);
-       push @{$class{enum}}, qr/$v/;
-    } elsif ($cmd eq "--enum-file") {
-       my $file = shift(@ARGV);
-       push @{$class{enum_file}}, $file;
-       push @{$class{enum}}, readMatchFile($file);
-    } elsif ($cmd eq "-s") {
-       my $v = shift(@ARGV);
-       push @matchStruct, qr/$v/;
-    } elsif ($cmd eq "--struct-file") {
-       my $file = shift(@ARGV);
-       push @matchStruct, readMatchFile($file);
-    } elsif ($cmd eq "-r") {
-       my $v = shift(@ARGV);
-
-       $v =~ m/(.*)=(.*)/;
-       $replace{$1} = $2;
-    } elsif ($cmd eq "--raw-calls") {
-       $rawCalls = 1;
-    } elsif ($cmd eq "-t") {
-       $package = shift(@ARGV);
-    } elsif ($cmd eq "-c") {
-       my %new = (
-           name => shift(@ARGV),
-           match => [],
-           match_file => [],
-           enum => [],
-           enum_file => [],
-           libs => []);
-       push @classes, \%new;
-       %class = %new;
-       print "new:\n".Dumper(\%class);
-    } elsif ($cmd =~ m/^-l(.*)/) {
-       push @{$class{libs}}, $1;
-    } elsif ($cmd eq "-d") {
-       $output = shift(@ARGV);
-    } elsif ($cmd eq "--enclosing-type") {
-       $enclosingType = shift(@ARGV);
-    } else {
-       $meta = $cmd;
-    }
-}
-
-$importPointer = "import api.Native.Pointer;" if (!$rawCalls);
-
-print "import poirnter: $importPointer\n";
-use Data::Dumper;
-
-require $meta;
-
-# box types for primitives
-%map_box = (
-    "long" => "Long",
-    "int" => "Integer",
-    "short" => "Short",
-    "char" => "Character",
-    "float" => "Float",
-    "double" => "Double",
-    "byte" => "Byte",
-    "void" => "Void"
-    );
-
-sub readMatchFile {
-    my $path = shift @_;
-    my @lines = ();
-
-    open(my $f,"<$path");
-    while (<$f>) {
-       chop;
-       next if m/^#/;
-
-       #push @lines, qr/\^$_\$/;
-       push @lines, $_;
-    }
-    close($f);
-
-    my $all = join ('|', @lines);
-
-    return qr/^($all)$/;
-}
-
-sub camelCase {
-    my $name = shift @_;
-
-    $name =~ s/_(.)/uc($1)/eg;
-
-    return $name;
-}
-
-sub StudlyCaps {
-    my $name = shift @_;
-
-    # hack, or good spot for it?
-    return $replace{$name} if $replace{$name};
-
-    $name =~ s/^(.)/uc($1)/e;
-    $name =~ s/_(.)/uc($1)/eg;
-
-    return $name;
-}
-
-
-sub structSignature {
-    my %struct = %{shift(@_)};
-    my $union = shift(@_);
-    my $sig = "";
-    my @fields = @{$struct{fields}};
-    my $offset = 0;
-
-    my $inbf = 0;
-    my $bfoffset = 0;
-    my $bfstart = 0;
-    my $bfsig = "";
-
-    for $fi (@fields) {
-       my %field = %{$fi};
-       my $off = $field{offset};
-
-       # bitfields, this only handles 1x u64 bitfield section
-       #  They need to: align to u32/u64
-       #  Group fields into one full u32/u64
-       # TODO: check alignment @ start?
-       # TODO: clean up and complete
-       # TODO: bitfields in unions are probably broken
-       if ($field{ctype} eq 'bitfield') {
-           if ($inbf) {
-               if ($off - $offset) {
-                   $bfsig .= "x";
-                   $bfsig .= ($off - $offset);
-               }
-               $bfsig .= $field{type};
-               $bfsig .= "($field{name})";
-               $offset = $off + $field{size};
-           } else {
-               $inbf = 1;
-               $bfsig = $field{type};
-               $bfsig .= "($field{name})";
-               $offset = $off + $field{size};
-               $bfstart = $field{offset};
-           }
-
-           if ($union) {
-               $inbf = 0;
-
-               if (($offset - $bfstart) == 32) {
-                   $bfsig = "u32=[$bfsig]";
-               } elsif (($offset - $bfstart) < 32) {
-                   $bfsig .= "x";
-                   $bfsig .= 32 - ($offset - $bfstart);
-                   $offset = $bfstart + 32;
-                   $bfsig = "u32=[$bfsig]";
-               } elsif (($offset - $bfstart) == 64) {
-                   $bfsig = "u64=[$bfsig]";
-               } elsif (($offset - $bfstart) < 64) {
-                   $bfsig .= "x";
-                   $bfsig .= 64 - ($offset - $bfstart);
-                   $offset = $bfstart + 64;
-                   $bfsig = "u64=[$bfsig]";
-               }
-
-               $sig .= $bfsig;
-               $sig .= "|" if ($union && $fi != @fields[$#fields]);
-           }
-           next;
-       } elsif ($inbf) {
-           if (($offset - $bfstart) == 32) {
-               $bfsig = "u32=[$bfsig]";
-           } elsif (($offset - $bfstart) < 32) {
-               $bfsig .= "x";
-               $bfsig .= 32 - ($offset - $bfstart);
-               $offset = $bfstart + 32;
-               $bfsig = "u32=[$bfsig]";
-           } elsif (($offset - $bfstart) == 64) {
-               $bfsig = "u64=[$bfsig]";
-           } elsif (($offset - $bfstart) < 64) {
-               $bfsig .= "x";
-               $bfsig .= 64 - ($offset - $bfstart);
-               $offset = $bfstart + 64;
-               $bfsig = "u64=[$bfsig]";
-           }
-           $sig .= $bfsig;
-           $inbf = 0;
-       }
-
-       # skip to next offset if necessary
-       if ($off > $offset) {
-           $sig .= "x";
-           $sig .= ($off - $offset);
-       }
-       $offset = $off + $field{size};
-
-       # normal field processing
-       if ($field{deref}) {
-           my $deref = $field{deref};
-
-           # HACK: function -> Void
-       #   if ($field{debug} eq 'function') {
-       #       $sig .= "u64($field{name}):v";
-       #    } els
-               if ($deref =~ m/^(u\d\d)(:.*)/) {
-               $sig .= "$1($field{name})$2";
-           } else {
-               $sig .= "$deref($field{name})";
-           }
-       } else {
-           if ($field{type} =~ m/(struct|union):(.*)/) {
-               $sig .= "\${$2}";
-           } elsif ($field{type} =~ m/([iuf])(\d+)/) {
-               $sig .= $1;
-               $sig .= $2;
-           } elsif ($field{type} eq 'void') {
-               $sig .= "v";
-           } elsif ($field{type} eq 'enum') {
-               # FIXME: set type in compiler
-               $sig .= "u32";
-           }
-
-           $sig .= "($field{name})";
-       }
-
-       $sig .= "|" if ($union && $fi != @fields[$#fields]);
-    }
-
-    # finish any trailing bitfield
-    # TODO: cleanup
-    if ($inbf) {
-       if (($offset - $bfstart) == 32) {
-           $bfsig = "u32=[$bfsig]";
-       } elsif (($offset - $bfstart) < 32) {
-           $bfsig .= "x";
-           $bfsig .= 32 - ($offset - $bfstart);
-           $offset = $bfstart + 32;
-           $bfsig = "u32=[$bfsig]";
-       } elsif (($offset - $bfstart) == 64) {
-           $bfsig = "u64=[$bfsig]";
-       } elsif (($offset - $bfstart) < 64) {
-           $bfsig .= "x";
-           $bfsig .= 64 - ($offset - $bfstart);
-           $offset = $bfstart + 64;
-           $bfsig = "u64=[$bfsig]";
-       }
-       #$bfsig .= "]";
-       $sig .= $bfsig;
-    }
-
-    return "[".$sig."]";
-}
-
-sub funcSignature {
-    my %func = %{shift(@_)};
-    my $sig = "";
-    my @params = @{$func{arguments}};
-
-    for $pi (@params) {
-       my %param = %{$pi};
-
-       if ($param{deref}) {
-           # HACK: function to void
-           if ($param{debug} eq "function") {
-               $sig .= "u64:v";
-           } else {
-               $sig .= $param{deref};
-           }
-       } else {
-           if ($param{type} =~ m/struct:(.*)/) {
-               $sig .= "\${$1}";
-           } elsif ($param{type} =~ m/([iuf])(\d*)/) {
-               $sig .= $1;
-               $sig .= $2;
-           } elsif ($param{type} eq "void") {
-               $sig .= "v";
-           }
-       }
-    }
-
-    my %result = %{$func{result}};
-    my $ret = "";
-
-    if ($result{deref}) {
-       $ret .= $result{deref};
-    } else {
-       if ($result{type} =~ m/^struct:(.*)/) {
-           $ret .= "\${$1}";
-       } elsif ($result{type} =~ m/^([iuf])(\d+)/) {
-           $ret .= $1;
-           $ret .= $2;
-       } elsif ($result{type} eq "void") {
-           $ret .= "v";
-       }
-    }
-
-    return "($sig)$ret";
-}
-
-sub deref {
-    my $type = shift @_;
-    my $ref = shift @_;
-
-    while ($ref) {
-       if ($ref =~ m/\[\d*(.*)\]/) {
-           my $sub = deref($type, $1);
-
-           return "Array<$sub>";
-       } elsif ($ref =~ m/^u64:\$/) {
-           # ignore penultimate pointer?
-           last;
-       } elsif ($ref =~ m/^u64:(.*)/) {
-           $type = "Pointer<$type>";
-           $ref = $1;
-       } else {
-           last;
-       }
-    }
-    return $type;
-}
-
-sub typeToJava {
-    my %param = %{shift(@_)};
-    my $type = $param{type};
-    my $ref = $param{deref};
-
-    if ($type =~ m/^struct:(.*)/) {
-       $type = $replace{$1} ? $replace{$1} : StudlyCaps($1);
-    } elsif ($type =~ m/call:/) {
-       # this re-writes ref to remove one pointer-to as the Callback absorbs it.
-       $type = "Callback<".$callMap{$type}.">";
-       $type || die ("No mapping for type ".Dumper(\%param));
-       $ref =~ s/^u(32|64)://;
-    } elsif ($type =~ m/^enum:(.*)/) {
-       # TODO: other enum options
-       $type = "int";
-    } elsif ($type eq "void") {
-       $type = "void";
-    } elsif ($type =~ m/^([iu])(\d*)/) {
-       my $sign = $1;
-       my $size = $2;
-
-       if ($size <= 8) {
-           $type = "byte";
-       } elsif ($size <= 16) {
-           if ($sign eq "i") {
-               $type = "short";
-           } else {
-               $type = "char";
-           }
-       } elsif ($size <= 32) {
-           $type = "int";
-       } else {
-           $type = "long";
-       }
-    } elsif ($type =~ m/^[f](\d*)$/) {
-       my $size = $1;
-
-       if ($size == 32) {
-           $type = "float";
-       } elsif ($size == 64) {
-           $type = "double";
-       }
-    }
-
-    if ($ref) {
-       $type = $map_box{$type} if ($map_box{$type});
-       $type = deref($type, $ref);
-    }
-
-    return $type;
-}
-
-sub typeToRaw {
-    my %param = %{shift(@_)};
-    my $type = $param{type};
-    my $ref = $param{deref};
-
-    my $type = typeToJava(\%param);
-
-    if ($ref =~ m/^u64:/) {
-       return "MemoryAddress";
-    } elsif ($type =~ m/^(struct|union):/) {
-       return "MemorySegment";
-    } else {
-       return $type;
-    }
-
-    # hackity hack
-#    if ($type =~ "(Pointer|Array|Callback)") {
-#      return "MemoryAddress";
-#    } elsif ($type =~ m/^[A-Z]/) {
-#      return "MemorySegment";
-#    } else {
-#      return $type;
-#    }
-}
-
-sub testMatch {
-    my $name = shift @_;
-
-    if (@_) {
-       for $pat (@_) {
-           if ($name =~ /$pat/) {
-               return 1;
-           }
-       }
-       return 0;
-    } else {
-       return 1;
-    }
-}
-
-# find all matching structures and then all that they require
-sub findStructs {
-    my %all = %{shift @_};
-    my @match = @_;
-    my @stack = grep {
-       my %e = %{$all{$_}};
-       $e{type} =~ m/(struct|union)/ && !$replace{$e{name}} && testMatch($e{name}, @match);
-    } keys %all;
-    my %visit = ();
-
-    while (@stack) {
-       my $test = shift @stack;
-
-       if (!$visit{$test}) {
-           my %struct = %{$all{$test}};
-
-           $visit{$test} = 1;
-
-           if (%struct) {
-               print "class: $struct{name}\n";
-               # find all types this one uses
-               for $f (@{$struct{fields}}) {
-                   my %field = %{$f};
-
-                   if ($field{type} =~ m/^(struct|union):(.*)/) {
-                       if (!$replace{$1} && !$set{$field{type}}) {
-                           $set{$field{type}} = $all{$field{type}};
-                           push @stack, $field{type};
-                       }
-                   }
-               }
-           } else {
-               # this is an anon type, typically used for handles
-               $test =~ m/^(struct|union):(.*)/;
-               if (!$replace{$2}) {
-                   print " anon: $2\n";
-                   my %rec = (
-                       type => 'struct',
-                       name => $2,
-                       size => 0
-                       );
-                   $data{$test} = \%rec;
-               }
-           }
-       }
-    }
-    return grep { !$replace{$_} } keys(%visit);
-}
-
-sub findDefinition {
-    my %all = %{shift @_};
-    my $type = shift @_;
-    my @match = @_;
-    my @stack = grep {
-       my %e = %{$all{$_}};
-       $e{type} eq $type && testMatch($e{name}, @match);
-    } keys %all;
-
-    return @stack;
-}
-
-sub arrayInfo {
-    my $ref = shift @_;
-    my %info = (
-       dims => [],
-       );
-
-    print "array $ref\n";
-    while ($ref =~ m/^\[(\d*)(.*)\]$/) {
-       push @{$info{dims}}, $1;
-       $ref = $2;
-       print "dim $1 -, '$2'\n";
-    }
-    $info{deref} = $ref;
-
-    return %info;
-}
-
-# ######################################################################
-
-# setup section
-
-# find all classes used by functions
-my %roots = ();
-for $c (@classes) {
-    my %class = %{$c};
-    my @libs = @{$class{libs}};
-    my @match = @{$class{match}};
-
-    for $k (findDefinition(\%data, 'func', @match)) {
-       my %func = %{$data{$k}};
-       my @params = @{$func{arguments}};
-
-       for $pi (@params) {
-           my %param = %{$pi};
-
-           if ($param{type} =~ m/^(struct|union):(.*)/) {
-               $roots{$2} = 1;
-           }
-       }
-
-       my %result = %{$func{result}};
-
-       if ($result{type} =~ m/^(struct|union):(.*)/) {
-           $roots{$2} = 1;
-       }
-    }
-}
-
-# add roots for any types used by calls
-# FIXME: only include ones used elsewhere
-for $k (grep { $_ =~ m/^call:/n } keys %data) {
-    my %func = %{$data{$k}};
-    my @params = @{$func{arguments}};
-
-    for $pi (@params) {
-       my %param = %{$pi};
-
-       if ($param{type} =~ m/^(struct|union):(.*)/) {
-           $roots{$2} = 1;
-       }
-    }
-
-    my %result = %{$func{result}};
-
-    if ($result{type} =~ m/^(struct|union):(.*)/) {
-       $roots{$2} = 1;
-    }
-}
-
-# Create anonymous structs for anything missing
-for $k (keys %roots) {
-    my $s = 'struct:'.$k;
-    my $u = 'union:'.$k;
-
-    if (!$data{$u} && !$data{$s} && !$replace{$k}) {
-       print " xanon: $s\n";
-       my %rec = (
-           type => 'struct',
-           name => $k,
-           size => 0
-           );
-       $data{$s} = \%rec;
-    }
-}
-
-$all = join ('|', keys %roots);
-if ($all) {
-    push @matchStruct, qr/^($all)$/;
-}
-print "structures:\n";
-print Dumper(@matchStruct);
-
-# make a map for all callbacks (call: type) to generated names
-for $c (grep { $_ =~ m/^call:/n } keys %data) {
-    my $name = $c;
-
-    print "$c\n";
-    # enum maybe to int?
-
-    $name =~ s/^call:/Call/;
-    if ($rawCalls) {
-       $name =~ s/\$\{([^\}]*)\}/L/g;
-    } else {
-       while ($name =~ m/\$\{([^\}]*)\}/) {
-           my $x = $1;
-           if ($replace{$x}) {
-               $x = $replace{$x};
-           } else {
-               $x = StudlyCaps($x);
-           }
-           $name =~ s/\$\{([^\}]*)\}/L$x/;
-       }
-    }
-    $name =~ s/[ui](64|32):/p/g;
-    $name =~ s/[ui]64/J/g;
-    $name =~ s/[ui]32/I/g;
-    $name =~ s/[ui]8/B/g;
-    $name =~ s/f32/F/g;
-    $name =~ s/f64/D/g;
-    $name =~ s/[\[\]\(\)]/_/g;
-
-    $callMap{$c} = "$name";
-}
-
-print "call mappings\n";
-print Dumper(\%callMap);
-
-# ######################################################################
-# Start output
-my $dst;
-
-use File::Basename;
-use File::Path qw(make_path);
-
-if ($package ne "") {
-    $packagePrefix = $package.".";
-}
-
-if ($enclosingType) {
-    my $classname = $packagePrefix.$enclosingType;
-
-    $classname =~ s@\.@/@g;
-
-    my $path = $output."/".$classname.".java";
-    my $dir = dirname($path);
-    my $class = basename($path, ".java");
-
-    print "path $path\n";
-    print "dirname $dir\n";
-
-    make_path($dir);
-    open ($dst, ">$path");
-
-    if ($package ne "") {
-       print $dst "package $package;\n";
-    }
-
-    print $dst <<END;
-import java.foreign.Libraries;
-import java.lang.invoke.MethodHandles;
-import jdk.incubator.foreign.*;
-import api.Native;
-$importPointer
-END
-    print $dst "public class $class {\n";
-}
-
-# ######################################################################
-# This is work in progress, aka a total fucking mess
-# Dump structures
-for $k (findStructs(\%data, @matchStruct)) {
-    my %struct = %{$data{$k}};
-    my @fields = @{$struct{fields}};
-    my $signature = structSignature(\%struct, ($struct{type} eq "union"));
-    my $name = StudlyCaps($struct{name});
-
-    if (!$enclosingType) {
-       my $classname = $packagePrefix.$name;
-
-       open ($dst, ">$path");
-       $classname =~ s@\.@/@g;
-
-       my $path = $output."/".$classname.".java";
-       my $dir = dirname($path);
-       my $class = basename($path, ".java");
-       make_path($dir);
-       open ($dst, ">$path");
-
-       if ($package ne "") {
-           print $dst "package $package;\n";
-       }
-       print $dst <<END;
-import jdk.incubator.foreign.*;
-import api.Native;
-$importPointer
-
-END
-    }
-
-    print $dst "public class $name extends Native {\n";
-
-    print $dst "\tpublic $name(MemoryAddress p) {\n";
-    print $dst "\t\tsuper(p);\n";
-    print $dst "\t}\n";
-
-    for $fi (@fields) {
-       my %field = %{$fi};
-       my $type = typeToJava(\%field);
-       my $cc = StudlyCaps($field{name});
-
-       if ($field{deref} =~ m/^\[/) {
-           # array
-           my %info = arrayInfo($field{deref});
-           my @dims = @{$info{dims}};
-           $info{type} = $field{type};
-           my $atype = typeToJava(\%info);
-           my @strides = ();
-
-           my $stride = 1;
-           for $dim (reverse(0 .. $#dims)) {
-               push @strides,$stride;
-               $stride *= $dims[$dim];
-           }
-
-           if ($field{type} =~ m/^(struct|union):(.*)/) {
-               for $dim (0 .. $#dims) {
-                   print $dst "// $dims[$dim]\n";
-                   }
-
-               print $dst "public $atype get$cc(";
-               for $dim (0 .. $#dims) {
-                   print $dst ", " if ($dim != 0);
-                   print $dst "int i$dim";
-               }
-               print $dst ") {\n";
-               print $dst "\tint i=";
-               for $dim (0 .. $#dims) {
-                   print $dst " + " if ($dim != 0);
-                   print $dst "(i$dim * $strides[$#dims - $dim])";
-               }
-               print $dst ";\n";
-               print $dst "return Native.Pointer.ofAddress(addr().addOffset(i * 8), 32, Data::new);\n";
-               print $dst "}\n";
-           } elsif ($field{type} =~ m/^call:/) {
-           } else {
-           }
-       } elsif ($field{deref} =~ m/^u64:\$/) {
-           # pointer-to-struct
-           if ($field{type} =~ m/^(struct|union):(.*)/) {
-               my $ltype = StudlyCaps($2);
-               my $offset = $field{offset} >> 3;
-               my $addr = $offset ? "addr().addOffset($offset)" : 'addr()';
-
-               my $size = %{$data{$field{type}}}{size} >> 3;
-
-               print $dst "\tpublic $ltype get$cc() {\n";
-               print $dst "\t\treturn $ltype.create(Native.getAddr($addr, $size));\n";
-               print $dst "\t}\n";
-
-               print $dst "\tpublic void set$cc($ltype v) {\n";
-               print $dst "\t\tNative.setAddr($addr, v.addr());\n";
-               print $dst "\t}\n";
-           }
-       } elsif ($field{deref} =~ m/^u64:u64:\$/) {
-           # pointer-to-pointer-to?
-           if ($field{type} =~ m/^(struct|union):(.*)/) {
-               my $ltype = StudlyCaps($2);
-               my $offset = $field{offset} >> 3;
-               my $addr = $offset ? "addr().addOffset($offset)" : 'addr()';
-
-               my $size = %{$data{$field{type}}}{size} >> 3;
-
-               print $dst "\tpublic $type get$cc() {\n";
-               print $dst "\t\treturn Native.Pointer.ofAddress($addr, $size, $ltype"."::new);\n";
-               print $dst "\t}\n";
-
-               print $dst "\tpublic void set$cc($type v) {\n";
-               print $dst "\t\tNative.setAddr($addr, v.addr());\n";
-               print $dst "\t}\n";
-           }
-       } elsif ($field{ctype} eq 'bitfield') {
-           my $alsr = $field{type} =~ m/^u/ ? '>>>' : '>>';
-           my $lshift = $field{size} <= 32 ? 5 : 6;
-           my $lbits = 1 << $lshift;
-           my $type = $lbits == 32 ? 'int' : 'long';
-           my $ltype = $lbits == 32 ? 'Int' : 'Long';
-
-           my $offset = ($field{offset} >> ($lshift)) * ($lbits / 8);
-           my $addr = $offset ? "addr().addOffset($offset)" : 'addr()';
-           my $shift = $field{offset} & ($lbits-1);
-           my $width = $field{size};
-           my $upshift = ($lbits-$width-$shift);
-           my $downshift = ($lbits-$width);
-           my $mask = sprintf("0x%x", ((1 << $width) - 1) << $shift);
-
-           print $dst "\tpublic $type get$cc() {\n";
-           print $dst "\t\treturn (($type)Native.get$ltype($addr)) << $upshift $alsr $downshift;\n";
-           print $dst "\t}\n";
-
-           print $dst "\tpublic void set$cc($type v) {\n";
-           print $dst "\t\tMemoryAddress addr = $addr;\n";
-           print $dst "\t\tNative.set$ltype(addr, ((($type)Native.get$ltype(addr)) & ~$mask) | ((v << $shift) & $mask));\n";
-           print $dst "\t}\n";
-       } elsif ($field{type} =~ m/^(struct|union):/) {
-           # embedded struct
-       } elsif ($field{type} =~ m/^call:/) {
-           # call, function?
-           print $dst "// call? $type $cc\n";
-           my $offset = $field{offset} >> 3;
-           my $addr = $offset ? "addr().addOffset($offset)" : 'addr()';
-           my $ltype = $type;
-
-           $type =~ s/Callback<(.*)>/$1/;
-
-           print $dst "\tprivate Pointer<$type> $cc;\n";
-
-           print $dst "\tpublic void set$cc($type v) {\n";
-           print $dst "\t\tif ($cc != null) $cc.close();\n";
-           print $dst "\t\tNative.setAddr($addr, ($cc = $type.call(v)).addr());\n";
-           print $dst "\t}\n";
-       } else {
-           my $offset = $field{offset} >> 3;
-           my $addr = $offset ? "addr().addOffset($offset)" : 'addr()';
-           my $ltype = $type;
-
-           $ltype =~ s/^(.)/uc($1)/e;
-
-           die("non-byte offset=$offset ".Dumper(\%field)) if ($field{offset} & 7);
-
-           print $dst "\tpublic $type get$cc() {\n";
-           print $dst "\t\treturn Native.get$ltype($addr);\n";
-           print $dst "\t}\n";
-
-           print $dst "\tpublic void set$cc($type v) {\n";
-           print $dst "\t\tNative.set$ltype($addr, v);\n";
-           print $dst "\t}\n";
-       }
-    }
-
-    my $byteSize = $struct{size} >> 3;
-    print $dst "\tpublic static final long sizeof = $byteSize;\n";
-
-    # TODO: optional just call new()
-    print $dst "\tpublic static $name create(MemoryAddress p) {\n";
-    print $dst "\t\treturn Native.resolve(p, $name"."::new);\n";
-    print $dst "\t}\n";
-
-    print $dst "\tpublic static $name alloc() {\n";
-    print $dst "\t\treturn $name.create(MemorySegment.allocateNative(sizeof).baseAddress());\n";
-    print $dst "\t}\n";
-    print $dst "\tpublic static Pointer<$name> alloc(int n) {\n";
-    print $dst "\t\treturn Pointer.alloc(n, sizeof, $name"."::new);\n";
-    print $dst "\t}\n";
-
-    if ($struct{type} eq "union") {
-       print $dst "\tpublic static MemoryLayout layout() { return Native.parseUnion(\"$signature\"); }\n";
-    } else {
-       print $dst "\tpublic static MemoryLayout layout() { return Native.parseStruct(\"$signature\"); }\n";
-       }
-
-    print $dst "}\n";
-
-    if (!$enclosingType) {
-       close($dst);
-    }
-}
-
-# ######################################################################
-# Dump classes for library linkage
-for $c (@classes) {
-    my %class = %{$c};
-    my @libs = @{$class{libs}};
-    my @match = @{$class{match}};
-
-    if (!$enclosingType) {
-       my $classname = $packagePrefix.$class{name};
-
-       open ($dst, ">$path");
-       $classname =~ s@\.@/@g;
-
-       my $path = $output."/".$classname.".java";
-       my $dir = dirname($path);
-       my $class = basename($path, ".java");
-       make_path($dir);
-       open ($dst, ">$path");
-
-       if ($package ne "") {
-           print $dst "package $package;\n";
-       }
-       print $dst <<END;
-import jdk.incubator.foreign.*;
-import java.lang.invoke.MethodHandle;
-import api.Native;
-$importPointer
-END
-    }
-
-    print $dst "public class $class{name} {\n";
-
-    print $dst "\tstatic final String[] libraries = {";
-    print $dst join(",", map { "\"$_\"" } @libs);
-    print $dst "};\n";
-
-    # enums to ints
-    # TODO: interfaces?
-    # TODO: static lib class?
-    # typedef enums might appear twice in the data, so ignore duplicates
-    # also, some api's have multiple definitions (?)
-    my %visited = ();
-    my @match_enum = @{$class{enum}};
-    for $k (sort(findDefinition(\%data, 'enum', @match_enum))) {
-       my %enum = %{$data{$k}};
-       my @values = @{$enum{values}};
-       my $type = "int";
-
-       if ($enum{value_type} =~ m/^[ui](\d+)/) {
-           $type = "long" if ($1 > 32)
-       }
-
-       print $dst "\n\t// enum $enum{name}\n";
-       for $vi (@values) {
-           my %value = %{$vi};
-
-           if (!$visited{$value{label}}) {
-               #print $dst "\tpublic static final $type $value{label} = ($type)$value{value};\n";
-               print $dst "\tpublic static final $type $value{label} = $value{value};\n";
-               $visited{$value{label}} = 1;
-           }
-       }
-    }
-
-    # function handles
-    print "class $class{name} -> match:\n".Dumper(\@match);
-
-    for $k (sort(findDefinition(\%data, 'func', @match))) {
-       my %func = %{$data{$k}};
-       my @params = @{$func{arguments}};
-       my $signature = funcSignature(\%func);
-       my $name = ($func{name});
-
-       print $dst "\tfinal static MethodHandle $name;\n";
-    }
-
-    # function handle init
-    print $dst "\tstatic {\n";
-    print $dst "\t\tLibraryLookup[] libs = Native.loadLibraries(libraries);\n";
-
-    for $k (sort(findDefinition(\%data, 'func', @match))) {
-       my %func = %{$data{$k}};
-       my @params = @{$func{arguments}};
-       my $signature = funcSignature(\%func);
-       my $name = ($func{name});
-
-       print $dst "\t\t$name = Native.downcallHandle(libs, \"$name\", \"$signature\");\n";
-    }
-    print $dst "\t}\n";
-
-    # function handle invocation
-    if ($rawCalls) {
-       for $k (sort(findDefinition(\%data, 'func', @match))) {
-           my %func = %{$data{$k}};
-           my @params = @{$func{arguments}};
-           my $signature = funcSignature(\%func);
-           my $name = ($func{name});
-           my %res = %{$func{result}};
-           my $result = typeToRaw(\%res);
-
-           print $dst "\tpublic static $result $name(";
-
-           for $pi (@params) {
-               my %param = %{$pi};
-               my $type = typeToRaw($pi);
-
-               print $dst "$type $param{name}";
-               print $dst ", " if ($pi != $params[$#params]);
-           }
-
-           print $dst ") throws Throwable {\n";
-           if ($result ne "void") {
-               print $dst "return ($result)";
-           }
-           print $dst "$name.invokeExact(";
-           for $pi (@params) {
-               my %param = %{$pi};
-
-               print $dst "$param{name}";
-               print $dst ", " if ($pi != $params[$#params]);
-           }
-           print $dst ");\n";
-           print $dst "\t}\n\n";
-       }
-       print $dst "}\n";
-    } else {
-       for $k (sort(findDefinition(\%data, 'func', @match))) {
-           my %func = %{$data{$k}};
-           my @params = @{$func{arguments}};
-           my $signature = funcSignature(\%func);
-           my $name = ($func{name});
-           my %res = %{$func{result}};
-           my $result = typeToJava(\%{$func{result}});
-
-           print $dst "\tpublic static $result $name(";
-
-           for $pi (@params) {
-               my %param = %{$pi};
-               my $type = typeToJava($pi);
-
-               $type =~ s/Callback/Pointer/;
-
-               # HACK
-               $type =~ s/Pointer<Void>/Pointer<?>/;
-
-               print $dst "$type $param{name}";
-               print $dst ", " if ($pi != $params[$#params]);
-           }
-
-           print $dst ") {\n";
-           # see also call below
-           print $dst "\t\ttry {\n";
-           print $dst "\t\t\t";
-           if ($res{type} =~ m/(struct|union)/n) {
-               if ($res{deref}) {
-                   print $dst "MemoryAddress add = (MemoryAddress)";
-               } else {
-                   print $dst "MemorySegment seg = (MemorySegment)";
-               }
-           } elsif ($result ne "void") {
-               print $dst "return ($result)";
-           }
-           print $dst "$name.invokeExact(";
-           for $pi (@params) {
-               my %param = %{$pi};
-
-               print $dst "$param{name}";
-               if ($param{deref}) {
-                   print $dst ".addr()";
-               } elsif ($param{type} =~ m/^struct|union/) {
-                   print $dst ".addr().segment()";
-               }
-               print $dst ", " if ($pi != $params[$#params]);
-           }
-           print $dst ");\n";
-           if ($res{type} =~ m/(struct|union)/n) {
-               if ($res{deref}) {
-                   print $dst "\t\t\treturn $result.create(add);\n";
-               } else {
-                   print $dst "\t\t\treturn $result.create(seg.baseAddress());\n";
-               }
-           }
-           print $dst "\t\t}\n";
-           print $dst "\t\tcatch (Throwable t) { throw new RuntimeException(t); }\n";
-           print $dst "\t}\n\n";
-       }
-
-       print $dst "}\n";
-    }
-
-    if (!$enclosingType) {
-       close($dst);
-    }
-}
-
-# ######################################################################
-# Dump callbacks
-# TODO: only those used by classes and functions that were exported
-# TODO: yeah this is a total total fucking shitshow
-
-if ($rawCalls) {
-    for $c (keys %callMap) {
-       my %call = %{$data{$c}};
-       my $name = $callMap{$c};
-       my @params = @{$call{arguments}};
-       my %res = %{$call{result}};
-       my $result = typeToRaw(\%res);
-       my $signature = funcSignature(\%call);
-
-       if (!$enclosingType) {
-           my $classname = $packagePrefix.$name;
-
-           open ($dst, ">$path");
-           $classname =~ s@\.@/@g;
-
-           my $path = $output."/".$classname.".java";
-           my $dir = dirname($path);
-           my $class = basename($path, ".java");
-           make_path($dir);
-           open ($dst, ">$path");
-
-           if ($package ne "") {
-               print $dst "package $package;\n";
-           }
-           print $dst <<END;
-import jdk.incubator.foreign.*;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Method;
-import api.Callback;
-import api.Native;
-$importPointer
-END
-       }
-
-       print $dst "\@FunctionalInterface\n";
-       print $dst "public interface $name {\n";
-       print $dst "\tpublic $result fn(";
-
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToRaw($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-
-       print $dst ") throws Throwable;\n";
-
-       # downcall
-       print $dst "\tstatic public $name of(MemoryAddress addr) {\n";
-       print $dst "\t\tMethodHandle func = Native.downcallHandle(addr, \"$signature\");\n";
-       print $dst "\t\treturn (";
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToRaw($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-       print $dst ") -> ";
-       if ($result ne "void") {
-           print $dst "($result)";
-       }
-       print $dst "func.invokeExact(";
-       for $pi (@params) {
-           my %param = %{$pi};
-
-           print $dst "$param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-       print $dst ");\n";
-       print $dst "\t}\n";
-
-       # upcall ##############################################################
-       # ??
-
-       print $dst "\tstatic MemoryAddress stub($name call) {\n";
-       print $dst "\t\treturn Native.upcallStub(MethodHandles.lookup(), call, \"$signature\");\n";
-       print $dst "\t}\n";
-
-       # # the raw interface as expected by the native code
-       # my $rawresult = typeToRaw(\%res);
-       # print $dst "\tpublic interface $rawName {\n";
-       # # fixme raw result
-       # print $dst "\t\tpublic $rawresult fn(";
-
-       # for $pi (@params) {
-       #     my %param = %{$pi};
-       #     my $type = typeToRaw($pi);
-
-       #     print $dst "$type $param{name}";
-       #     print $dst ", " if ($pi != $params[$#params]);
-       # }
-
-       # print $dst ");\n";
-       # print $dst "\t}\n";
-
-       # print $dst "\tstatic public Pointer<$name> call($name v) {\n";
-       # print $dst "\t\t$rawName func = (";
-       # for $pi (@params) {
-       #     my %param = %{$pi};
-       #     my $type = typeToRaw($pi);
-
-       #     print $dst "$type $param{name}";
-       #     print $dst ", " if ($pi != $params[$#params]);
-       # }
-       # print $dst ") -> {\n";
-       # print $dst "\t\t\t";
-       # if ($rawresult ne "void") {
-       #     print $dst "return ";
-       # }
-       # print $dst "v.fn(";
-       # for $pi (@params) {
-       #     my %param = %{$pi};
-       #     my $type = typeToJava($pi);
-       #     my $rawtype = typeToRaw($pi);
-
-       #     print "type ='$type'\n";
-       #     if ($type =~ m/^Pointer<[^>]*>$/) {
-       #       print $dst "Pointer.ofAddress($param{name})";
-       #     } elsif ($type eq "Pointer<Pointer<Void>>") {
-       #       print $dst "Pointer.ofAddressP($param{name})";
-       #     } elsif ($rawtype eq "MemoryAddress") {
-       #       print $dst "$type.create($param{name})";
-       #     } elsif ($rawtype eq "MemorySegment") {
-       #       print $dst "$type.create($param{name}.baseAddress())";
-       #     } else {
-       #       print $dst "$param{name}";
-       #     }
-       #     print $dst ", " if ($pi != $params[$#params]);
-       # }
-       # print $dst ")";
-       # if ($rawresult eq "MemoryAddress") {
-       #     print $dst ".addr()";
-       # } elsif ($rawresult eq "MemorySegment") {
-       #     print $dst ".addr().segment()";
-       # }
-       # print $dst ";\n";
-
-       # print $dst "\t\t};\n";
-       # print $dst "\t\treturn Native.Pointer.ofCallback(MethodHandles.lookup(), v, func, \"$signature\");\n";
-       # print $dst "\t}\n";
-
-       print $dst "}\n";
-
-       if (!$enclosingType) {
-           close($dst);
-       }
-    }
-} else {
-    for $c (keys %callMap) {
-       my %call = %{$data{$c}};
-       my $name = $callMap{$c};
-       my @params = @{$call{arguments}};
-       my %res = %{$call{result}};
-       my $result = typeToJava(\%{$call{result}});
-       my $signature = funcSignature(\%call);
-
-       if (!$enclosingType) {
-           my $classname = $packagePrefix.$name;
-
-           open ($dst, ">$path");
-           $classname =~ s@\.@/@g;
-
-           my $path = $output."/".$classname.".java";
-           my $dir = dirname($path);
-           my $class = basename($path, ".java");
-           make_path($dir);
-           open ($dst, ">$path");
-
-           if ($package ne "") {
-               print $dst "package $package;\n";
-           }
-           print $dst <<END;
-import jdk.incubator.foreign.*;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Method;
-import api.Native;
-$importPointer
-END
-       }
-
-       # any in-line structures need to be added to the resolutionContext
-       # TODO: only include actual inline, not pointers
-       #my %resolve = ();
-       #my @list = @params;
-       #unshift(@list,$call{result});
-       #for $pi (@list) {
-       #       my %param = %{$pi};
-       #
-       #       if ($param{type} =~ m/^(struct|union):(.*)/) {
-       #           $resolve{StudlyCaps($2).".class"} = 1;
-       #       }
-       #    }
-       #my $resolve = join (",", keys %resolve);
-
-       print $dst "\@FunctionalInterface\n";
-       print $dst "public interface $name {\n";
-       print $dst "\tpublic $result fn(";
-
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToJava($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-
-       print $dst ");\n";
-
-       # downcall
-       print $dst "\tstatic public $name of(MemoryAddress addr) {\n";
-       print $dst "\t\tMethodHandle func = Native.downcallHandle(addr, \"$signature\");\n";
-       print $dst "\t\treturn (";
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToJava($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-       print $dst ") -> {\n";
-       print $dst "\t\t\ttry {\n";
-       print $dst "\t\t\t\t";
-       if (!$res{deref} && $res{type} =~ m/(struct|union)/n) {
-           print $dst "MemorySegment seg = (MemorySegment)";
-       } elsif ($result ne "void") {
-           print $dst "return ($result)";
-       }
-       print $dst "func.invokeExact(";
-       for $pi (@params) {
-           my %param = %{$pi};
-
-           print $dst "$param{name}";
-           if ($param{deref}) {
-               print $dst ".addr()";
-           } elsif ($param{type} =~ m/^struct|union/) {
-               print $dst ".addr().segment()";
-           }
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-       print $dst ");\n";
-       if (!$res{deref} && $res{type} =~ m/(struct|union)/n) {
-           print $dst "\t\t\t\treturn $result.create(seg.baseAddress());\n";
-       }
-       print $dst "\t\t\t} catch (Throwable t) { throw new RuntimeException(t); }\n";
-       print $dst "\t\t};\n";
-       print $dst "\t}\n";
-
-       # upcall ##############################################################
-       # the raw interface as expected by the native code
-       my $rawName = $name.'Raw';
-       my $rawresult = typeToRaw(\%res);
-       print $dst "\tpublic interface $rawName {\n";
-       # fixme raw result
-       print $dst "\t\tpublic $rawresult fn(";
-
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToRaw($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-
-       print $dst ");\n";
-       print $dst "\t}\n";
-
-       print $dst "\tstatic public Pointer<$name> call($name v) {\n";
-       print $dst "\t\t$rawName func = (";
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToRaw($pi);
-
-           print $dst "$type $param{name}";
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-       print $dst ") -> {\n";
-       print $dst "\t\t\t";
-       if ($rawresult ne "void") {
-           print $dst "return ";
-       }
-       print $dst "v.fn(";
-       for $pi (@params) {
-           my %param = %{$pi};
-           my $type = typeToJava($pi);
-           my $rawtype = typeToRaw($pi);
-
-           print "type ='$type'\n";
-           if ($type =~ m/^Pointer<[^>]*>$/) {
-               print $dst "Pointer.ofAddress($param{name})";
-           } elsif ($type eq "Pointer<Pointer<Void>>") {
-               print $dst "Pointer.ofAddressP($param{name})";
-           } elsif ($rawtype eq "MemoryAddress") {
-               print $dst "$type.create($param{name})";
-           } elsif ($rawtype eq "MemorySegment") {
-               print $dst "$type.create($param{name}.baseAddress())";
-           } else {
-               print $dst "$param{name}";
-           }
-           print $dst ", " if ($pi != $params[$#params]);
-       }
-       print $dst ")";
-       if ($rawresult eq "MemoryAddress") {
-           print $dst ".addr()";
-       } elsif ($rawresult eq "MemorySegment") {
-           print $dst ".addr().segment()";
-       }
-       print $dst ";\n";
-
-       print $dst "\t\t};\n";
-       print $dst "\t\treturn Native.Pointer.ofCallback(MethodHandles.lookup(), v, func, \"$signature\");\n";
-       print $dst "\t}\n";
-
-       print $dst "}\n";
-
-       if (!$enclosingType) {
-           close($dst);
-       }
-    }
-}
-
-# Finish off
-if ($enclosingType) {
-    print $dst "}\n";
-    close($dst);
-}
diff --git a/src/generate-native b/src/generate-native
new file mode 100755 (executable)
index 0000000..55eaee7
--- /dev/null
@@ -0,0 +1,893 @@
+#!/usr/bin/perl
+
+# -*- Mode:perl; perl-indent-level:4;tab-width:4; -*-
+
+# TODO: get/set should take a typed field for struct/union *
+# TODO: a flag for func_name to funcName option
+
+use Data::Dumper;
+use File::Basename;
+use File::Path qw(make_path);
+
+$scriptPath = dirname(__FILE__);
+
+$package = "";
+$output = "bin";
+$verbose = 0;
+
+# usage
+#  -t package    target package
+#  -d directory  output root
+#  -v            verbose
+
+my %typeSizes = (
+       i8 => 'byte', u8 => 'byte',
+       i16 => 'short', u16 => 'short',
+       i32 => 'int', u32 => 'int',
+       i64 => 'long', u64 => 'long',
+       f32 => 'float',
+       f64 => 'double',
+       );
+
+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;",
+       );
+
+while (@ARGV) {
+       my $cmd = shift(@ARGV);
+
+       if ($cmd eq "-t") {
+               $package = shift(@ARGV);
+    } elsif ($cmd eq "-d") {
+               $output = shift(@ARGV);
+    } elsif ($cmd eq "-v") {
+               $verbose++;
+       } else {
+               $meta = $cmd;
+       }
+}
+
+# load in interface file
+do $meta;
+
+analyseAndFixTypes();
+
+if ($verbose) {
+       print "Using:n";
+       print Dumper(\%data);
+}
+
+# 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 @replace = ();
+
+       # pass 1, fix call definition names and keys
+       foreach $old (keys %data) {
+               if ($old =~ m/^call:/) {
+                       push @replace, $old;
+               }
+       }
+       foreach $old (@replace) {
+               my $new = $old;
+               my $c;
+
+               $new =~ s/(.*)\((.*)\)(.*)/$1Call_$2_$3/;
+               $data{$new} = $c = delete $data{$old};
+               $c->{name} =~ s/(.*)\((.*)\)(.*)/$1Call_$2_$3/;
+       }
+
+       # 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}})) {
+                               print "Add anonymous $1 $2\n";
+                               $data{$a->{type}} = {
+                                       name => $2,
+                                       type => $1,
+                                       size => 0
+                               };
+                       }
+
+                       if ($a->{type} =~ m/^call:/) {
+                               $a->{type} =~ s/(.*)\((.*)\)(.*)/$1Call_$2_$3/;
+                       }
+
+                       # must be last
+                       $a->{typeInfo} = queryTypeInfo($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*).*\]/) {
+                       $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*).*\]/) {
+                       $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;
+}
+
+sub queryTypeInfo {
+       my $m = shift @_;
+       my $info = {};
+
+       # 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+)$/) {
+               if ($m->{deref} =~ m/\[(\d*).*\]/) {
+                       $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}.".create(\${result})";
+               } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
+                       $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
+                       $info->{create} = $info->{type}.".create(\${result})";
+               } else {
+                       $info->{type} = $typeSizes{$m->{type}};
+                       $info->{carrier} = $typeSizes{$m->{type}};
+                       $info->{resolve} = "($info->{type})(\${value})";
+                       $info->{create} = "\${result}";
+               }
+       } elsif ($m->{type} =~ m/^(struct|union):(.*)/) {
+               my $type = $2;
+               if ($m->{deref} =~ m/\[(\d*).*\]/) {
+                       $info->{type} = $type;
+                       $info->{create} = $info->{type}.".createArray($1, \${result}, scope())";
+               } elsif ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
+                       $info->{type} = "Memory.PointerArray";
+                       $info->{create} = $info->{type}.".create(\${result})";
+               } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
+                       $info->{type} = $type;
+                       $info->{create} = $info->{type}.".create(\${result}, scope())";
+               } else {
+                       $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}.".create(\${result})";
+               } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
+                       $info->{type} = "MemoryAddress";
+                       $info->{create} = "\${result}";
+                       $info->{resolve} = "(Addressable)\${value}";
+               } else {
+                       $info->{type} = "void";
+                       $info->{carrier} = "void";
+                       delete $info->{resolve};
+               }
+       } else {
+               print Dumper($m);
+               die ("unknown type");
+       }
+
+       return $info;
+}
+
+sub formatFunction {
+       my $c = shift @_;
+       my @arguments = @{$c->{arguments}};
+       my $result = $c->{result};
+       my $desc;
+       my $index = 0;
+       my $desc;
+       my $name = $c->{name};
+
+       my $rtype = $result->{typeInfo}->{type};
+
+       #print Dumper($c);
+
+       $desc = $rtype;
+       $desc .= " $name(";
+
+       for $m (@arguments) {
+               $desc .= ", " if ($index++ > 0);
+               $desc .= $m->{typeInfo}->{type};
+               $desc .= " $m->{name}"
+       }
+       $desc .=") {\n ";
+       $index = 0;
+
+       $desc .= "try {\n";
+       $desc .= "  $result->{typeInfo}->{carrier} res\$value = ($result->{typeInfo}->{carrier})" if ($rtype ne "void");
+       $desc .= "  " if ($rtype eq "void");
+
+       $desc .= "$name\$FH.invokeExact(\n   ";
+       for $m (@arguments) {
+               my $resolve = $m->{typeInfo}->{resolve};
+
+               $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") {
+               my $create = $result->{typeInfo}->{create};
+
+               # ooh, templates could insert other arguments or values as well?
+               $create =~ s/\${result\}/res\$value/;
+
+               $desc .= "  return $create;\n";
+       }
+       # throw Error()?
+       $desc .= " } catch (Throwable t) { throw new RuntimeException(t); }\n";
+
+       $desc .="}";
+
+       return $desc;
+}
+
+# create an interface for function pointers
+# FiXME: this should be exportCallback to a file
+sub formatCallback {
+       my $c = 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;
+
+               $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);
+
+       $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 $desc = "";
+       my $info = $m->{typeInfo};
+       my $Name = ucfirst($m->{name});
+       my $tmp;
+
+       # info -> needsalloc?
+
+       # TODO: String
+       # TODO: embedded arrays are quite different setup
+
+       if ($info->{byValue}) {
+               $tmp = $info->{create};
+               $tmp =~ s/\$\{result\}/segment/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 .= "  return $tmp;\n";
+               $desc .= " }\n";
+
+               $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 .= "  return $tmp;\n";
+               $desc .= " }\n";
+       } else {
+               $tmp = $info->{create};
+               $tmp =~ s/\$\{result\}/($info->{carrier})$m->{name}\$VH.get(segment)/g;
+
+               $desc .= " public $info->{type} get$Name() {\n";
+               $desc .= "  return $tmp;\n";
+               $desc .= " }\n";
+
+               $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 (!($m->{deref} =~ m/\[(\d*).*\]/)) {
+               $tmp = $info->{resolve};
+               $tmp =~ s/\$\{value\}/value/g;
+
+               $desc .=  " public void set$Name($info->{type} value) {\n";
+               $desc .=  "  $m->{name}\$VH.set(segment, $tmp);\n";
+               $desc .=  " }\n";
+
+               $desc .=  " public void set$Name"."At(long index, $info->{type} value) {\n";
+               $desc .=  "  MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
+               $desc .=  "  $m->{name}\$VH.set(segment, $tmp);\n";
+               $desc .=  " }\n";
+       }
+
+       # indexed
+
+       return $desc;
+}
+
+sub exportStruct {
+       my $f = shift @_;
+       my $s = shift @_;
+       my @fields = @{$s->{fields}};
+       my $isHandle = $s->{size} == 0;
+       #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 " 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 create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope));\n";
+               print $f " }\n";
+               print $f " public static $s->{name} createArray(MemoryAddress address, long size, ResourceScope scope) {\n";
+               print $f "  return create(MemorySegment.ofAddress(address, size * LAYOUT.byteSize(), scope));\n";
+               print $f " }\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) { return new $s->{name}(address); }\n";
+               print $f " public MemoryAddress address() { return address; }\n";
+               print $f " public ResourceScope scope() { return scope; }\n";
+       }
+
+       # FIXME: use typeInfo
+       # TODO: indexed accessors
+       # accessors
+       if (1) {
+               foreach $m (@fields) {
+                       print $f formatGetSet($s, $m);
+               }
+       } else {
+               foreach $m (@fields) {
+                       my $Name = ucfirst($m->{name});
+
+                       print $f " // [$m->{deref}] [$m->{type}] [$m->{ctype}]\n";
+
+                       if ($m->{deref} =~ m/^(u64:|u32:)\(/) {
+                               # This is a function pointer, type must be type = 'call:.*'
+
+                               if ($m->{type} =~ m/^call:(.*)/) {
+                                       my $jtype = $1;
+
+                                       $jtype =~ s/(.*)\((.*)\)(.*)/Call$1_$2_$3/;
+
+                                       print $f " public Memory.FunctionPointer<$jtype> get$Name() {\n";
+                                       print $f "  // FIXME: better scope\n";
+                                       print $f "  return $jtype.downcall((MemoryAddress)$m->{name}\$VH.get(segment), scope());\n";
+                                       print $f " }\n";
+
+                                       print $f " public void set$Name(Memory.FunctionPointer<$jtype> value) {\n";
+                                       print $f "  $m->{name}\$VH.set(segment, Memory.address(value));\n";
+                                       print $f " }\n";
+                               }
+                       } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
+                               # all other pointer types require extra context, e.g. a length or type
+                               print $f " public MemoryAddress get$Name() {\n";
+                               print $f "  return (MemoryAddress)$m->{name}\$VH.get(segment);\n";
+                               print $f " }\n";
+
+                               # FIXME: set could use the type though
+                               print $f " public void set$Name(MemoryAddress value) {\n";
+                               print $f "  $m->{name}\$VH.set(segment, Memory.address(value));\n";
+                               print $f " }\n";
+                       } elsif ($m->{type} eq "bitfield") {
+                               # TODO
+                       } elsif ($m->{type} =~ m/^struct|union:(.*)$/) {
+                               my $jtype = $1;
+
+                               $jtype = "Memory.HandleArray<$jtype>" if ($data{$m->{type}}->{size} == 0);
+
+                               # embedded type including arrays
+                               print $f " public $jtype get$Name() {\n";
+                               print $f "  MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
+                               print $f "  MemorySegment seg = segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n";
+                               print $f "  return $jtype.create(seg, $jtype::new);\n" if ($data{$m->{type}}->{size} == 0);
+                               print $f "  return $jtype.create(seg);\n" if ($data{$m->{type}}->{size} != 0);
+                               print $f " }\n";
+                       } elsif ($m->{type} =~ m/^[uif]\d+$/) {
+                               my $jtype = $typeSizes{$m->{type}};
+                               my $Jtype = ucfirst($jtype);
+                               my $JTYPE = uc($jtype);
+
+                               if ($m->{deref} =~ m/\[(\d*).*\]/) {
+                                       # array type
+                                       print $f " public Memory.$Jtype"."Array get$Name() {\n";
+                                       print $f "  MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
+                                       print $f "  MemorySegment seg = segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n";
+                                       print $f "  return new Memory.$Jtype"."Array(seg);\n";
+                                       print $f " }\n";
+                               } else {
+                                       # primitive type
+                                       print $f " public $jtype get$Name() {\n";
+                                       print $f "  return ($jtype)$m->{name}\$VH.get(segment);\n";
+                                       print $f " }\n";
+
+                                       print $f " public void set$Name($jtype value) {\n";
+                                       print $f "  $m->{name}\$VH.set(segment, ($jtype)value);\n";
+                                       print $f " }\n";
+                               }
+                       }
+                       # struct                                print $f " MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
+
+               }
+       }
+
+       # layout and varhandles
+       if ($#fields >= 0) {
+               print $f "static final GroupLayout LAYOUT = ".formatLayout($s).";\n";
+
+               foreach $m (@fields) {
+                       next if ($m->{typeInfo}->{byValue});
+                       print $f " static final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\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;
+}
+
+foreach $x (grep { m/^(struct|union):/ } sort keys %data) {
+       my $s = $data{$x};
+       my $path = nameToPath($output, "$package.$s->{name}");
+
+       open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
+
+       exportStruct($f, $s);
+
+       close $f;
+}
+
+foreach $x (grep { m/^call:/ } sort keys %data) {
+       my $c = $data{$x};
+       my $name = $c->{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);
+
+       close $f;
+}
+
+# just quick and dirty for now
+# may want a non-static version with a specific scope?
+{
+       my @functions = grep { /^func:/ } keys %data;
+       my $lib = {
+               name => "APILib",
+               functions => \@functions
+       };
+
+
+       my $path = nameToPath($output, "$package.$lib->{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 "public class $lib->{name} {\n";
+
+       print $f " static ResourceScope scope() { return ResourceScope.globalScope(); }\n";
+       foreach $cname (@{$lib->{functions}}) {
+               my $c = $data{$cname};
+               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);
+               print $f "public static ";
+               print $f $tmp;
+       }
+
+       print $f "}\n";
+
+       close $f;
+}
diff --git a/src/template/Frame.java b/src/template/Frame.java
new file mode 100644 (file)
index 0000000..5eafe57
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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;
+               }
+       }
+
+}
diff --git a/src/template/Memory.java b/src/template/Memory.java
new file mode 100644 (file)
index 0000000..1cdf9f6
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * 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;
+
+/**
+ * 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 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;
+       }
+
+       // 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(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;
+
+               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();
+               }
+
+               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;
+
+               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();
+               }
+
+               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 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();
+               }
+
+               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;
+
+               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();
+               }
+
+               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;
+
+               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();
+               }
+
+               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(long size, SegmentAllocator alloc) {
+                       return create(alloc.allocateArray(Memory.POINTER, size));
+               }
+
+               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);
+               }
+       }
+
+       public static class HandleArray<T extends Memory.Addressable> extends AbstractList<T> implements Memory.Addressable {
+               final MemorySegment segment;
+               BiFunction<MemoryAddress,ResourceScope,T> create;
+
+               private HandleArray(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create) {
+                       this.segment = segment;
+                       this.create = create;
+               }
+
+               public static <T extends Memory.Addressable> HandleArray<T> create(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create) {
+                       return new HandleArray<>(segment, create);
+               }
+
+               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);
+               }
+
+               @Override
+               public final MemoryAddress address() {
+                       return segment.address();
+               }
+
+               public final ResourceScope scope() {
+                       return segment.scope();
+               }
+
+               @Override
+               public int size() {
+                       return (int)length();
+               }
+
+               @Override
+               public T get(int index) {
+                       return getAtIndex(index);
+               }
+
+               @Override
+               public T set(int index, T value) {
+                       T old = getAtIndex(index);
+                       setAtIndex(index, value);
+                       return old;
+               }
+
+               public long length() {
+                       return segment.byteSize() / Memory.POINTER.byteSize();
+               }
+
+               public T getAtIndex(long index) {
+                       MemoryAddress ptr = segment.getAtIndex(Memory.POINTER, index);
+                       return ptr != null ? create.apply(ptr, scope) : null;
+               }
+
+               public void setAtIndex(long index, T value) {
+                       segment.setAtIndex(Memory.POINTER, index, value != null ? value.address() : MemoryAddress.NULL);
+               }
+       }
+
+}
diff --git a/src/template/Native.java b/src/template/Native.java
new file mode 100644 (file)
index 0000000..d214edc
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * 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 java.io.StringReader;
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import jdk.incubator.foreign.*;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.System.Logger.Level;
+
+/**
+ * Base class for all native objects.
+ * <p>
+ * Handles instantiation and provides helper functions for native access.
+ * <p>
+ * Work in progress.
+ * <p>
+ * For better safety the 'p' field should be the CHandle, and addr() would
+ * call get(). Otherwise one must not release ANY object which might ever
+ * be used again - including any objects returned by the getInfo(). However ...
+ * it's a trade-off and it's a lot of code to change.
+ * <p>
+ * FIXME: there are MemorySegment based accessors for primitive types now, use those
+ */
+public class Native implements Memory.Addressable {
+
+       private final MemoryAddress p;
+
+       private final static boolean dolog = true;
+
+       protected Native(MemoryAddress p) {
+               this.p = p;
+       }
+
+       static System.Logger log() {
+               return System.getLogger("notzed.native");
+       }
+
+       public MemoryAddress address() {
+               return p;
+       }
+
+       /* ********************************************************************** */
+       /* GC handling */
+       /* ********************************************************************** */
+       /**
+        * Resource index.
+        */
+       static private final PointerTable map = new PointerTable();
+
+       /**
+        * Reference queue for stale objects.
+        */
+       static private final ReferenceQueue<Native> references = new ReferenceQueue<>();
+
+       private static <T extends Native> T createInstance(Class<T> jtype, MemoryAddress p) {
+               cleanerStep();
+               try {
+                       Class[] params = {MemoryAddress.class};
+                       Constructor<T> cc = jtype.getDeclaredConstructor(params);
+
+                       cc.setAccessible(true);
+
+                       return cc.newInstance(p);
+               } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                       log().log(Level.ERROR, "createInstance", ex);
+                       throw new RuntimeException(ex);
+               }
+       }
+
+       /*
+       public static <T extends Native> T resolve(Class<T> jtype, MemoryAddress p) {
+               T o;
+
+               //if (dolog)
+               log().log(Level.DEBUG, () -> String.format("  resolve $%016x %s", p.offset(), jtype.getName()));
+
+               // Instantiation needs to be synchronized for obvious reasons.
+               synchronized (map) {
+                       CHandle h = (CHandle) map.get(p);
+
+                       if (h == null || (o = jtype.cast(h.get())) == null) {
+                               o = createInstance(jtype, p);
+                               h = new CHandle(o, references, p);
+                               map.putAlways(h);
+                       }
+               }
+               return o;
+               }*/
+       public static <T extends Native> T resolve(MemoryAddress p, Function<MemoryAddress, T> create) {
+               T o;
+               boolean step = false;
+
+               //if (dolog)
+               //      log().log(Level.DEBUG, () -> String.format("  resolv $%016x %s", Memory.toLong(p), create));
+               if (p.toRawLongValue() == 0)
+                       return null;
+
+               // Instantiation needs to be synchronized for obvious reasons.
+               synchronized (map) {
+                       CHandle h = (CHandle)map.get(p);
+
+                       String fmt;
+
+                       if (h == null || (o = (T)(h.get())) == null) {
+                               o = create.apply(p);
+
+                               fmt = h == null ? "  create $%016x %s" : "  replac $%016x %s";
+
+                               h = new CHandle(o, references, p);
+                               map.put(h);
+                               step = true;
+                       } else {
+                               fmt = "  exists $%016x %s";
+                       }
+                       {
+                               T x = o;
+                               log().log(Level.DEBUG, () -> String.format(fmt, p.toRawLongValue(), x.getClass().getName()));
+                       }
+               }
+
+               if (step)
+                       cleanerStep();
+
+               return o;
+       }
+
+       /*
+       public static <T extends Native> void register(T o) {
+               T o;
+               boolean step = false;
+
+               if (dolog)
+                       log().log(Level.DEBUG, () -> String.format("  regist $%016x %s", o.addr().offset(), o.getClass().getName()));
+
+               CHandle h = new CHandle(o, references, o.addr());
+
+               synchronized (map) {
+                       map.put(h);
+                       step = true;
+               }
+
+               if (step)
+                       cleanerStep();
+
+               return o;
+               }*/
+       public void release() {
+               WeakReference<? extends Native> ref;
+
+               synchronized (map) {
+                       ref = map.remove(p);
+               }
+
+               if (ref != null) {
+                       if (dolog)
+                               log().log(Level.DEBUG, () -> String.format("  force  $%016x %s", p.toRawLongValue(), getClass().getName()));
+
+                       ref.enqueue();
+               }
+       }
+
+       public static <T extends Native> void release(T a) {
+               if (a != null)
+                       a.release();
+       }
+
+       public static void release(Native... list) {
+               for (Native o: list)
+                       release(o);
+       }
+
+       static {
+               Thread cleanup = new Thread(Native::cleaner, "Native cleaner");
+               cleanup.setPriority(Thread.MAX_PRIORITY);
+               cleanup.setDaemon(true);
+               cleanup.start();
+       }
+
+       private static void cleanerStep() {
+               try {
+                       CHandle stale = (CHandle)references.poll();
+                       if (stale != null) {
+                               synchronized (map) {
+                                       map.remove(stale.p);
+                               }
+                               stale.release();
+                       }
+               } catch (Throwable ex) {
+               }
+       }
+
+       /**
+        * Cleaner thread.
+        * <p>
+        * This polls the reference queue and releases objects via
+        * their static release method.
+        */
+       private static void cleaner() {
+               if (dolog)
+                       log().log(Level.DEBUG, "Native finaliser started");
+               try {
+                       while (true) {
+                               CHandle stale = (CHandle)references.remove();
+                               do {
+                                       try {
+                                               synchronized (map) {
+                                                       map.remove(stale.p);
+                                               }
+                                               stale.release();
+                                       } catch (Throwable ex) {
+                                       }
+                                       stale = (CHandle)references.poll();
+                               } while (stale != null);
+                       }
+               } catch (InterruptedException ex) {
+               }
+       }
+
+       private static class CHandle extends WeakReference<Native> {
+
+               protected MemoryAddress p;
+               final Class<? extends Native> jtype;
+               CHandle next;
+
+               CHandle(Native referent, ReferenceQueue<Native> references, MemoryAddress p) {
+                       super(referent, references);
+                       this.p = p;
+                       this.jtype = referent.getClass();
+               }
+
+               void release() {
+                       try {
+                               if (p != null) {
+                                       if (dolog)
+                                               log().log(Level.DEBUG, () -> String.format("  releas $%016x %s", p.toRawLongValue(), jtype.getName()));
+
+                                       Method mm = jtype.getDeclaredMethod("release", MemoryAddress.class);
+                                       mm.setAccessible(true);
+                                       mm.invoke(null, p);
+                               }
+                       } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                               log().log(Level.ERROR, jtype.getName(), ex);
+                       } finally {
+                               p = null;
+                       }
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       return (obj instanceof CHandle) && ((CHandle)obj).p == p;
+               }
+
+               @Override
+               public int hashCode() {
+                       //return p.hashCode();
+                       return hashCode(p);
+               }
+
+               /**
+                * Simple hashcode for native pointers.
+                * <p>
+                * This simply strips the bottom 4 bits from the pointer as
+                * on a 64-bit system the low 3 bits are typically zero and the 4th
+                * isn't very well distributed.
+                *
+                * @param p
+                * @return
+                */
+               public static final int hashCode(long p) {
+                       return (int)p >>> 4;
+               }
+
+               /**
+                * Sigh, memoryaddress has a miserable hashCode(), it's even worse than Long.hashCode()
+                */
+               public static final int hashCode(MemoryAddress p) {
+                       return p.hashCode() >>> 5;
+               }
+       }
+
+       public static void debugFlushAll() {
+               for (int i = 0; i < 3; i++) {
+                       try {
+                               System.gc();
+                               Thread.sleep(100);
+                       } catch (InterruptedException x) {
+                       }
+                       CHandle stale = (CHandle)references.poll();
+                       while (stale != null) {
+                               try {
+                                       synchronized (map) {
+                                               map.remove(stale.p);
+                                       }
+                                       stale.release();
+                               } catch (Throwable ex) {
+                               }
+                               stale = (CHandle)references.poll();
+                       }
+               }
+       }
+
+       public static void debugDumpReachable(String title) {
+               synchronized (map) {
+                       System.out.println(title);
+                       for (CHandle h: map.table) {
+                               while (h != null) {
+                                       Native o = h.get();
+                                       System.out.printf(" $%016x: %s %-40s %s\n",
+                                               h.p.toRawLongValue(),
+                                               o == null ? "dead" : "live",
+                                               h.jtype.getName(),
+                                               o);
+                                       h = h.next;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Lightweight pointer hashtable.
+        * <p>
+        * This serves two purposes:
+        * <ol>
+        * <li>Track and resolve unique objects based on memory address;
+        * <li>Hold hard references to the WeakReference as required by the gc system.
+        * </ol>
+        * <p>
+        * CHandle's are chained directly from the index table, the p field
+        * is used as a key directly, and hash values are not cached. This combines
+        * to save significant memory per node.
+        */
+       private static class PointerTable {
+
+               int mask = 63;
+               int size = 0;
+               CHandle[] table = new CHandle[64];
+
+               private void resize(int length) {
+                       CHandle[] ntable = new CHandle[length];
+                       int nmask = length - 1;
+
+                       for (int i = 0; i < table.length; i++) {
+                               CHandle h = table[i];
+
+                               while (h != null) {
+                                       CHandle n = h.next;
+                                       int k = h.hashCode() & nmask;
+
+                                       h.next = ntable[k];
+                                       ntable[k] = h;
+
+                                       h = n;
+                               }
+                       }
+
+                       table = ntable;
+                       mask = nmask;
+               }
+
+               public CHandle put(CHandle h) {
+                       CHandle o = remove(h.p);
+
+                       putAlways(h);
+
+                       return o;
+               }
+
+               public void putAlways(CHandle h) {
+                       if (size > table.length)
+                               resize(table.length * 2);
+
+                       int i = h.hashCode() & mask;
+
+                       h.next = table[i];
+                       table[i] = h;
+                       size += 1;
+               }
+
+               public CHandle get(MemoryAddress p) {
+                       int i = CHandle.hashCode(p) & mask;
+                       CHandle h = table[i];
+
+                       while (h != null && !h.p.equals(p))
+                               h = h.next;
+                       return h;
+               }
+
+               public CHandle remove(MemoryAddress p) {
+                       int i = CHandle.hashCode(p) & mask;
+                       CHandle h = table[i];
+                       CHandle a = null;
+
+                       while (h != null && !h.p.equals(p)) {
+                               a = h;
+                               h = h.next;
+                       }
+                       if (h != null) {
+                               if (a != null)
+                                       a.next = h.next;
+                               else
+                                       table[i] = h.next;
+                               size -= 1;
+                       }
+
+                       return h;
+               }
+       }
+}
index ba83192..a458279 100644 (file)
@@ -1,22 +1,36 @@
 
 CFLAGS=-g -fPIC
 
-JAVA_HOME ?= /home/notzed/src/openjdk-panama-14
+JAVA_HOME?=/opt/jdk-foreign/jvm/openjdk-19-internal
 JAVAC=$(JAVA_HOME)/bin/javac
 JAVA=$(JAVA_HOME)/bin/java
 
-all: bin/libapi.so bin/api.classes bin/classes/api/test/TestAPI.class
+JAVACFLAGS=--add-modules jdk.incubator.foreign
 
-bin/classes/api/test/TestAPI.class: api/test/TestAPI.java bin/api.classes
-       $(JAVAC) -cp bin/classes -d bin/classes $<
+api_SOURCES := $(wildcard ../src/api/*.java)
+api_demo_SOURCES := $(wildcard src/api/test/*.java)
 
-bin/api.classes: bin/api.pm
-       ../src/generate -d bin/java -t api -c APILib -lapi -s api ./bin/api.pm
-       $(JAVAC) -d bin/classes bin/java/api/*.java
+all::
+       mkdir -p bin
+
+all:: bin/demo.built
+
+bin/api.built: bin/api.gen
+       $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \
+               $(shell find bin/java -name '*.java')
+       touch $@
+
+bin/api.gen: bin/api.pm ../src/generate-native $(api_SOURCES)
+       ../src/generate-native -d bin/java -t proto.api ./bin/api.pm
        touch $@
 
-bin/api.pm: api.h
-       gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@ ./$< -o /dev/null
+bin/api.pm: api.h ../src/export.so
+       gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@~ ./$< -o /dev/null
+       mv $@~ $@
+
+bin/demo.built: $(api_demo_SOURCES) bin/api.built
+       $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(api_demo_SOURCES)
+       touch $@
 
 bin/api.o: api.c api.h
        $(CC) $(CFLAGS) -c -o $@ $<
@@ -24,12 +38,12 @@ bin/api.o: api.c api.h
 bin/libapi.so: bin/api.o
        $(CC) -o $@ -shared $^
 
-check: all
-       $(JAVA) -Djava.library.path=bin -cp bin/classes api.test.TestAPI
+demo: bin/demo.built bin/libapi.so
+       $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+               -Djava.library.path=bin -cp bin/classes \
+               api.test.TestAPI
 
 clean:
        rm -rf bin
 
-.PHONY: check clean
-
-$(if $(filter clean,$(MAKECMDGOALS)),,$(shell mkdir -p bin))
+.PHONY: demo clean all
index 9dbb2a3..0aad6b8 100644 (file)
@@ -3,16 +3,6 @@
 #include <string.h>
 #include "api.h"
 
-void print_data(struct data *data) {
-       while (data) {
-               printf("%p: a=%d b=%d c=%d d=%d", data, data->a, data->b, data->c, data->d);
-               if (data->test_a)
-                       printf(" data->test_a()=%d", data->test_a());
-               printf("\n");
-               data = data->next;
-       }
-}
-
 static void funca(int a) {
        printf("funca: %d\n", a);
 }
@@ -28,6 +18,8 @@ static int funcc(float f) {
 }
 
 void *api_func(const char *name) {
+       printf("requesting function %s\n", name);
+
        if (strcmp(name, "funca") == 0)
                return funca;
        else if (strcmp(name, "funcb") == 0)
@@ -37,3 +29,23 @@ void *api_func(const char *name) {
        else
                return NULL;
 }
+
+void print_data(struct data *data) {
+       while (data) {
+               printf("%p: a=%d b=%d c=%d d=%d", data, data->a, data->b, data->c, data->d);
+               if (data->test_a)
+                       printf(" data->test_a()=%d", data->test_a());
+               printf("\n");
+               data = data->next;
+       }
+}
+
+struct api *api_create(void) {
+       struct api *api = malloc(sizeof(*api));
+
+       api->funca = funca;
+       api->funcb = funcb;
+       api->funcc = funcc;
+
+       return api;
+}
index 7fc1c3d..e4639ca 100644 (file)
@@ -4,17 +4,21 @@ struct data {
 
        int a;
        int b;
-       int c:3;
-       unsigned d:5;
+       int c;          // c:3;  // bitfields not implemented with new generator yet
+       unsigned d;     // d:5;
+
        int (*test_a)(void);
+
+       char array[12];
 };
 
 void print_data(struct data *data);
 
-void *api_func(const char *name);
-
 struct api {
        void (*funca)(int a);
        int (*funcb)(int b);
        int (*funcc)(float b);
 };
+
+void *api_func(const char *name);
+struct api *api_create(void);
diff --git a/test-api/api/test/TestAPI.java b/test-api/api/test/TestAPI.java
deleted file mode 100644 (file)
index b463b9d..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-
-package api.test;
-
-import java.foreign.*;
-import java.foreign.memory.*;
-import java.foreign.annotations.*;
-import java.util.function.IntFunction;
-
-import api.*;
-
-public class TestAPI {
-
-       @NativeHeader()
-       public interface APIExt {
-
-               @NativeFunction(value="(i32)v")
-               public void funca(int a);
-
-               @NativeFunction(value="(i32)i32")
-               public int funcb(short b);
-
-               @NativeFunction(value="(f32)i32")
-               public int funcc(float b);
-
-               @NativeFunction(value="(f32)i32")
-               public int funcd(float b);
-
-               public static boolean exists(APILib lib, String func) {
-               }
-
-               public static APIExt bind(APILib lib) {
-                       APIExt ext = Libraries.bind(APIExt.class, (String name)->{
-                                       Scope ss = s.fork();
-                                       Pointer<Byte> cname = ss.allocateCString(name);
-                                       Pointer<?> p = lib.api_func(cname);
-                                       ss.close();
-
-                                       return new Library.Symbol() {
-                                               public String getName() {
-                                                       return name;
-                                               }
-                                               public Pointer<?> getAddress() {
-                                                       System.out.printf(" %s -> %016x\n", name, p.addr());
-                                                       return p;
-                                               }
-                                       };
-                               });
-               }
-       }
-
-       public static void main(String[] args) {
-               APILib lib = APILib.bind;
-               LayoutType<Data> dataLayout = LayoutType.ofStruct(Data.class);
-
-               try (Scope s = Libraries.libraryScope(lib).fork()) {
-                       Pointer<Data> a = s.allocate(dataLayout);
-                       Pointer<Data> b = s.allocate(dataLayout);
-                       Callback<Call__I> cb = s.allocateCallback(Call__I.class,
-                               () -> {
-                                       return 56;
-                               });
-
-                       Data ad = a.get();
-                       Data bd = b.get();
-
-                       ad.setNext(b);
-                       ad.setA(1);
-                       ad.setB(2);
-                       ad.setC((byte)3);
-                       ad.setD((byte)4);
-
-                       bd.setA(5);
-                       bd.setB(6);
-                       bd.setC((byte)255);
-                       bd.setD((byte)255);
-
-                       bd.setTestA(cb);
-
-                       System.out.println("from a\n");
-                       lib.print_data(a);
-                       System.out.println("\bfrom b\n");
-                       lib.print_data(b);
-
-                       // Try to bind to custom address
-                       APIExt ext = Libraries.bind(APIExt.class, (String name)->{
-                                       Scope ss = s.fork();
-                                       Pointer<Byte> cname = ss.allocateCString(name);
-                                       Pointer<?> p = lib.api_func(cname);
-                                       ss.close();
-
-                                       return new Library.Symbol() {
-                                               public String getName() {
-                                                       return name;
-                                               }
-                                               public Pointer<?> getAddress() {
-                                                       System.out.printf(" %s -> %016x\n", name, p.addr());
-                                                       return p;
-                                               }
-                                       };
-                               });
-
-                       System.out.println("invoke ext interface");
-                       ext.funca(42);
-                       System.out.printf("funb -> %d\n", ext.funcb((short)56));
-                       System.out.printf("func -> %d\n", ext.funcc(56.7198273918723f));
-
-                       ext.funcd(12);
-               }
-       }
-}
diff --git a/test-api/src/api/test/TestAPI.java b/test-api/src/api/test/TestAPI.java
new file mode 100644 (file)
index 0000000..2d8be3b
--- /dev/null
@@ -0,0 +1,69 @@
+
+package api.test;
+
+import jdk.incubator.foreign.*;
+
+import proto.api.*;
+import static proto.api.APILib.*;
+import java.lang.invoke.*;
+
+public class TestAPI {
+
+       public static void main(String[] args) {
+               System.loadLibrary("api");
+
+               try (Frame frame = Memory.createFrame();
+                       ResourceScope scope = ResourceScope.newConfinedScope()) {
+
+                       data a = data.create(frame);
+                       data b = data.create(frame);
+
+                       Memory.FunctionPointer<Call__i32> cb = Call__i32.upcall(() -> {
+                                       return 56;
+                               }, scope);
+
+
+                       a.setNext(b);
+                       a.setA(1);
+                       a.setB(2);
+                       a.setC(3);                      //a.setC((byte)3);
+                       a.setD(4);                      //a.setD((byte)4);
+
+
+                       b.setA(5);
+                       b.setB(6);
+                       b.setC((byte)255);
+                       b.setD((byte)255);
+
+                       b.setTest_a(cb);
+
+                       System.out.println("from a");
+                       print_data(a);
+                       System.out.println("from b");
+                       print_data(b);
+
+                       //api api = proto.api.api.create(frame);
+                       //api.setFunca(api_func(Memory.ByteArray.create("funca", frame)));
+                       //api.setFuncb(api_func(Memory.ByteArray.create("funcb", frame)));
+                       //api.setFuncc(api_func(Memory.ByteArray.create("funcc", frame)));
+
+                       // dynamic lookup
+                       System.out.println("call funca via symbol lookup");
+                       Memory.FunctionPointer<Call_i32_v> funca = Call_i32_v.downcall(api_func(Memory.ByteArray.create("funca", frame)), scope);
+                       System.out.printf(" %s\n", funca.symbol());
+                       funca.function().call(12);
+
+                       api api = api_create();
+
+                       System.out.println("call funca via function table");
+                       api.getFunca().function().call(99);
+
+                       api.setFunca(Call_i32_v.upcall(
+                                       i-> System.out.printf("java.funca: %d\n", i),
+                                       scope));
+
+                       System.out.println("call funca via java upcall");
+                       api.getFunca().function().call(22);
+               }
+       }
+}
diff --git a/test-vulkan/Makefile b/test-vulkan/Makefile
new file mode 100644 (file)
index 0000000..2ecde13
--- /dev/null
@@ -0,0 +1,41 @@
+
+JAVA_HOME=/opt/jdk-foreign/jvm/openjdk-19-internal
+JAVAC=$(JAVA_HOME)/bin/javac
+JAVA=$(JAVA_HOME)/bin/java
+
+zvk_TEMPLATES := $(wildcard template/*.java)
+zvk_SOURCES := $(wildcard src/zvk/*.java)
+zvk_demo_SOURCES := $(wildcard src/zvk/test/*.java)
+
+all::
+       mkdir -p bin
+
+all:: bin/demo.built bin/classes/zvk/test/mandelbrot.bin
+
+bin/api.built: bin/api.gen $(zvk_SOURCES)
+       $(JAVAC) --add-modules jdk.incubator.foreign -d bin/classes \
+               $(shell find bin/gen -name '*.java') \
+               $(zvk_SOURCES)
+       touch $@
+
+bin/api.gen: /usr/share/vulkan/registry/vk.xml generate-vulkan $(zvk_TEMPLATES)
+       PERL_HASH_SEED=0 ./generate-vulkan -d bin/gen
+       touch $@
+
+bin/demo.built: bin/api.built $(zvk_demo_SOURCES)
+       $(JAVAC) --add-modules jdk.incubator.foreign -d bin/classes -cp bin/classes \
+               $(zvk_demo_SOURCES)
+       touch $@
+
+bin/classes/zvk/test/mandelbrot.bin: mandelbrot.comp
+       mkdir -p $(@D)
+       glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+demo: bin/demo.built bin/classes/zvk/test/mandelbrot.bin
+       $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+               -cp bin/classes zvk.test.TestVulkan
+
+clean:
+       rm -rf api bin mandelbrot.pam
+
+.PHONY: demo clean all
diff --git a/test-vulkan/generate-vulkan b/test-vulkan/generate-vulkan
new file mode 100755 (executable)
index 0000000..7409ca4
--- /dev/null
@@ -0,0 +1,2443 @@
+#!/usr/bin/perl
+
+# -*- Mode:perl; perl-indent-level:4;tab-width:4; -*-
+
+# TODO: upcalls are just done manually, see template/PFN*
+# TODO: check the extension function pointer setup/splitting is correct
+# TODO: things that take a Memory.*Array probably don't need to also take a count, if one is defined in the registry
+# TODO: the api constants, where to?
+# TODO: vkDestroyDevice and vkDestroyInstance should close the ResourceScope they have
+
+use Data::Dumper;
+use File::Path qw(make_path);
+
+require XML::Parser;
+
+# these can't really be changed yet
+$targetDirectory = "api";
+$targetPackage = "zvk";
+
+while (@ARGV) {
+       my $cmd = shift(@ARGV);
+
+       if ($cmd eq "-t") {
+               $targetPackage = shift(@ARGV);
+    } elsif ($cmd eq "-d") {
+               $targetDirectory = shift(@ARGV);
+    } elsif ($cmd eq "-v") {
+               $verbose++;
+       }
+}
+
+$xml = XML::Parser->new(Style => 'Objects');
+$doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml');
+
+$registry = @{$doc}[0];
+
+%toDrop = (
+    VkBaseInStructure => 1,
+    VkBaseOutStructure => 1,
+    vkEnumeratePhysicalDeviceGroups => 1,
+    vkEnumeratePhysicalDevices => 1,
+    VkPhysicalDeviceGroupProperties => 1,
+    vkAllocateCommandBuffers => 1,
+    vkMapMemory => 1,
+    );
+
+my %accessMode = (
+    'VkDebugUtilsMessengerCallbackDataEXT' => 3, # actually read-only?
+    'VkClearColorValue' => 3,
+    'VkCommandBufferAllocateInfo' => 3
+    );
+
+
+# unused but this documents all INSTANCE creation functions
+%dynamicInit = (
+    vkCreateInstance => 1,
+    vkEnumeratePhysicalDevices => VkInstance,
+    vkCreateDevice => VkPhysicalDevice,
+    vkAllocateCommandBuffers => VkDevice,
+    vkGetDeviceQueue => VkDevice,
+    vkGetDeviceQueue2 => VkDevice,
+
+    VkInstance => 'instance',
+    VkPhysicalDevice => 'instance,device',# dunno how that works
+    VkCommandBuffer => 'instance,device', # instance is only debugutils
+    VkDevice => 'instance,device',        # instance is only debugutils
+    VkQueue => 'instance,device'          # instance is only debugutils
+
+    );
+
+%typeToJavaPrimitive = (
+    uint8_t => 'byte', char => 'byte',
+    uint16_t => 'short',
+    int32_t => 'int', uint32_t => 'int', int => 'int',
+    int64_t => 'long', uint64_t => 'long', size_t => 'long',
+    float => 'float',
+    double => 'double'
+    );
+
+# must only include java primitives
+%typeSizePrimitive = (
+    "byte" => 8,
+    "short" => 16,
+    "int" => 32,
+    "long" => 64,
+    "float" => 32,
+    "double" => 64
+    );
+
+# pre-load types from external header files
+%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' },
+    );
+# from API Constants section
+%apiConstants = ();
+%alias = ();
+%commands = ();
+@commandsList = ();
+@features = ();
+@extensions = ();
+@bitmaskTypes = ();
+
+%commandsExported = ();
+
+# 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 $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;
+}
+
+sub addRequire {
+    my $list = shift @_;
+    my $z = shift @_;
+    my $sourceName = shift @_;
+    my $source = shift @_;
+    my %req = (
+               name => $z->{name},
+               $sourceName => $source
+               );
+
+    $req{comment} = $z->{comment} if (defined($z->{comment}));
+
+    if ($z->isa('type')) {
+               $req{category} = 'type';
+               push @$list, \%req;
+    } elsif ($z->isa('command')) {
+               $req{category} = 'command';
+               push @$list, \%req;
+    } elsif ($z->isa('enum')) {
+               $req{category} = "enum";
+               $req{alias} = $z->{alias} if (defined($z->{alias}));
+               $req{bitpos} = $z->{bitpos} if (defined($z->{bitpos}));
+               $req{dir} = $z->{dir} if (defined($z->{dir}));
+               $req{extends} = $z->{extends} if (defined($z->{extends}));
+               $req{extnumber} = $z->{extnumber} if (defined($z->{extnumber}));
+               $req{offset} = $z->{offset} if (defined($z->{offset}));
+               $req{value} = $z->{value} if (defined($z->{value}));
+               push @$list, \%req;
+    }
+}
+
+foreach $x (grep  { $_->isa('types') } @{$registry->{Kids}}) {
+    foreach $t (grep { $_->isa('type') } @{$x->{Kids}}) {
+               if (!defined($t->{alias})) {
+                       my $category = $t->{category};
+
+                       if ($category eq 'struct' || $category eq 'union') {
+                               my @members = ();
+
+                               foreach $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();
+                                       }
+
+                                       $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 @oaramTypes = ();
+
+                               foreach $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 $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 $x (grep  { $_->isa('enums') } @{$registry->{Kids}}) {
+    if ($x->{type} eq "enum") {
+               my @members = ();
+               foreach $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 $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 $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};
+                       }
+               }
+    }
+}
+
+foreach $x (grep  { $_->isa('commands') } @{$registry->{Kids}}) {
+    foreach $y (grep  { $_->isa('command') } @{$x->{Kids}}) {
+               if (!defined($y->{alias})) {
+                       my %cmd = ();
+                       my @params = ();
+
+                       $cmd{successcodes} = $y->{successcodes} if (defined $y->{successcodes});
+                       $cmd{errorcodes} = $y->{errorcodes} if (defined $y->{errorcodes});
+
+                       foreach $z (@{$y->{Kids}}) {
+                               if ($z->isa('proto')) {
+                                       $cmd{proto} = scanMember($z);
+                               } elsif ($z->isa('param')) {
+                                       push @params, scanMember($z);
+                               }
+                       }
+
+                       $cmd{params} = \@params;
+
+                       my $name = $cmd{proto}->{name};
+
+                       if ($cmd{proto}->{fullType} eq "") {
+                               print Dumper($y);
+                               die();
+                       }
+                       $cmd{name} = $name;
+                       $commands{$cmd{name}} = \%cmd;
+                       push @commandsList, \%cmd;
+
+               } else {
+                       # want forward ref or not?
+                       $alias{$y->{name}} = $y->{alias};
+               }
+    }
+}
+
+foreach $x (grep  { $_->isa('feature') } @{$registry->{Kids}}) {
+    my %feature = (
+               api => $x->{api},
+               name => $x->{name},
+               number => $x->{number},
+               comment => $x->{comment}
+               );
+    my @requires = ();
+    foreach $y (grep  { $_->isa('require') } @{$x->{Kids}}) {
+               foreach $z (@{$y->{Kids}}) {
+                       addRequire(\@requires, $z, 'feature', \%feature);
+               }
+    }
+    $feature{requires} = \@requires;
+
+    push @features, \%feature;
+}
+
+foreach $x (grep  { $_->isa('extensions') } @{$registry->{Kids}}) {
+    foreach $y (grep  { $_->isa('extension') } @{$x->{Kids}}) {
+               my %extension = (
+                       name => $y->{name},
+                       number => $y->{number},
+                       supported => $y->{supported},
+                       type => $y->{type}
+                       );
+               my @requires = ();
+
+               $extension{requiresExtension} = $y->{requires} if (defined($y->{requires}));
+               $extension{platform} = $y->{platform} if (defined($y->{platform}));
+
+               foreach $z (grep  { $_->isa('require') } @{$y->{Kids}}) {
+                       foreach $a (@{$z->{Kids}}) {
+                               addRequire(\@requires, $a, 'extension', \%extension);
+                       }
+               }
+
+               $extension{requires} = \@requires;
+
+               push @extensions,\%extension;
+    }
+}
+
+my %dump = ();
+
+sub patchRequires {
+    my $x = shift @_;
+    my $r = shift @_;
+
+    if ($r->{category} eq "type") {
+               $dump{$r->{name}} = $r;
+    } elsif ($r->{category} eq "command") {
+               $dump{$r->{name}} = $r;
+    } elsif ($r->{category} eq "enum") {
+               if ($r->{extends}) {
+                       my $type = $data{$r->{extends}};
+                       my %info = ( name => $r->{name} );
+
+                       $info{comment} = $r->{comment} if (defined($r->{comment}));
+                       if (defined($r->{value})) {
+                               $info{value} = $r->{value};
+                       } elsif (defined($r->{bitpos})) {
+                               $info{value} = "".(1<<$r->{bitpos}) if (defined($r->{bitpos}));
+                       } elsif (defined($r->{extnumber})) {
+                               $info{value} = "".(1000000000 + 1000 * ($r->{extnumber} - 1) + $r->{offset});
+                       } elsif (defined($r->{offset})) {
+                               $info{value} = $r->{dir}."".(1000000000 + 1000 * ($x->{number} - 1) + $r->{offset});
+                       } elsif (defined($r->{alias})) {
+                               #print "ignoring enum alias: '$r->{alias}' -> '$r->{name}'\n";
+                               $info{alias} = $r->{alias};
+                       } else {
+                               print Dumper($r);
+                               die();
+                       }
+
+                       push @{$type->{members}}, \%info;
+                       $dump{$r->{extends}} = { category => 'enum', name => $r->{extends} };
+               } else {
+                       my %enum = ( name => $r->{name} );
+
+                       # this is only needed for two values, one int, the other a string
+                       $enum{value} = $r->{value};
+                       $enum{category} = 'enum:define';
+                       $enum{type} = 'const char *' if ($r->{value} =~ m/^".*"$/);
+                       $enum{type} = 'uint32_t' if ($r->{value} =~ m/^[0-9]*$/);
+                       $enum{extension} = $x->{name};
+
+                       die() if (!defined($enum{type}));
+
+                       # extension?  do i care?
+                       $dump{$enum{name}} = { category => 'enum:define', name => $enum{name} };
+                       $data{$enum{name}} = \%enum;
+
+               }
+    } else {
+               die();
+    }
+}
+
+# collate types in each interface
+# TODO: versions separated?  or define one version?
+foreach $x (@features) {
+       my @requires = @{$x->{requires}};
+
+       foreach $r (@requires) {
+               patchRequires($x, $r);
+       }
+}
+
+# patch extensions in and collate all types to include in output
+# TODO: there's various versioning crap here too
+foreach $x (grep { $_->{supported} eq 'vulkan'
+                                          && (!defined($_->{platform}) || $_->{platform} =~ m/xlib|wayland|xcb/) } @extensions) {
+    my @requires = @{$x->{requires}};
+
+    foreach $r (@requires) {
+               patchRequires($x, $r);
+    }
+}
+
+# fix up bitmask type bases
+foreach $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 => \@members,
+                       type => $x->{type}
+                       );
+               $data{$enum{name}} = \%enum;
+    }
+}
+
+# Analyse length optioons
+foreach $x (sort keys %dump) {
+    my $d = $dump{$x};
+    my $name = $d->{name};
+
+    if ($d->{category} eq "command") {
+               analyseLengthParameters($commands{$name});
+    } elsif ($d->{category} eq "struct") {
+               analyseLengthMembers($data{$name});
+    }
+}
+
+# This splits functions into class-based groups
+# i.e. those with a handle as first argument
+# the remaining ones are all VkInstance related static methods.
+%functionSets = ( static => [] );
+foreach $x (sort keys %dump) {
+    my $d = $dump{$x};
+
+    if ($d->{category} eq "command") {
+               my $name = $d->{name};
+
+               $name = $alias{$name} if (defined $alias{$name});
+
+               my $cmd = $commands{$name};
+               my @params = @{$cmd->{params}};
+
+               if ($#params >= 0 && defined($data{$params[0]->{fullType}}->{type})) {
+                       my $t = $params[0]->{fullType};
+
+                       if (defined($functionSets{$t})) {
+                               push @{$functionSets{$t}}, $d->{name};
+                       } else {
+                               my @list = ( $d->{name} );
+                               $functionSets{$t} = \@list;
+                       }
+                       #$set{$params[0]->{fullType}} = 1;
+                       #push @{$set{$params[0]->{fullType}}}, $d->{name};
+               } else {
+                       #print Dumper($d);
+                       push @{$functionSets{static}}, $d->{name};
+               }
+    }
+}
+
+# create list of extensions by type - instance or device - and set extensionType on each function
+%functionByExtensionType = ();
+foreach $y (keys %functionSets) {
+    my $x = $functionSets{$y};
+    foreach $c (@{$x}) {
+               next if ($alias{$c});
+               my $cmd = $commands{$c};
+
+               my $d = $dump{$cmd->{name}};
+               my $ext = $d->{extension};
+               if (defined $ext) {
+                       my $type = $ext->{type};
+
+                       # sigh, some physicaldevice or instance calls come from device extensions... but it's parent is only the instance
+                       # so make sure they're actually in that type
+
+                       my @params = @{$cmd->{params}};
+                       if ($params[0]->{baseType} =~ m/^VkInstance|VkPhysicalDevice$/) {
+                               $type = "instance";
+                       }
+
+                       push @{$functionByExtensionType{$type}}, $c;
+                       $cmd->{extensionType} = $type;
+                       $cmd->{extensionSource} = $ext->{type};
+                       $cmd->{extensionName} = $ext->{name};
+               }
+    }
+}
+
+
+# TODO: unused
+sub findType {
+    my $next = shift @_;
+    my $type;
+
+    #  basetypes?
+
+    print"search $next\n";
+    do {
+               $type = $next;
+               if (defined($alias{$type})) {
+                       $next = $alias{$type};
+                       print "  goto alias\n";
+               } elsif (defined($data{$type})) {
+                       my $d = $data{$type};
+
+                       if ($d->{category} eq "struct") {
+                               return $d;
+                       } elsif ($d->{category} eq "enum") {
+                               return $d;
+                       } elsif ($d->{category} eq "basetype") {
+                               return $d;
+                       } else {
+                               print "category: $d->{category}\n";
+                               die("fuckoff");
+                       }
+               } else {
+                       return $type;
+               }
+               print"  $next\n";
+    } while (defined $next);
+
+    return undefined;
+}
+
+sub functionDescriptorType {
+    my $p = shift @_;
+    my $baseType = $p->{baseType};
+    my $fullType = $p->{fullType};
+
+    if ($fullType =~ m/\*/) {
+               return "Memory.POINTER";
+    } else {
+               my $type;
+
+               # FIXME: This loops to handle a double-alias for some flag types
+               # FIXME: FlagsNV -> FlagsKHR -> FlagBitsKHR
+               # FIXME: maybe fix it elsewhere
+               do {
+                       $baseType = $alias{$baseType} if (defined($alias{$baseType}));
+                       $type = $data{$baseType};
+
+                       #print "\n\n$baseType = ".Dumper($type)."\n\n";
+
+                       if (defined $type->{type}) {
+                               $baseType = $type->{type};
+                               #print "  has subtype -> $baseType\n";
+                       } elsif ($type->{category} eq "enum" || $type->{category} eq "enum:bitmask") {
+                               $baseType = "int32_t";
+                               #print "  is normal enm -> $baseType\n";
+                       }
+               } while ($baseType =~ m/Flags/ && defined($alias{$baseType}));
+
+               return 'Memory.'.uc($typeToJavaPrimitive{$baseType}) if ($typeToJavaPrimitive{$baseType});
+
+               # handles and some hacks
+               if ($baseType =~ m/^PFN_/) {
+                       return "Memory.POINTER";
+               } elsif ($baseType eq "VK_DEFINE_NON_DISPATCHABLE_HANDLE") {
+                       return "Memory.POINTER";
+               } elsif ($baseType eq "VK_DEFINE_HANDLE") {
+                       return "Memory.POINTER";
+               } elsif ($baseType eq "VkFlags") {
+                       return 'Memory.INT';
+               } elsif ($baseType eq "VkFlags64") {
+                       return 'Memory.LONG';
+               }
+
+               $type = $data{$baseType};
+
+               #print "    ** Unhandled base type $baseType  full=$fullType nextalias=$alias{$baseType} orig=$p->{baseType}\n";
+               #print Dumper($type);
+               #die();
+
+               return "$type->{name}.LAYOUT";
+               #die();
+    }
+}
+
+# yuck
+sub functionJavaType {
+    my $p = shift @_;
+    my $baseType = $p->{baseType};
+    my $fullType = $p->{fullType};
+    my $type;
+    my $deref = $fullType =~ tr/*/*/;
+
+    if ($deref > 0 || $fullType =~ m/\[.*\]/) {
+
+               if ($p->{len} == 'null-terminated' && $fullType eq 'const char*') {
+                       return 'String';
+               } elsif ($fullType eq "void*") {
+                       return "MemoryAddress";
+               } elsif ($p->{len} && $fullType eq 'const char* const*') {
+                       return 'String[]';
+               }
+
+               $baseType = $alias{$baseType} if (defined($alias{$baseType}));
+               $type = $data{$baseType};
+
+               # TODO: if len or altlen set, then it's probably an array otherwise it's a holder/pointer
+
+               if (defined $type->{type}) {
+                       $baseType = $type->{type};
+                       #print "  has subtype -> $baseType\n";
+               } elsif ($type->{category} eq "enum" || $type->{category} eq "enum:bitmask") {
+                       $baseType = "int32_t";
+                       #print "  is normal enm -> $baseType\n";
+               }
+
+               # alias?
+               return 'Memory.'.ucfirst($typeToJavaPrimitive{$baseType}).'Array' if (defined $typeToJavaPrimitive{$baseType});
+
+               # handles and some hacks
+               if ($baseType eq "void") {
+                       return "Memory.PointerArray";
+                       #return "void";
+               } elsif ($baseType =~ m/^PFN_/) {
+                       return "MemoryAddress";
+                       #return $type->{name};
+               } 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 $type->{name};
+               } elsif ($baseType eq "VK_DEFINE_HANDLE") {
+                       # could be more typed
+                       return "Memory.HandleArray<$p->{baseType}>";
+                       #return "Memory.PointerArray";
+                       #return $type->{name};
+               } elsif ($baseType eq "VkFlags") {
+                       return 'Memory.IntArray';
+               } elsif ($baseType eq "VkFlags64") {
+                       return 'Memory.LongArray';
+               }
+
+               if (defined($alias{$baseType})) {
+                       $baseType = $alias{$baseType};
+               }
+               $type = $data{$baseType};
+
+               # TODO: function poitners?
+               #print "lookup $fullType -> unhandled base type $baseType -> $type->{name}\n";
+
+               if (defined($type->{name})) {
+                       return $type->{name};
+               } else {
+                       return "MemorySegment";
+               }
+    } else {
+               # FIXME: This loops to handle a double-alias for some flag types
+               # FIXME: FlagsNV -> FlagsKHR -> FlagBitsKHR
+               # FIXME: maybe fix it elsewhere
+               do {
+                       $baseType = $alias{$baseType} if (defined($alias{$baseType}));
+                       $type = $data{$baseType};
+
+                       #print "\n\n$baseType = ".Dumper($type)."\n\n";
+
+                       if (defined $type->{type}) {
+                               $baseType = $type->{type};
+                               #print "  has subtype -> $baseType\n";
+                       } elsif ($type->{category} eq "enum" || $type->{category} eq "enum:bitmask") {
+                               $baseType = "int32_t";
+                               #print "  is normal enm -> $baseType\n";
+                       }
+               } while ($baseType =~ m/Flags/ && defined($alias{$baseType}));
+
+               return $typeToJavaPrimitive{$baseType} if (defined $typeToJavaPrimitive{$baseType});
+
+               # handles and some hacks
+               if ($baseType eq "void") {
+                       return "void";
+               } elsif ($baseType =~ m/^PFN_/) {
+                       return "MemoryAddress";
+                       #return $type->{name};
+               } elsif ($baseType eq "VK_DEFINE_NON_DISPATCHABLE_HANDLE") {
+                       return $type->{name};
+               } elsif ($baseType eq "VK_DEFINE_HANDLE") {
+                       return $type->{name};
+               } elsif ($baseType eq "VkFlags") {
+                       return 'int';
+               } elsif ($baseType eq "VkFlags64") {
+                       return 'long';
+               }
+
+               if (defined($alias{$baseType})) {
+                       $baseType = $alias{$baseType};
+               }
+               $type = $data{$baseType};
+
+               # TODO: function poitners?
+               #print "lookup $fullType -> unhandled base type $baseType -> $type->{name}\n";
+
+               if (defined($type->{name})) {
+                       return $type->{name};
+               } else {
+                       return "Object";
+               }
+    }
+}
+
+sub formatFunctionDescriptor {
+    my $cmd = shift @_;
+    my $proto = $cmd->{proto};
+    my @params = @{$cmd->{params}};
+
+    my $type = $data{$proto->{baseType}};
+    my $nargs = 0;
+    my $desc;
+
+    if ($proto->{fullType} eq "void") {
+               $desc = "FunctionDescriptor.ofVoid(";
+    } else {
+               $desc = "FunctionDescriptor.of(";
+               $desc .= functionDescriptorType($proto);
+               $nargs++;
+    }
+
+    foreach $param (@params) {
+               $desc .= "," if ($nargs++ > 0);
+               $desc .= functionDescriptorType($param);
+               $desc .= ".withName(\"$param->{name}\")";
+    }
+    $desc .= ")";
+    return prettyFormat($desc);
+}
+
+sub find {
+    my $n = shift @_;
+    my @list = @{shift @_};
+
+    foreach $p (@list) {
+               return $p if ($p->{name} eq $n);
+    }
+    return;
+}
+
+# return the core root type, will be VK_DEFINE_HANDLE for handles, etc
+sub getRootType {
+    my $baseType = shift @_;
+    my $type;
+
+    $baseType = $alias{$baseType} if (defined($alias{$baseType}));
+    $type = $data{$baseType};
+
+    if (defined $type->{type}) {
+               $baseType = $type->{type};
+               #print "  has subtype -> $baseType\n";
+    } elsif ($type->{category} eq "enum" || $type->{category} eq "enum:bitmask") {
+               $baseType = "int32_t";
+               #print "  is normal enm -> $baseType\n";
+    }
+
+    return $baseType;
+}
+
+sub analyseLengthParameters {
+    my $cmd = shift @_;
+    my @params = @{$cmd->{params}};
+
+    foreach $param (@params) {
+               if ($param->{len} eq 'null-terminated') {
+                       if ($param->{fullType} eq 'const char*') {
+                               $param->{lenType} = 'string';
+                       } else {
+                               die ("unsupported function length type $param->{fullType}");
+                       }
+               } elsif ($param->{len} =~ m/^(.+),?/) {
+                       my $count = $1;
+                       my $src = find($count, \@params);
+
+                       $src->{lenTarget} = $param->{name};
+
+                       # if count is a pointer then it's for returns
+                       # find out target type to determine structure of parameter
+                       my $rootType = getRootType($param->{baseType});
+                       my $fullType = $param->{fullType};
+                       my $nderef = $fullType =~ tr/*/*/;
+                       my $lenType = 'raw';
+
+                       if ($nderef == 1 && defined $typeToJavaPrimitive{$rootType}) {
+                               $lenType = 'primitive-array';
+                       } elsif ($rootType eq "VK_DEFINE_NON_DISPATCHABLE_HANDLE") {
+                               $lenType = 'handle-array';
+                       } elsif ($rootType eq "VK_DEFINE_HANDLE") {
+                               $lenType = 'handle-array';
+                       } elsif ($nderef == 2) {
+                               $lenType = 'pointer-array';
+                       } else {
+                               $lenType = 'struct-array';
+                       }
+
+                       $src->{lenType} = $lenType.'-count';
+                       $param->{lenType} = $lenType;
+               }
+    }
+}
+
+# can merge with above?
+# not used yet tho
+sub analyseLengthMembers {
+    my $cmd = shift @_;
+    my @params = @{$cmd->{members}};
+
+    foreach $param (@params) {
+               if ($param->{len} eq 'null-terminated') {
+                       if ($param->{fullType} eq 'const char*') {
+                               $param->{lenType} = 'string';
+                       } else {
+                               die ("unsupported function length type $param->{fullType}");
+                       }
+               } elsif ($param->{len} =~ m/^(.+),?/) {
+                       my $count = $1;
+                       my $src = find($count, \@params);
+
+                       $src->{lenTarget} = $param->{name};
+
+                       # find out target type to determine structure of parameter
+                       my $rootType = getRootType($param->{baseType});
+                       my $fullType = $param->{fullType};
+                       my $nderef = $fullType =~ tr/*/*/;
+                       my $lenType = 'raw';
+
+                       if ($nderef == 1 && defined $typeToJavaPrimitive{$rootType}) {
+                               $lenType = 'primitive-array';
+                       } elsif ($rootType eq "VK_DEFINE_NON_DISPATCHABLE_HANDLE") {
+                               $lenType = 'handle-array';
+                       } elsif ($rootType eq "VK_DEFINE_HANDLE") {
+                               $lenType = 'handle-array';
+                       } elsif ($nderef == 2) {
+                               $lenType = 'pointer-array';
+                       } else {
+                               $lenType = 'struct-array';
+                       }
+
+                       $src->{lenType} = $lenType.'-count';
+                       $param->{lenType} = $lenType;
+               }
+    }
+}
+
+# TBD
+sub dumpFunctionPrototype {
+    my $cmd = shift @_;
+    my $proto = $cmd->{proto};
+    my @params = @{$cmd->{params}};
+
+    my $type = $data{$proto->{baseType}};
+    my $nargs = 0;
+
+    if ($proto->{fullType} eq "void") {
+               print "void";
+    } else {
+               print functionJavaType($proto);
+    }
+    print " $proto->{name}(";
+
+    foreach $param (@params) {
+               my $lenType = $param->{lenType};
+               my $fullType = $param->{fullType};
+               my $cderef = ($fullType =~ tr/*/*/);
+
+               #print "\n[ $param->{name} lentype=$lenType ]\n";
+
+               next if ($cderef == 0 && $lenType eq 'primitive-array-count');
+               next if ($cderef == 0 && $lenType eq 'handle-array-count');
+               # keep raw-count, struct-array-count and output counts
+
+               print ', ' if ($nargs++ > 0);
+
+
+               if ($lenType =~ m/-count$/ && $cderef > 0) {
+                       # count is an output
+                       print $typeToJavaPrimitive{getRootType($param->{baseType})}.'[]';
+               } elsif ($lenType eq 'string') {
+                       print 'String';
+               } elsif ($lenType eq 'handle-array') {
+                       # alias?
+                       print $param->{baseType}.'[]';
+               } elsif ($lenType eq 'primitive-array') {
+                       print $typeToJavaPrimitive{getRootType($param->{baseType})}.'[]';
+               } elsif ($lenType eq 'pointer-array') {
+                       # what do i want here, MemoryAddress[]?
+                       # or just MemorySegment
+                       print 'MemorySegment';
+               } elsif ($lenType eq 'struct-array') {
+                       print 'MemorySegment';
+               } else {
+                       print functionJavaType($param);
+               }
+               print ' ';
+               print $param->{name};
+    }
+    print ')';
+}
+
+sub formatPrototype {
+    my $cmd = shift @_;
+    my $static = shift @_;
+
+    my $proto = $cmd->{proto};
+    my @params = @{$cmd->{params}};
+
+    my $nargs = 0;
+    my $jrtype = proto->{fullType} eq 'void' ? 'void' : functionJavaType($proto);
+    my $desc;
+
+    # TODO: don't include return type if it's success
+    $desc = ($cmd->{successcodes} eq 'VK_SUCCESS') ? $desc = 'void' : $jrtype;
+    $desc .= " $proto->{name}(";
+
+    # TODO: static or not
+    foreach $param (@params [($static ? 0 : 1) .. $#params]) {
+               my $fullType = $param->{fullType};
+               my $deref = ($fullType =~ tr/*/*/);
+
+               # implied counts
+               next if ($deref == 0 && $lenType eq 'primitive-array-count');
+               next if ($deref == 0 && $lenType eq 'handle-array-count');
+
+               $desc .= ', ' if ($nargs++ > 0);
+
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               $desc .= "$tname $param->{name}";
+    }
+    $desc .= ')';
+
+    return ($jrtype, $desc);
+}
+
+# TODO: merge with above, maybe pass in params list for different rnage
+# TODO: also return type type
+sub formatCreatePrototype {
+    my $cmd = shift @_;
+    my $static = shift @_;
+
+    my $proto = $cmd->{proto};
+    my @params = @{$cmd->{params}};
+
+    my $nargs = 0;
+    my $jrtype = proto->{fullType} eq 'void' ? 'void' : functionJavaType($proto);
+    my $desc;
+
+    # TODO: don't include return type if it's success
+    #$desc = ($cmd->{successcodes} eq 'VK_SUCCESS') ? $desc = 'void' : $jrtype;
+    $desc = $params[$#params]->{baseType};
+    $desc .= " $proto->{name}(";
+
+    # TODO: static or not
+    foreach $param (@params [($static ? 0 : 1) ..$#params-1]) {
+               my $fullType = $param->{fullType};
+               my $deref = ($fullType =~ tr/*/*/);
+
+               # implied counts
+               next if ($deref == 0 && $lenType eq 'primitive-array-count');
+               next if ($deref == 0 && $lenType eq 'handle-array-count');
+
+               $desc .= ', ' if ($nargs++ > 0);
+
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               $desc .= "$tname $param->{name}";
+    }
+    $desc .= ')';
+
+    return ($jrtype, $desc);
+}
+
+# exports a non-static member function
+# TODO: handle static too
+sub exportFunction {
+    my $f = shift @_;
+    my $cmd = shift @_;
+    my $static = shift @_;
+    my ($jrtype, $prototype) = formatPrototype($cmd, $static);
+    my $index = 0;
+    my @params = @{$cmd->{params}};
+
+    $commandsExported{$cmd->{proto}->{name}} = 1;
+    #print "$jrtype\n";
+    #print Dumper($cmd);
+
+    die("cannot have static extensions") if ($static && $cmd->{extensionType});
+
+    print $f "\tpublic ";
+    print $f "static " if $static;
+    print $f $prototype;
+    print $f "throws Exception" if ($cmd->{successcodes});
+    print $f " {\n";
+
+    # 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";
+
+    # setup
+    foreach $param (@params[($static ? 0 : 1) .. $#params]) {
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               # TODO: if it's an output only then don't copy the count?
+               if ($tname eq 'String') {
+                       print $f "\t\t\tMemorySegment $param->{name}\$h = frame.copy($param->{name});\n";
+               }
+    }
+    # invoke
+    print $f "\t\t\t";
+    print $f "res\$code = ($jrtype)" if ($jrtype ne 'void');
+    print $f "$cmd->{extensionType}Dispatch." if ($cmd->{extensionType});
+    print $f "$cmd->{name}\$FH.invokeExact(\n";
+
+    $index = 0;
+    if (!$static) {
+               print $f "\t\t\t\t(Addressable)address()";
+               print $f ",\n" if ($index++ > 0);
+    }
+
+    foreach $param (@params[($static ? 0 : 1) .. $#params]) {
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               print $f ",\n" if ($index++ > 0);
+
+               if ($tname eq 'String') {
+                       print $f "\t\t\t\t(Addressable)Memory.address($param->{name}\$h)";
+               } elsif ($tname =~ m/Memory\..*Array/) {
+                       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})";
+               } elsif ($tname eq 'MemorySegment' || $tname eq 'MemoryAddress') {
+                       print $f "\t\t\t\t(Addressable)Memory.address($param->{name})";
+               } else {
+                       print $f "\t\t\t\t($tname)$param->{name}";
+               }
+    }
+    print $f ");\n";
+
+    # TODO: create functions how???
+
+    # copy any output data back
+    foreach $param (@params) {
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               next if ($param->{fullType} =~ m/const/);
+
+               if ($tname =~ m/\[\]$/) {
+                       print $f "\t\t\tMemorySegment.ofArray($param->{name}).copyFrom($param->{name}\$h);\n";
+               }
+    }
+
+    if ($cmd->{successcodes}) {
+               my $returnStatement = ($cmd->{successcodes} eq 'VK_SUCCESS') ? 'return' : 'return res$code';
+               foreach $r (split /,/, $cmd->{successcodes}) {
+                       print $f "\t\t\tif (res\$code == VkResult.$r) $returnStatement;\n";
+               }
+    } elsif ($jrtype ne "void") {
+               print $f "\t\t\treturn res\$code;\n";
+    }
+
+    print $f "\t\t} catch (Throwable t) { throw new RuntimeException(t); }\n";
+    if ($cmd->{successcodes}) {
+       # fixme, some exception type(s)
+               print $f "\t\tthrow new Exception(String.format(\"failcode %d\", res\$code));\n";
+    }
+
+    print $f "\t}\n";
+}
+
+# create functions return a handle in their last argument
+sub exportCreateFunction {
+    my $f = shift @_;
+    my $cmd = shift @_;
+    my $static = shift @_;
+    my ($jrtype, $prototype) = formatCreatePrototype($cmd, $static);
+    my $index = 0;
+    my @params = @{$cmd->{params}};
+    my $instanceType = $params[$#params]->{baseType};
+
+    $commandsExported{$cmd->{proto}->{name}} = 1;
+
+    # CHANGE: static
+    print $f "\tpublic ";
+    print $f 'static ' if ($static);
+    print $f $prototype;
+    print $f "throws Exception" if ($cmd->{successcodes});
+    print $f " {\n";
+
+    print $f "\t\t$jrtype res\$code;\n" if ($jrtype ne 'void');
+    print $f "\t\ttry (Frame frame = Memory.createFrame()) {\n";
+
+    # setup
+    # CHANGE: return holder
+    print $f "\t\t\tMemorySegment instance\$h = frame.allocatePointer();\n";
+
+    # CHANGE: range
+    foreach $param (@params[($static ? 0 : 1) .. $#params-1]) {
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               # TODO: if it's an output only then don't copy the count?
+               if ($tname eq 'String') {
+                       print $f "\t\t\tMemorySegment $param->{name}\$h = frame.copy($param->{name});\n";
+               }
+    }
+    # invoke
+    print $f "\t\t\t";
+    print $f "res\$code = ($jrtype)" if ($jrtype ne 'void');
+    print $f "$cmd->{extensionType}Dispatch." if ($cmd->{extensionType});
+    print $f "$cmd->{name}\$FH.invokeExact(\n";
+
+    $index = 0;
+    # CHANGE: no 'this'
+    if (!$static) {
+               print $f "\t\t\t\t(Addressable)address()";
+               print $f ",\n" if ($index++ > 0);
+    }
+
+    # CHANGE: range
+    foreach $param (@params[($static ? 0 : 1) .. $#params-1]) {
+               my $tname = functionJavaType($param);
+               $tname = $alias{$tname} if (defined $alias{$tname});
+
+               print $f ",\n" if ($index++ > 0);
+
+               if ($tname eq 'String') {
+                       print $f "\t\t\t\t(Addressable)Memory.address($param->{name}\$h)";
+               } elsif ($tname =~ m/Memory\..*Array/) {
+                       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})";
+               } elsif ($tname eq 'MemorySegment' || $tname eq 'MemoryAddress') {
+                       print $f "\t\t\t\t(Addressable)Memory.address($param->{name})";
+               } else {
+                       print $f "\t\t\t\t($tname)$param->{name}";
+               }
+    }
+    # CHANGE: return pointer
+    print $f ",\n" if ($index++ > 0);
+    print $f "\t\t\t\t(Addressable)instance\$h.address()";
+
+    print $f ");\n";
+
+    # CHANGE: no output data
+    # # copy any output data back
+    # foreach $param (@params) {
+    #  my $tname = functionJavaType($param);
+    #  $tname = $alias{$tname} if (defined $alias{$tname});
+
+    #  next if ($param->{fullType} =~ m/const/);
+
+    #  if ($tname =~ m/\[\]$/) {
+    #      print $f "\t\t\tMemorySegment.ofArray($param->{name}).copyFrom($param->{name}\$h);\n";
+    #  }
+    # }
+
+    # CHANGE: different return code processing
+    # if ($cmd->{successcodes}) {
+    #  my $returnStatement = ($cmd->{successcodes} eq 'VK_SUCCESS') ? 'return' : 'return res$code';
+    #  foreach $r (split /,/, $cmd->{successcodes}) {
+    #      print $f "\t\tif (res\$code == VkResult.$r) $returnStatement;\n";
+    #  }
+    # } else {
+    #  print $f "\t\treturn $res\$code;\n";
+    # }
+
+    # FIXME: see vkCreateGraphicsPipelines multiple 'success' types
+    # This might cause leaks?
+    print $f "\t\t\tif (res\$code == VkResult.VK_SUCCESS)\n\t" if ($jrtype ne 'void');
+
+    # initialise extension function tables if required
+    if ($data{$instanceType}->{type} eq "VK_DEFINE_HANDLE") {
+               if ($instanceType eq "VkInstance") {
+                       print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0));\n";
+               } elsif ($instanceType =~ m/^VkDevice|VkPhysicalDevice$/) {
+                       print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0), instanceDispatch);\n";
+               } else {
+                       print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0), instanceDispatch, deviceDispatch);\n";
+               }
+    } else {
+               print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0));\n";
+    }
+
+    print $f "\t\t} catch (Throwable t) { throw new RuntimeException(t); }\n";
+
+    # CHANGE: different return code processing
+    # if ($cmd->{successcodes}) {
+    #  # fixme, some exception type(s)
+    #   print $f "\t\tthrow new Exception(String.format(\"failcode %d\", res\$code));\n";
+    # }
+
+    print $f "\t\tthrow new Exception(String.format(\"failcode %d\", res\$code));\n" if ($jrtype ne 'void');
+
+    print $f "\t}\n";
+}
+
+%descToSize = (
+    'Memory.BYTE' => 8,
+    'Memory.SHORT' => 16,
+    'Memory.INT' => 32,
+    'Memory.LONG' => 64,
+    'Memory.FLOAT' => 32,
+    'Memory.DOUBLE' => 64,
+    'Memory.POINTER' => 64
+    );
+
+sub calcMemberSize {
+    my $m = shift @_;
+    my $desc = functionDescriptorType($m);
+    my $size;
+    my $align = 1;
+
+    if (defined $descToSize{$desc}) {
+               $size = $descToSize{$desc};
+               $align = $size;
+    } elsif ($desc =~ m/^(.+)\.LAYOUT/) {
+               #print "\n$desc\n";
+               ($size, $align) = calcSize($data{$1});
+               #print "\n";
+    } else {
+               print Dumper($m);
+               print "description='$desc'\n";
+               die("unknown size");
+    }
+
+    # handle array types
+    if ($m->{fullType} =~ m/\[(\d+)\]/) {
+               #print "array size $m->{fullType} = $1\n";
+               $size *= $1;
+    } elsif ($m->{fullType} =~ m/\[(.+)\]/) {
+               my $value = $apiConstants{$1}->{value};
+               #print "array size $m->{fullType} = $1 = $value\n";
+               $size *= $value;
+    }
+
+    #print "member $m->{name} size=$size align=$align\n";
+    #print Dumper($m);
+    #my $sizeb = $size / 8;
+    #my $alignb = $align / 8;
+    #print "size=$size ($sizeb) align=$align ($alignb) desc=$desc\n\n";
+
+    return ($size, $align);
+}
+
+sub calcSize {
+    my $s = shift @_;
+    my $sizeof = 0;
+
+    return ($s->{bitSize}, $s->{bitAlign}) if (defined $s->{bitSize});
+
+    if ($s->{category} eq "struct") {
+               my $maxAlign = 1;
+
+               for $m (@{$s->{members}}) {
+                       my ($size, $align) = calcMemberSize($m);
+
+                       # my $desc = functionDescriptorType($m);
+                       # my $size;
+                       # my $align = 1;
+
+                       # if (defined $descToSize{$desc}) {
+                       #       $size = $descToSize{$desc};
+                       # } elsif ($desc =~ m/^(.+)\.LAYOUT/) {
+                       #       $size = calcSize($data{$1});
+                       # }
+
+                       $maxAlign = $align if $align > $maxAlign;
+
+                       #           if ($size >=8 && $size <= 64) {
+                       #               $align = $size;
+                       #           } elsif ($size > 64) {
+                       #               $align = 64;
+                       #           } else {
+                       #               $align = 1;
+                       #           }
+
+                       $sizeof = ($sizeof + $align - 1) & ~($align - 1);
+                       $sizeof += $size;
+               }
+
+               $sizeof = ($sizeof + $maxAlign - 1) & ~($maxAlign-1);
+               $s->{bitAlign} = $maxAlign;
+               $s->{bitSize} = $sizeof;
+    } elsif ($s->{category} eq "union") {
+               my $maxAlign = 1;
+
+               for $m (@{$s->{members}}) {
+                       my $desc = functionDescriptorType($m);
+                       my $size;
+                       my $align = 1;
+
+                       if (defined $descToSize{$desc}) {
+                               $size = $descToSize{$desc};
+                       } elsif ($desc =~ m/^(.+)\.LAYOUT/) {
+                               $size = calcSize($data{$1});
+                       }
+
+                       $sizeof = $size if ($size > $sizeof);
+               }
+
+               # FIXME: probably wrong
+               $sizeof = ($sizeof + 63) & ~(63);
+               $s->{bitAlign} = 64;
+               $s->{bitSize} = $sizeof;
+    } elsif ($s->{category} eq "enum:bitmask" || $s->{category} eq "enum") {
+               # hmm,?
+               if ($s->{type} eq "VkFlags64") {
+                       $s->{bitSize} = 64;
+                       $s->{bitAlign} = 64;
+               } else {
+                       $s->{bitSize} = 32;
+                       $s->{bitAlign} = 32;
+               }
+    } else {
+               print Dumper($s);
+               die();
+    }
+
+    return ($s->{bitSize}, $s->{bitAlign});
+}
+
+sub formatLayout {
+    my $s = shift @_;
+    my $layout;
+
+    if ($s->{category} eq 'struct') {
+               my $sizeof = 0;
+               my $index = 0;
+
+               $layout = 'MemoryLayout.structLayout(';
+               for $m (@{$s->{members}}) {
+                       my $type = functionDescriptorType($m);
+                       my ($size, $align) = calcMemberSize($m);
+                       my $pad = $sizeof & ($align - 1);
+
+                       if ($pad) {
+                               $layout .= ',' if ($index++ > 0);
+                               $layout .= "MemoryLayout.paddingLayout($pad)";
+                               $sizeof += $pad;
+                       }
+
+                       #if ($type =~ m/\.LAYOUT$/) {
+                       {
+                               if ($m->{fullType} =~ m/\[(\d+)\]/) {
+                                       my $value = $1;
+                                       #print "s array size $s->{name}.$m->{name} $m->{fullType} = $1\n";
+                                       $type = "MemoryLayout.sequenceLayout($value,\t$type)";
+                               } elsif ($m->{fullType} =~ m/\[(.+)\]/) {
+                                       my $value = $apiConstants{$1}->{value};
+                                       #print "s array size $s->{name}.$m->{name} $m->{fullType} = $1 = $value\n";
+                                       $type = "MemoryLayout.sequenceLayout($value,\t$type)";
+                               }
+                       }
+
+                       $layout .= ',' if ($index++ > 0);
+                       $layout .= $type.".withName(\"$m->{name}\")";
+                       $sizeof += $size;
+               }
+               my $pad = $sizeof & 63;
+               $layout .= ',' if ($pad);
+               $layout .= "MemoryLayout.paddingLayout($pad)" if ($pad);
+               $layout .= ')';
+    } elsif ($s->{category} eq "union") {
+               my $index = 0;
+
+               $layout = 'MemoryLayout.unionLayout(';
+               for $m (@{$s->{members}}) {
+                       my $type = functionDescriptorType($m);
+                       my ($size, $align) = calcMemberSize($m);
+
+                       # not used
+                       #if ($type =~ m/\.LAYOUT$/) {
+                       {
+                               if ($m->{fullType} =~ m/\[(\d+)\]/) {
+                                       my $value = $1;
+                                       #print "u array size $s->{name}.$m->{name} $m->{fullType} = $1\n";
+                                       $type = "MemoryLayout.sequenceLayout($value, $type)";
+                               } elsif ($m->{fullType} =~ m/\[(.+)\]/) {
+                                       my $value = $apiConstants{$1}->{value};
+                                       #print "u array size $s->{name}.$m->{name} $m->{fullType} = $1 = $value\n";
+                                       $type = "MemoryLayout.sequenceLayout($value, $type)";
+                               }
+                       }
+                       $layout .= ',' if ($index++ > 0);
+                       $layout .= $type.".withName(\"$m->{name}\")";
+                       $sizeof = $size if ($size > $sizeof);
+               }
+               my $pad = $sizeof & 63;
+               $layout .= ',' if ($pad);
+               $layout .= "MemoryLayout.paddingLayout($pad)" if ($pad);
+               $layout .= ')';
+    } elsif ($s->{category} eq "enum") {
+    } elsif ($s->{category} eq "enum:bitmask") {
+    } else {
+               #print "X $s->{category}\n";
+    }
+
+    return prettyFormat($layout);
+}
+
+sub prettyFormat {
+    my $java = shift @_;
+
+    $java =~ s/\(/\(\n\t/;
+    $java =~ s/,/,\n\t/g;
+
+    return $java;
+}
+
+#
+
+#print 'VkAccelerationStructureGeometryTrianglesDataKHR'."\n";
+
+# print formatLayout($data{'VkDeviceOrHostAddressConstKHR'});
+
+# print formatLayout($data{'VkAccelerationStructureGeometryTrianglesDataKHR'});
+# print formatLayout($data{'VkApplicationInfo'});
+# print "\n";
+
+# ($size,$align) = calcSize($data{'VkAccelerationStructureGeometryTrianglesDataKHR'});
+# $bytes = $size / 8;
+# print "bytes=$bytes bits=$size align=$align\n";
+# exit 0;
+
+if (0) {
+    foreach $k (sort keys %functionSets) {
+               my $list = $functionSets{$k};
+
+               print "first: $k\n";
+
+               foreach $c (@$list) {
+                       print " $c\n";
+               }
+    }
+}
+
+
+sub updateAccess {
+    my $name = shift @_;
+    my $callType = shift @_;
+
+    $name = $alias{$name} if (defined $alias{$name});
+
+    my $type = $data{$name};
+    if ($type->{category} eq 'struct' || $type->{category} eq 'union') {
+               if ($callType =~ m/const/) {
+                       $accessMode{$name} |= 2;
+               } else {
+                       $accessMode{$name} |= 1;
+               }
+
+               # handle any nested types too for this parameter
+               foreach $p (@{$type->{members}}) {
+                       updateAccess($p->{baseType}, $callType);
+               }
+    }
+}
+
+# determine which structures are input only or output only
+# i.e. const or not-const
+foreach $x (values %functionSets) {
+    foreach $y (@{$x}) {
+               my $cmd = $commands{$y};
+
+               foreach $p (@{$cmd->{params}}) {
+                       updateAccess($p->{baseType}, $p->{fullType});
+               }
+    }
+}
+
+# look for all 'create' functions - last parameter is a writeable handle*
+# also work out which ones are static or not - first parameter is (dispatchable?) handle
+%functionCreate = ();
+foreach $x (values %functionSets) {
+    foreach $y (@{$x}) {
+               my $cmd = $commands{$y};
+               my @params = @{$cmd->{params}};
+
+               my $first = $params[0];
+               my $last = $params[$#params];
+
+               # handle parent also?
+               my $firstFullType = $first->{fullType};
+               my $lastFullType = $last->{fullType};
+
+               next if !defined $data{$first->{baseType}};
+               next if !defined $data{$last->{baseType}};
+
+               my $static = !($data{$first->{baseType}}->{type} eq 'VK_DEFINE_HANDLE'
+                                          && ($firstFullType =~ tr/*/*/ == 0));
+               my $create = (!($lastFullType =~ m/const/)
+                                         && ($lastFullType =~ tr/*/*/ == 1)
+                                         && !$last->{len}
+                                         && ($data{$last->{baseType}}->{type} eq 'VK_DEFINE_HANDLE'
+                                                 || $data{$last->{baseType}}->{type} eq 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'));
+
+               if ($create) {
+                       # print "Create function $y\n";
+                       # print "  static\n" if $static;
+                       # print '$data{$last->{baseType}}->{type} = ';
+                       # print "$data{$last->{baseType}}->{type}\n";
+                       # print Dumper($cmd);
+                       # print "first ".Dumper($first);
+                       # print "last ".Dumper($last);
+                       $functionCreate{$cmd->{proto}->{name}} = {
+                               create => $create,
+                               static => $static
+                       };
+               }
+    }
+}
+
+# dump all to-dump types
+if (0) {
+    foreach $x (sort keys %dump) {
+               my $d = $dump{$x};
+               my $name = $d->{name};
+
+               $name = $alias{$name} if (defined $alias{$name});
+
+               print "----\n";
+               print Dumper($d);
+
+               if ($d->{category} eq "command") {
+                       print Dumper($commands{$name});
+               } else {
+                       print Dumper($data{$name});
+               }
+               print "\n";
+    }
+}
+
+
+
+# debug shit
+#exportFunction(STDOUT, $commands{'vkGetDeviceProcAddr'});
+
+if (0) {
+    print "layout\n";
+    print formatLayout($data{'VkAccelerationStructureInfoNV'});
+    print "\n";
+    $name = 'VkAccelerationStructureInfoNV';
+    while (defined($name)) {
+               my $s = $data{$name};
+
+               print "$name:\n";
+               print Dumper($s);
+               $name = $alias{$name};
+    }
+    exit 0;
+}
+
+# ######################################################################
+# The main output routine
+
+#my $cmd = $commands{'vkGetQueueCheckpointDataNV'};
+#print Dumper($cmd);
+#dumpFunctionPrototype($cmd);
+#exit 0;
+
+$baseDir = $targetPackage;
+$baseDir =~ s@\.@.@g;
+$baseDir = $targetDirectory.'/'.$baseDir;
+
+make_path($baseDir);
+
+# dump all structure (and enum?) types
+foreach $x (sort keys %dump) {
+    my $d = $dump{$x};
+
+    next if $toDrop{$d->{name}};
+    next if ($d->{category} eq 'command');
+
+    my $name = $d->{name};
+    my $s = $data{$name};
+
+    if ($s->{category} eq "struct" || $s->{category} eq 'union') {
+               my @members = @{$s->{members}};
+               my $isTyped = $#members >= 1
+                       && $members[0]->{name} eq "sType"
+                       && $members[1]->{name} eq "pNext";
+
+               # TODO: handle 'len' and 'altlen' for accessor setup
+               # TODO: find out which are input and which are output values
+               # TODO: could use the function parameters
+
+               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";
+                       while (<$template>) {
+                               print $f $_;
+                       }
+                       close ($template);
+                       next;
+               }
+
+               print $f "package $targetPackage;\n";
+               print $f "import jdk.incubator.foreign.*;\n";
+               print $f "import java.lang.invoke.*;\n";
+
+               print $f "public final class $s->{name} implements Memory.Addressable {\n";
+
+               print $f "\tfinal MemorySegment segment;\n";
+
+               print $f "\t$s->{name}(MemorySegment segment) {\n";
+               print $f "\t\tthis.segment = segment;\n";
+               if ($isTyped && $members[0]->{values}) {
+                       print $f "\t\tsegment.set(Memory.INT, 0, VkStructureType.$members[0]->{values});\n";
+               }
+               print $f "\t}\n";
+
+               print $f "\tpublic final MemoryAddress address() { return segment.address(); }\n";
+
+               # basic factory methods
+               print $f "\tpublic static $s->{name} create(Frame 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 "\t\treturn new $s->{name}(frame.allocateArray(LAYOUT, count));\n";
+                       print $f "\t}\n\n";
+               }
+
+               print $f "\tpublic static $s->{name} create(ResourceScope scope) {\n";
+               print $f "\t\treturn new $s->{name}(MemorySegment.allocateNative(LAYOUT, scope));\n";
+               print $f "\t}\n\n";
+
+               if (!$isTyped) {
+                       print $f "\tpublic static $s->{name} createArray(ResourceScope scope, long count) {\n";
+                       print $f "\t\treturn new $s->{name}(MemorySegment.allocateNative(LAYOUT.byteSize() * count, scope));\n";
+                       print $f "\t}\n\n";
+               }
+
+               # factory for all arguments for non-readonly types
+               # This should only be for typed types i think
+               # actually it should be always for typed types?
+
+               # FIXME: embedded arrays are ignored and only accessed via getters
+               #        ... but if they're primitive arrays they should probably be included as basically inlined
+
+               #if ($isTyped && $#members > 1  && (!defined($accessMode{$s->{name}}) || ($accessMode{$s->{name}} & 2))) {
+               if (!defined($accessMode{$s->{name}}) || ($accessMode{$s->{name}} & 2)) {
+                       # ignore next two here
+                       my @range = @members[($isTyped ? 2 : 0) .. $#members];
+                       # analyse first TODO: put elsewhere
+                       my $totalArgs = 0;
+
+                       foreach $m (@range) {
+                               my $jtype = functionJavaType($m);
+
+                               # hmm, only if short?
+                               if ($jtype eq 'String[]' && $m->{len} =~ m/([^,]+),?/) {
+                                       my $count = find($1, \@range);
+
+                                       if (defined($count)) {
+                                               $count->{lenType} = 'implied-count';
+                                               $m->{lenType} = 'implied';
+                                               $m->{lenTarget} = $count->{name};
+                                       }
+                               }
+
+                               $m->{functionJavaType} = $jtype;
+                               $m->{functionDescriptor} = functionDescriptorType($m);
+
+                               if ($m->{fullType} =~ m/\[(.+)\]/) {
+                                       my $prim = $typeToJavaPrimitive{$m->{baseType}};
+                                       my $repeat = $1;
+
+                                       $repeat = $apiConstants{$repeat} if defined $apiConstants{$repeat};
+
+                                       if ($prim && $repeat <= 9) {
+                                               $m->{shortPrimitiveArrayLength} = $repeat;
+                                               $m->{shortPrimitiveArrayType} = $prim;
+                                               $totalArgs += $repeat;
+                                       }
+                               } elsif (!($m->{functionDescriptor} =~ m/\.LAYOUT$/)) {
+                                       $totalArgs += 1;
+                               }
+                       }
+
+                       # complex factories()
+                       if ($totalArgs > 0 && $s->{category} eq 'struct') {
+                               print $f "\tpublic static $s->{name} create(\n";
+                               print $f "\t\tFrame frame";
+                               foreach $m (@range) {
+                                       my $jtype = functionJavaType($m);
+
+                                       next if ($m->{lenType} eq 'implied-count');
+                                       # ignore embedded structs and handle arrays
+                                       next if ($m->{functionDescriptor} =~ m/\.LAYOUT$/);
+
+                                       if ($m->{shortPrimitiveArrayLength}) {
+                                               for (my $i = 0;$i<$m->{shortPrimitiveArrayLength};$i++) {
+                                                       print $f ",\n";
+                                                       print $f "\t\t$m->{shortPrimitiveArrayType} $m->{name}\$$i";
+                                               }
+                                       } else {
+                                               print $f ",\n";
+                                               print $f "\t\t$jtype $m->{name}";
+                                       }
+                               }
+                               print $f ") {\n";
+                               print $f "\t\t$s->{name} self = create(frame);\n";
+
+                               # Should probably just call set methods, but that's a pain to do here
+                               foreach $m (@range) {
+                                       my $jtype = functionJavaType($m);
+
+                                       next if ($m->{lenType} eq 'implied-count');
+                                       # ignore embedded structs and arrays
+                                       next if ($m->{functionDescriptor} =~ m/\.LAYOUT$/);
+                                       next if ($m->{fullType} =~ m/\[.*\]/ && !$m->{shortPrimitiveArrayLength});
+
+                                       if ($jtype eq 'String' || $jtype eq 'String[]') {
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, frame.copy($m->{name}).address());\n";
+                                               if ($m->{lenType} eq 'implied') {
+                                                       print $f "\t\t$m->{lenTarget}\$VH.set(self.segment, $m->{name} != null ? $m->{name}.length : 0);\n";
+                                               }
+                                       } elsif (defined($data{$jtype})) {
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, Memory.address($m->{name}));\n";
+                                       } elsif ($m->{shortPrimitiveArrayLength}) {
+                                               print $f "$jtype $m->{name}\$array = self.get".ucfirst($m->{name})."();\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/) {
+                                               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?
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, Memory.address($m->{name}));\n";
+                                       } else {
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, $m->{name});\n";
+                                       }
+                               }
+                               print $f "\t\treturn self;\n";
+                               print $f "\t}\n";
+                       }
+
+                       if ($totalArgs > 0 && $s->{category} eq 'union') {
+                               for $m (@range) {
+                                       next if ($m->{lenType} eq 'implied-count');
+                                       # ignore embedded structs and handle arrays
+                                       next if ($m->{functionDescriptor} =~ m/\.LAYOUT$/);
+                                       next if ($m->{fullType} =~ m/\[.*\]/ && !$m->{shortPrimitiveArrayLength});
+
+                                       print $f "\tpublic static $s->{name} create".ucfirst($m->{name})."(\n";
+                                       print $f "\t\tFrame frame";
+                                       my $jtype = functionJavaType($m);
+
+                                       if ($m->{shortPrimitiveArrayLength}) {
+                                               for (my $i = 0;$i<$m->{shortPrimitiveArrayLength};$i++) {
+                                                       print $f ",\n";
+                                                       print $f "\t\t$m->{shortPrimitiveArrayType} $m->{name}\$$i";
+                                               }
+                                       } else {
+                                               print $f ",\n";
+                                               print $f "\t\t$jtype $m->{name}";
+                                       }
+
+                                       print $f ") {\n";
+                                       print $f "\t\t$s->{name} self = create(frame);\n";
+
+                                       print $f "// $jtype\n";
+                                       if ($jtype eq 'String' || $jtype eq 'String[]') {
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, frame.copy($m->{name}).address());\n";
+                                               if ($m->{lenType} eq 'implied') {
+                                                       print $f "\t\t$m->{lenTarget}\$VH.set(self.segment, $m->{name} != null ? $m->{name}.length : 0);\n";
+                                               }
+                                       } elsif (defined($data{$jtype})) {
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, Memory.address($m->{name}));\n";
+                                       } elsif ($m->{shortPrimitiveArrayLength}) {
+                                               print $f "$jtype array = self.get".ucfirst($m->{name})."();\n";
+                                               for (my $i = 0;$i<$m->{shortPrimitiveArrayLength};$i++) {
+                                                       print $f "array.setAtIndex($i, $m->{name}\$$i);\n";
+                                               }
+                                       } elsif ($jtype =~ m/$Memory.*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?
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, Memory.address($m->{name}));\n";
+                                       } else {
+                                               print $f "\t\t$m->{name}\$VH.set(self.segment, $m->{name});\n";
+                                       }
+                                       print $f "\t\treturn self;\n";
+                                       print $f "\t}\n";
+                               }
+                       }
+               }
+
+
+               # accessors
+               my @range = $isTyped ? @members[1..$#members] : @members;
+
+               # for arrays of structs, must obviously be sized properly
+               print $f "public long length() { return segment.byteSize() / LAYOUT.byteSize(); }\n";
+
+               foreach $m (@range) {
+                       my $name = $m->{name};
+                       my $fullType = $m->{fullType};
+                       my $strip = $fullType =~ tr/*/*/;
+
+                       my $jtype = functionJavaType($m);
+                       my $needFrame = $jtype =~ m/String/;
+
+                       # hack
+                       if ($s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR'
+                               && $m->{name} eq 'pGeometries') {
+                               $name = 'pGeometries0';
+                       }
+
+                       # cleanup name for setCamelCase()
+                       $name = ucfirst(substr $name,$strip);
+
+                       # FIXME: MemoryAddress types
+
+                       if ((!defined($accessMode{$s->{name}}) || ($accessMode{$s->{name}} & 2))
+                               && !(functionDescriptorType($m) =~ m/\.LAYOUT$/ || $m->{fullType} =~ m/\[.*\]/)) {
+                               # non-index
+                               print $f "\tpublic void set$name(";
+                               print $f "Frame frame, " if ($needFrame);
+                               print $f "$jtype value) {\n";
+                               if ($jtype eq 'String' || $jtype eq 'String[]') {
+                                       print $f "\t\t$m->{name}\$VH.set(segment, frame.copy(value).address());\n";
+                                       # auto-set length of a String[]
+                                       if ($jtype eq 'String[]' && $m->{len} =~ m/([^,]+),?/) {
+                                               my $src = $1;
+                                               print $f "\t\t$src\$VH.set(segment, value != null ? value.length : 0);\n";
+                                       }
+                               } elsif (defined($data{$jtype})) {
+                                       print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n";
+                               } elsif ($jtype =~ m/$Memory.*Array/) {
+                                       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";
+                               }
+                               print $f "\t}\n";
+
+                               # indexed accessor
+                               print $f "\tpublic void set$name(";
+                               print $f "Frame frame, " if ($needFrame);
+                               print $f "long index, $jtype value) {\n";
+                               print $f "\t\tMemorySegment seg = segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize());\n";
+                               if ($jtype eq 'String' || $jtype eq 'String[]') {
+                                       print $f "\t\t$m->{name}\$VH.set(seg, frame.copy(value).address());\n";
+                                       # auto-set length of a String[]
+                                       if ($jtype eq 'String[]' && $m->{len} =~ m/([^,]+),?/) {
+                                               my $src = $1;
+                                               print $f "\t\t$src\$VH.set(seg, value != null ? value.length : 0);\n";
+                                       }
+                               } elsif (defined($data{$jtype})) {
+                                       print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n";
+                               } elsif ($jtype =~ m/$Memory.*Array/) {
+                                       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";
+                               }
+                               print $f "\t}\n";
+                       }
+
+                       if (!defined($accessMode{$s->{name}}) || ($accessMode{$s->{name}} & 1) || $m->{fullType} =~ m/\[.*\]/ || functionDescriptorType($m) =~ m/\.LAYOUT$/) {
+                               # non-index
+                               print $f "\tpublic $jtype get$name() {\n";
+                               if ($jtype eq 'String') {
+                                       print $f "\t\tMemoryAddress ptr = (MemoryAddress)$m->{name}\$VH.get(segment);\n";
+                                       print $f "\t\treturn ptr.getUtf8String(0);\n";
+                               } elsif ($jtype eq 'String[]') {
+                                       die("not implemented");
+                               } elsif (functionDescriptorType($m) =~ m/\.LAYOUT$/) {
+                                       print $f "\t\tMemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
+                                       print $f "\t\treturn new $jtype(segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize()));\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";
+                                       } else {
+                                               print $f "\t\treturn new $jtype(seg);\n";
+                                       }
+                               } else {
+                                       print $f "\t\treturn ($jtype)$m->{name}\$VH.get(segment);\n";
+                               }
+                               print $f "\t}\n";
+
+                               # indexed accessor FIXME: just run above parameterised with slice offset
+                               # alternative something like:
+                               # VarHandle getFlagsAtIndex = MemoryLayout.sequenceLayout(VkQueueFamilyProperties.LAYOUT)
+                               #    .varHandle(PathElement.sequenceElement(), PathElement.groupElement("queueFlags"));
+                               # int flags = (int)getFlagsAtIndex.get(famprops.address(), (long)j);
+
+                               print $f "\tpublic $jtype get$name(long index) {\n";
+                               if ($jtype eq 'String') {
+                                       # FIXME: This looks ... really wrong
+                                       print $f "\t\tMemoryAddress ptr = (MemoryAddress)$m->{name}\$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize()));\n";
+                                       print $f "\t\treturn ptr.getUtf8String(0);\n";
+                               } elsif ($jtype eq 'String[]') {
+                                       die("not implemented");
+                               } elsif (functionDescriptorType($m) =~ m/\.LAYOUT$/) {
+                                       print $f "\t\tMemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
+                                       print $f "\t\treturn new $jtype(segment.asSlice(index * LAYOUT.byteSize() + LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize()));\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";
+                                       } else {
+                                               print $f "\t\treturn new $jtype(seg);\n";
+                                       }
+                               } else {
+                                       print $f "\t\treturn ($jtype)$m->{name}\$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize()));\n";
+                               }
+                               print $f "\t}\n";
+                       }
+               }
+
+               # insert template parts
+               if (open(my $template, "<", "template/$s->{name}-part.java")) {
+                       print $f "// << inserted from: 'template/$s->{name}-part.java'\n";
+                       while (<$template>) {
+                               print $f $_;
+                       }
+                       close ($template);
+               }
+
+               # layout
+               foreach $m (@members) {
+                       print $f "\t// $m->{fullType} $m->{name}\n";
+               }
+
+               print $f "\tpublic static final GroupLayout LAYOUT = ";
+               print $f formatLayout($s);
+               print $f ";\n";
+               # varhandles
+               foreach $m (@members) {
+                       # embedded structs are handled as offsets in the accessors
+                       if (functionDescriptorType($m) =~ m/\.LAYOUT$/) {
+                               print $f "\t// static final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
+                       } else {
+                               print $f "\tstatic final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
+                       }
+               }
+
+               print $f "}\n";
+               close($f);
+    } elsif ($s->{category} eq "enum" || $s->{category} eq 'enum:bitmask') {
+               my @members = @{$s->{members}};
+               my $type = calcSize($s) == 32 ? "int" : "long";
+               my $L = $type eq "long" ? "L" : "";
+               my %seen = ();
+
+               open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java");
+
+               print $f "package $targetPackage;\n";
+               print $f "public class $s->{name} {\n";
+
+               my $tries = 0;
+               do {
+                       my @later = ();
+                       foreach $m (@members) {
+                               next if ($seen{$m->{name}});
+
+                               if (defined($m->{value})) {
+                                       print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
+                                       print $f "\tpublic static final $type $m->{name} = $m->{value}$L;\n";
+                                       $seen{$m->{name}} = $m;
+                               } elsif (defined($m->{alias})) {
+                                       if ($seen{$m->{alias}}) {
+                                               print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
+                                               print $f "\tpublic static final $type $m->{name} = $m->{alias};\n";
+                                               $seen{$m->{name}} = $m;
+                                       } else {
+                                               push @later, $m;
+                                       }
+                               } else {
+                                       print Dumper($s);
+                                       print Dumper($m);
+                                       die();
+                               }
+                       }
+                       @members = (@later);
+               } while ($#members >= 0 && $tries++ < 5);
+
+               if ($#members >= 0) {
+                       print Dumper($s);
+                       print "Left over\n";
+                       foreach $m (@members) {
+                               print Dumper($m);
+                       }
+                       print "seen\n";
+                       foreach $m (keys %seen) {
+                               print Dumper($seen{$m});
+                       }
+                       die("unable to resolve aliases");
+               }
+
+               # FIXME: this shouldn't be necessary, the other generators should just use the right primitive types
+               if ($type eq "int") {
+                       print $f "final static jdk.incubator.foreign.ValueLayout.OfInt LAYOUT = Memory.INT;\n";
+               } else {
+                       print $f "final static jdk.incubator.foreign.ValueLayout.OfLong LAYOUT = Memory.LONG;\n";
+               }
+
+               print $f "}\n";
+               close($f);
+
+    } elsif ($s->{category} eq 'handle') {
+               open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java");
+
+               print $f "package $targetPackage;\n";
+
+               print $f "import jdk.incubator.foreign.*;\n";
+               print $f "import java.lang.invoke.*;\n";
+
+               print $f "public final class $s->{name} implements Memory.Addressable {\n";
+
+               print $f "\tfinal MemoryAddress address;\n";
+
+               # instances use a different constructor
+               if ($s->{type} eq 'VK_DEFINE_HANDLE') {
+                       print $f "\tfinal DispatchInstance instanceDispatch;\n";
+                       if ($s->{name} eq "VkInstance") {
+                               print $f "\t$s->{name}(MemoryAddress address) {\n";
+                               print $f "\t\tthis.address = address;\n";
+                               print $f "\t\tthis.instanceDispatch = new DispatchInstance(this, Memory.sharedScope());\n";
+                               print $f "\t}\n";
+
+                               print $f "\tpublic static $s->{name} create(MemoryAddress address) {\n";
+                               print $f "\t\treturn address != MemoryAddress.NULL ? new $s->{name}(address) : null;\n";
+                               print $f "\t}\n\n";
+                       } elsif ($s->{name} =~ m/^VkPhysicalDevice/) {
+                               print $f "\t$s->{name}(MemoryAddress address, DispatchInstance instanceDispatch) {\n";
+                               print $f "\t\tthis.address = address;\n";
+                               print $f "\t\tthis.instanceDispatch = instanceDispatch;\n";
+                               print $f "\t}\n";
+
+                               print $f "\tpublic static $s->{name} create(MemoryAddress address, DispatchInstance instanceDispatch) {\n";
+                               print $f "\t\treturn address != MemoryAddress.NULL ? new $s->{name}(address, instanceDispatch) : null;\n";
+                               print $f "\t}\n\n";
+                       } elsif ($s->{name} =~ m/^VkDevice/) {
+                               print $f "\tfinal DispatchDevice deviceDispatch;\n";
+                               print $f "\t$s->{name}(MemoryAddress address, DispatchInstance instanceDispatch) {\n";
+                               print $f "\t\tthis.address = address;\n";
+                               print $f "\t\tthis.instanceDispatch = instanceDispatch;\n";
+                               print $f "\t\tthis.deviceDispatch = new DispatchDevice(this, Memory.sharedScope());\n";
+                               print $f "\t}\n";
+
+                               print $f "\tpublic static $s->{name} create(MemoryAddress address, DispatchInstance instanceDispatch) {\n";
+                               print $f "\t\treturn address != MemoryAddress.NULL ? new $s->{name}(address, instanceDispatch) : null;\n";
+                               print $f "\t}\n\n";
+                       } else {
+                               print $f "\tfinal DispatchDevice deviceDispatch;\n";
+                               print $f "\t$s->{name}(MemoryAddress address, DispatchInstance instanceDispatch, DispatchDevice deviceDispatch) {\n";
+                               print $f "\t\tthis.address = address;\n";
+                               print $f "\t\tthis.instanceDispatch = instanceDispatch;\n";
+                               print $f "\t\tthis.deviceDispatch = deviceDispatch;\n";
+                               print $f "\t}\n";
+
+                               print $f "\tpublic static $s->{name} create(MemoryAddress address, DispatchInstance instanceDispatch, DispatchDevice deviceDispatch) {\n";
+                               print $f "\t\treturn address != MemoryAddress.NULL ? new $s->{name}(address, instanceDispatch, deviceDispatch) : null;\n";
+                               print $f "\t}\n\n";
+                       }
+               } else {
+                       print $f "\t$s->{name}(MemoryAddress address) {\n";
+                       print $f "\t\tthis.address = address;\n";
+                       print $f "\t}\n";
+
+                       print $f "\tpublic static $s->{name} create(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 "\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 final MemoryAddress address() { return address; }\n";
+
+               # add all methods who take this as the first argument
+               if ($s->{type} eq 'VK_DEFINE_HANDLE') {
+                       print "Handle: $s->{name}\n";
+
+                       foreach $c (@{$functionSets{$s->{name}}}) {
+                               next if ($alias{$c} || $toDrop{$c});
+
+                               my $cmd = $commands{$c};
+                               my $desc = formatFunctionDescriptor($cmd);
+
+                               $desc =~ s/^/\t\t/mg;
+                               #print "// MethodHandle $cmd->{name}\n";
+                               #print Dumper($cmd);
+
+                               if (!defined $cmd->{extensionType}) {
+                                       print $f "\tstatic final MethodHandle $cmd->{name}\$FH = Memory.downcall(\n\t\t\"$cmd->{name}\",\n";
+                                       print $f $desc;
+                                       print $f ");\n";
+                               }
+
+                               print $f "\t/**\n\t * $cmd->{proto}->{fullType} $cmd->{name} (";
+                               foreach $m (@{$cmd->{params}}) {
+                                       print $f " $m->{fullType}";
+                               }
+                               print $f " )\n";
+                               print $f "\t * Requires $cmd->{extensionSource} extension \"$cmd->{extensionName}\"\n" if ($cmd->{extensionType});
+                               print $f "\t * Success Codes: $cmd->{successcodes}\n", if $cmd->{successcodes};
+                               print $f "\t *   Error Codes: $cmd->{errorcodes}\n", if $cmd->{errorcodes};
+                               print $f "\t */\n";
+
+                               if ($functionCreate{$c}) {
+                                       -                   exportCreateFunction($f, $cmd, 0)
+                               } else {
+                                       exportFunction($f, $cmd, 0);
+                               }
+
+                               # if ($c =~ m/^vkCreateDevice|vkGetDeviceQueue$/) {
+                               #     exportCreateFunction($f, $cmd, 0);
+                               # }
+                       }
+
+                       # and dump the static functions here too
+                       if ($s->{name} eq "VkInstance") {
+                               foreach $c (@{$functionSets{static}}) {
+                                       next if ($alias{$c} || $toDrop{$c});
+
+                                       my $cmd = $commands{$c};
+                                       my $desc = formatFunctionDescriptor($cmd);
+
+                                       $desc =~ s/^/\t\t/mg;
+
+                                       #print "// static MethodHandle $cmd->{name}\n";
+                                       #print Dumper($cmd);
+
+                                       print $f "\t// $cmd->{proto}->{fullType} $cmd->{name} (";
+                                       foreach $m (@{$cmd->{params}}) {
+                                               print $f " $m->{fullType}";
+                                       }
+                                       print $f " )\n";
+
+                                       print $f "\tstatic final MethodHandle $cmd->{name}\$FH = Memory.downcall(\n\t\t\"$cmd->{name}\",\n";
+                                       print $f $desc;
+                                       print $f ");\n";
+
+                                       # FIXME: need to make these non-static/init at startup, or probably - depending on extensions requested
+                                       my $d = $dump{$cmd->{name}};
+                                       if ($d->{extension}) {
+                                               print $f "\t// EXTENSION $d->{extension}->{name}\n";
+                                       }
+
+                                       if ($functionCreate{$c}) {
+                                               exportCreateFunction($f, $cmd, 1);
+                                       } else {
+                                               exportFunction($f, $cmd, 1);
+                                       }
+                                       # if ($c eq 'vkCreateInstance') {
+                                       #       exportCreateFunction($f, $cmd, 1);
+                                       # }
+                               }
+                       }
+
+                       if (open(my $template, "<", "template/$s->{name}-part.java")) {
+                               print $f "// << inserted from: 'template/$s->{name}-part.java'\n";
+                               while (<$template>) {
+                                       print $f $_;
+                               }
+                               close ($template);
+                       }
+               }
+
+               print $f "}\n";
+               close($f);
+    } else {
+               #print "X $s->{category}\n";
+    }
+
+}
+
+# ###################################################################### #
+# create extension handles
+my %byExtension = ();
+my %byType = ();
+my %byHandle = ();
+foreach $y (keys %functionSets) {
+    my $x = $functionSets{$y};
+    foreach $c (@{$x}) {
+               next if ($alias{$c});
+               my $cmd = $commands{$c};
+
+               my $d = $dump{$cmd->{name}};
+               my $ext = $d->{extension};
+               if (defined $ext) {
+                       push @{$byExtension{$ext->{name}}}, $c;
+                       push @{$byType{$ext->{type}}}, $c;
+                       push @{$byHandle{$y}}, "$ext->{type} $c";
+               }
+    }
+}
+
+if (0) {
+       foreach $ext (sort keys %byHandle) {
+               print "extension $ext\n";
+               foreach $c (sort @{$byHandle{$ext}}) {
+                       print " $c\n";
+               }
+       }
+}
+
+foreach $ext (sort keys %functionByExtensionType) {
+    my @list = @{$functionByExtensionType{$ext}};
+    my $className = 'Dispatch'.ucfirst($ext);
+    my $typeName = ucfirst($ext);
+
+    open(my $f, ">", "$baseDir/$className.java") || die("unable to open $baseDir/$s->{name}.java");
+
+    print $f "package $targetPackage;\n";
+
+    print $f "import jdk.incubator.foreign.*;\n";
+    print $f "import java.lang.invoke.*;\n";
+
+    print $f "final class $className {\n";
+
+    my @inits = ();
+    my $index = 'a';
+
+    foreach $c (sort @list) {
+               my $cmd = $commands{$c};
+
+               print $f " final MethodHandle $cmd->{name}\$FH;\n";
+    }
+
+    my $count = 0;
+
+    print $f " $className(Vk$typeName o, ResourceScope scope) {\n";
+
+    foreach $c (sort @list) {
+               my $cmd = $commands{$c};
+               my $desc = formatFunctionDescriptor($cmd);
+
+               # if ($count == 0) {
+               #     print $f " private void init_$index($typeName o, ResourceScope scope) {\n";
+               #     push @inits,"init_$index";
+               #     $index++;
+               # }
+               print $f "  $cmd->{name}\$FH = Memory.downcall(\n";
+               print $f "    \"$cmd->{name}\",\n";
+               print $f "    o.vkGet$typeName"."ProcAddr(\"$cmd->{name}\"),\n";
+               print $f $desc;
+               print $f ",\n    scope);\n";
+
+               # if ($count++ == 100) {
+               #     print $f " }\n";
+               #     $count = 0;
+               # }
+    }
+
+    #print $f " }\n" if $count;
+
+    # foreach $init (@inits) {
+    #  print $f "  $init(o, scope);\n";
+    # }
+    print $f " }\n";
+
+    print $f "}\n";
+}
+
+if (0) {
+       print "unexported functions:\n";
+       foreach $x (values %functionSets) {
+               foreach $y (@{$x}) {
+                       print " $y alias=$alias{$y}\n" if !$commandsExported{$y};
+               }
+       }
+}
+exit 0;
+
+# functions tst
+foreach $x (sort keys %dump) {
+    my $d = $dump{$x};
+    my $name = $d->{name};
+
+    # use this to check if it's in an alias function, just call the real one
+    #$name = $alias{$name} if (defined $alias{$name});
+
+    if ($d->{category} eq "command") {
+               my $cmd = $commands{$name};
+
+               if (defined($cmd)) {
+                       #print Dumper($d);
+                       #print Dumper($cmd);
+                       if (0) {
+                               print "\tstatic final MethodHandle $cmd->{name}\$FH = NativeZ.downcall(\n\t\t\"$cmd->{name}\",\n\t\t";
+                               dumpFunctionDescriptor($cmd);
+                               print ");\n";
+                       }
+                       print "\tpublic static ";
+                       dumpFunctionPrototype($cmd);
+                       print " {}\n";
+                       print "\n";
+               }
+    }
+}
diff --git a/test-vulkan/mandelbrot.comp b/test-vulkan/mandelbrot.comp
new file mode 100644 (file)
index 0000000..86e2a85
--- /dev/null
@@ -0,0 +1,64 @@
+#version 450
+
+#define WIDTH (1920*1)
+#define HEIGHT (1080*1)
+#define LWS_X 8
+#define LWS_Y 8
+#define LIMIT 10000
+
+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[];
+};
+
+void main() {
+
+       /*
+         In order to fit the work into workgroups, some unnecessary threads are launched.
+         We terminate those threads here.
+       */
+       if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+               return;
+
+       float x = float(gl_GlobalInvocationID.x) / float(WIDTH);
+       float y = float(gl_GlobalInvocationID.y) / float(HEIGHT);
+
+       /*
+         What follows is code for rendering the mandelbrot set.
+       */
+       vec2 uv = vec2(x, (y - 0.5) * (12.0 / 19.0) + 0.5);
+       float n = 0.0;
+       vec2 c = vec2(-.445, 0.0) + (uv - 0.5)*(4.0);
+       vec2 z = vec2(0.0);
+       const int M = LIMIT;
+
+       for (int i = 0; i<M; i++) {
+               z = vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y) + c;
+
+               if (dot(z, z) > 4)
+                       break;
+               n++;
+       }
+
+       // we use a simple cosine palette to determine color:
+       // http://iquilezles.org/www/articles/palettes/palettes.htm
+       float t = float(n) * 500.0 / float(M);
+       vec3 d = vec3(0.5, 0.5, 0.5);
+       vec3 e = vec3(0.5, 0.5, 0.5);
+       vec3 f = vec3(1.0, 1.0, 1.0);
+       vec3 g = vec3(0.00, 0.33, 0.67);
+
+       vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0);
+
+       if (n == M)
+               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;
+}
diff --git a/test-vulkan/src/zvk/Frame.java b/test-vulkan/src/zvk/Frame.java
new file mode 100644 (file)
index 0000000..0514252
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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;
+               }
+       }
+
+}
diff --git a/test-vulkan/src/zvk/Memory.java b/test-vulkan/src/zvk/Memory.java
new file mode 100644 (file)
index 0000000..ac24cdb
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ * 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);
+               }
+       }
+
+}
diff --git a/test-vulkan/src/zvk/PFN_Test.java b/test-vulkan/src/zvk/PFN_Test.java
new file mode 100644 (file)
index 0000000..d8327c5
--- /dev/null
@@ -0,0 +1,107 @@
+
+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);
+                               }
+                       }
+               };
+       }
+}
diff --git a/test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java b/test-vulkan/src/zvk/PFN_vkDebugReportCallbackEXT.java
new file mode 100644 (file)
index 0000000..226ea28
--- /dev/null
@@ -0,0 +1,62 @@
+
+package zvk;
+
+import java.lang.invoke.*;
+import jdk.incubator.foreign.*;
+
+/*
+callback: typedef VkBool32 (*PFN_vkDebugReportCallbackEXT)(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objectType,
+ uint64_t object,
+ size_t location,
+ int32_t messageCode,
+ const char* pLayerPrefix,
+ const char* pMessage,
+ void* pUserData);
+*/
+public class PFN_vkDebugReportCallbackEXT implements Memory.Addressable {
+
+       NativeSymbol symbol;
+
+       public PFN_vkDebugReportCallbackEXT(NativeSymbol symbol) {
+               this.symbol = symbol;
+       }
+
+       public MemoryAddress address() {
+               return symbol.address();
+       }
+
+       public interface Callback {
+               int call(int flags, int type, long thing, long location, int code, String layer, String message);
+       }
+
+       private interface Trampoline {
+               int call(int flags, int type, long thing, long location, int code, MemoryAddress layer, MemoryAddress message, MemoryAddress userData);
+       }
+
+       public static NativeSymbol of(Callback target, ResourceScope scope) {
+               Trampoline trampline = (int flags, int type, long thing, long location, int code, MemoryAddress layer, MemoryAddress message, MemoryAddress userData) -> {
+                       try {
+                               return target.call(flags, type, thing, location, code,
+                                       layer.getUtf8String(0),
+                                       message.getUtf8String(0));
+                       } catch (Exception x) {
+                       }
+                       return 0;
+               };
+               return Memory.upcall(trampline, "call",
+                       "(IIJJILjdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress)I",
+                       FunctionDescriptor.of(
+                               Memory.INT,
+                               Memory.INT,
+                               Memory.INT,
+                               Memory.LONG,
+                               Memory.LONG,
+                               Memory.INT,
+                               Memory.POINTER,
+                               Memory.POINTER,
+                               Memory.POINTER),
+                       scope);
+       }
+}
diff --git a/test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java b/test-vulkan/src/zvk/PFN_vkDebugUtilsMessengerCallbackEXT.java
new file mode 100644 (file)
index 0000000..873d694
--- /dev/null
@@ -0,0 +1,49 @@
+
+package zvk;
+
+import java.lang.invoke.*;
+import jdk.incubator.foreign.*;
+
+public class PFN_vkDebugUtilsMessengerCallbackEXT implements Memory.Addressable {
+
+       NativeSymbol symbol;
+
+       public PFN_vkDebugUtilsMessengerCallbackEXT(NativeSymbol symbol) {
+               this.symbol = symbol;
+       }
+
+       public MemoryAddress address() {
+               return symbol.address();
+       }
+
+       public interface Callback {
+               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) {
+               Trampoline trampline = (severity, flags, addr, userData) -> {
+                       try {
+                               return target.call(severity, flags,
+                                       new VkDebugUtilsMessengerCallbackDataEXT(
+                                               MemorySegment.ofAddress(addr,
+                                                       VkDebugUtilsMessengerCallbackDataEXT.LAYOUT.byteSize(),
+                                                       ResourceScope.globalScope())));
+                       } catch (Exception x) {
+                       }
+                       return 0;
+               };
+               return Memory.upcall(trampline, "call",
+                       "(IILjdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;)I",
+                       FunctionDescriptor.of(
+                               Memory.INT,
+                               Memory.INT,
+                               Memory.INT,
+                               Memory.POINTER,
+                               Memory.POINTER),
+                       scope);
+       }
+}
diff --git a/test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java b/test-vulkan/src/zvk/PFN_vkDeviceMemoryReportCallbackEXT.java
new file mode 100644 (file)
index 0000000..dfda985
--- /dev/null
@@ -0,0 +1,48 @@
+
+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);
+       }
+}
diff --git a/test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java b/test-vulkan/src/zvk/VkPhysicalDeviceGroupProperties.java
new file mode 100644 (file)
index 0000000..48bd7e6
--- /dev/null
@@ -0,0 +1,80 @@
+package zvk;
+import jdk.incubator.foreign.*;
+import java.lang.invoke.*;
+public final class VkPhysicalDeviceGroupProperties implements Memory.Addressable {
+       final MemorySegment segment;
+       final DispatchInstance instanceDispatch;
+
+       VkPhysicalDeviceGroupProperties(MemorySegment segment, DispatchInstance instanceDispatch) {
+               this.segment = segment;
+               this.instanceDispatch = instanceDispatch;
+               segment.set(Memory.INT, 0, VkStructureType.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES);
+       }
+       public final MemoryAddress address() { return segment.address(); }
+       /*
+       public static VkPhysicalDeviceGroupProperties create(Frame frame) {
+               return new VkPhysicalDeviceGroupProperties(frame.allocate(LAYOUT));
+       }
+
+       public static VkPhysicalDeviceGroupProperties create(ResourceScope scope) {
+               return new VkPhysicalDeviceGroupProperties(MemorySegment.allocateNative(LAYOUT, scope));
+               }*/
+
+       // len= altLen=
+       MemoryAddress getNext() {
+               return (MemoryAddress)pNext$VH.get(segment);
+       }
+       MemoryAddress getNext(long index) {
+               return (MemoryAddress)pNext$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize()));
+       }
+
+       public long length() {
+               return segment.byteSize() / LAYOUT.byteSize();
+       }
+
+       // len= altLen=
+       int getPhysicalDeviceCount() {
+               return (int)physicalDeviceCount$VH.get(segment);
+       }
+       int getPhysicalDeviceCount(long index) {
+               return (int)physicalDeviceCount$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize()));
+       }
+       // len= altLen=
+       Memory.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);
+       }
+       Memory.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);
+       }
+       // len= altLen=
+       int getSubsetAllocation() {
+               return (int)subsetAllocation$VH.get(segment);
+       }
+       int getSubsetAllocation(long index) {
+               return (int)subsetAllocation$VH.get(segment.asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize()));
+       }
+       // VkStructureType sType
+       // void* pNext
+       // uint32_t physicalDeviceCount
+       // VkPhysicalDevice                 [VK_MAX_DEVICE_GROUP_SIZE] physicalDevices
+       // VkBool32 subsetAllocation
+       public static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+       Memory.INT.withName("sType"),
+       MemoryLayout.paddingLayout(32),
+       Memory.POINTER.withName("pNext"),
+       Memory.INT.withName("physicalDeviceCount"),
+       MemoryLayout.paddingLayout(32),
+       MemoryLayout.sequenceLayout(32,
+               Memory.POINTER).withName("physicalDevices"),
+       Memory.INT.withName("subsetAllocation"),
+       MemoryLayout.paddingLayout(32));
+       static final VarHandle sType$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("sType"));
+       static final VarHandle pNext$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("pNext"));
+       static final VarHandle physicalDeviceCount$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("physicalDeviceCount"));
+       static final VarHandle physicalDevices$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("physicalDevices"));
+       static final VarHandle subsetAllocation$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("subsetAllocation"));
+}
diff --git a/test-vulkan/src/zvk/test/TestVulkan.java b/test-vulkan/src/zvk/test/TestVulkan.java
new file mode 100644 (file)
index 0000000..cafef8c
--- /dev/null
@@ -0,0 +1,534 @@
+ /*
+The MIT License (MIT)
+
+Copyright (C) 2017 Eric Arnebäck
+Copyright (C) 2019 Michael Zucchi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+ */
+
+/*
+ * This is a Java conversion of a C conversion of this:
+ * https://github.com/Erkaman/vulkan_minimal_compute
+ *
+ * It's been simplified a bit and converted to the 'zvk' api.
+ */
+
+package zvk.test;
+
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import java.lang.invoke.*;
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.PathElement;
+
+import zvk.*;
+
+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 zvk.VkDebugUtilsMessageSeverityFlagBitsEXT.*;
+import static zvk.VkDebugUtilsMessageTypeFlagBitsEXT.*;
+
+public class TestVulkan {
+       ResourceScope scope = ResourceScope.newSharedScope();
+
+       int WIDTH = 1920*1;
+       int HEIGHT = 1080*1;
+
+       VkInstance instance;
+       VkPhysicalDevice physicalDevice;
+
+       VkDevice device;
+       VkQueue computeQueue;
+
+       long dstBufferSize = WIDTH * HEIGHT * 4 * 4;
+       //VkBuffer dstBuffer;
+       //VkDeviceMemory dstMemory;
+       BufferMemory dst;
+
+       VkDescriptorSetLayout descriptorSetLayout;
+       VkDescriptorPool descriptorPool;
+       Memory.HandleArray<VkDescriptorSet> descriptorSets = VkDescriptorSet.createArray(scope, 1);
+
+       int computeQueueIndex;
+       VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
+
+       String mandelbrot_entry = "main";
+       Memory.IntArray mandelbrot_cs;
+
+       VkShaderModule mandelbrotShader;
+       VkPipelineLayout pipelineLayout;
+       Memory.HandleArray<VkPipeline> computePipeline = VkPipeline.createArray(scope, 1);
+
+       VkCommandPool commandPool;
+       Memory.HandleArray<VkCommandBuffer> commandBuffers;
+
+       record BufferMemory ( VkBuffer buffer, VkDeviceMemory memory ) {};
+
+       VkDebugUtilsMessengerEXT logger;
+
+       void init_debug() throws Exception {
+               try (Frame frame = Memory.createFrame()) {
+                       NativeSymbol cb = PFN_vkDebugUtilsMessengerCallbackEXT.of((severity, flags, data) -> {
+                                       System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+                                       return 0;
+                               }, scope);
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(frame,
+                               0,
+                               VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+                               | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
+                               VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
+                               cb.address(),
+                               null);
+
+                       logger = instance.vkCreateDebugUtilsMessengerEXT(info, null);
+               }
+
+               //typedef VkBool32 (*PFN_vkDebugUtilsMessengerCallbackEXT)(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *, void *);
+
+       }
+
+       void init_instance() throws Exception {
+               try (Frame frame = Memory.createFrame()) {
+                       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" }
+                               );
+
+                       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;
+                       int count;
+                       int res;
+
+                       devs = instance.vkEnumeratePhysicalDevices();
+
+                       int best = 0;
+                       int devid = -1;
+                       int queueid = -1;
+
+                       for (int i=0;i<devs.length();i++) {
+                               VkPhysicalDevice dev = devs.getAtIndex(i);
+                               VkQueueFamilyProperties famprops;
+
+                               // TODO: change to return the allocated array directly
+                               dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, null);
+                               famprops = VkQueueFamilyProperties.createArray(frame, count$h.getAtIndex(0));
+                               dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, famprops);
+
+                               int family_count = count$h.getAtIndex(0);
+
+                               for (int j=0;j<family_count;j++) {
+                                       int score = 0;
+
+                                       if ((famprops.getQueueFlags(j) & VkQueueFlagBits.VK_QUEUE_COMPUTE_BIT) != 0)
+                                               score += 1;
+                                       if ((famprops.getQueueFlags(j) & VkQueueFlagBits.VK_QUEUE_GRAPHICS_BIT) == 0)
+                                               score += 1;
+
+                                       if (score > best) {
+                                               score = best;
+                                               devid = i;
+                                               queueid = j;
+                                       }
+                               }
+                       }
+
+                       if (devid == -1)
+                               throw new Exception("Cannot find a suitable device");
+
+                       computeQueueIndex = queueid;
+                       physicalDevice = devs.getAtIndex(devid);
+
+                       Memory.FloatArray qpri = new Memory.FloatArray(frame, 0.0f);
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+                               frame,
+                               0,
+                               queueid,
+                               1,
+                               qpri);
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               frame,
+                               0,
+                               1,
+                               qinfo,
+                               null,
+                               null,
+                               null);
+
+                       device = physicalDevice.vkCreateDevice(devinfo, null);
+
+                       System.out.printf("device = %s\n", device.address());
+
+                       // NOTE: app scope
+                       deviceMemoryProperties = VkPhysicalDeviceMemoryProperties.create(scope);
+                       physicalDevice.vkGetPhysicalDeviceMemoryProperties(deviceMemoryProperties);
+
+                       computeQueue = device.vkGetDeviceQueue(queueid, 0);
+               }
+       }
+
+       /**
+        * Buffers are created in three steps:
+        * 1) create buffer, specifying usage and size
+        * 2) allocate memory based on memory requirements
+        * 3) bind memory
+        *
+        */
+       BufferMemory init_buffer(long dataSize, int usage, int properties) throws Exception {
+               try (Frame frame = Memory.createFrame()) {
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(frame,
+                               0,
+                               dataSize,
+                               usage,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0,
+                               null);
+
+                       VkBuffer buffer = device.vkCreateBuffer(buf_info, null);
+
+                       device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(frame,
+                               req.getSize(),
+                               find_memory_type(deviceMemoryProperties, req.getMemoryTypeBits(), properties));
+
+                       VkDeviceMemory memory = device.vkAllocateMemory(alloc, null);
+
+                       device.vkBindBufferMemory(buffer, memory, 0);
+
+                       return new BufferMemory(buffer, memory);
+               }
+       }
+
+       /**
+        * Descriptors are used to bind and describe memory blocks
+        * to shaders.
+        *
+        * *Pool is used to allocate descriptors, it is per-device.
+        * *Layout is used to group descriptors for a given pipeline,
+        * The descriptors describe individually-addressable blocks.
+        */
+       void init_descriptor() throws Exception {
+               try (Frame frame = Memory.createFrame()) {
+                       /* Create descriptorset layout */
+                       VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(frame,
+                               0,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               1,
+                               VK_SHADER_STAGE_COMPUTE_BIT,
+                               null);
+
+                       VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(frame,
+                               0,
+                               1,
+                               layout_binding);
+
+                       descriptorSetLayout = device.vkCreateDescriptorSetLayout(descriptor_layout, null);
+
+                       /* Create descriptor pool */
+                       VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(frame,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               1);
+
+                       VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(frame,
+                               0,
+                               1,
+                               1,
+                               type_count);
+
+                       descriptorPool = device.vkCreateDescriptorPool(descriptor_pool, null);
+
+                       /* Allocate from pool */
+                       Memory.HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(frame, 1);
+
+                       layout_table.setAtIndex(0, descriptorSetLayout);
+
+                       VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(frame,
+                               descriptorPool,
+                               1,
+                               layout_table);
+
+                       device.vkAllocateDescriptorSets(alloc_info, descriptorSets);
+
+                       /* Bind a buffer to the descriptor */
+                       VkDescriptorBufferInfo bufferInfo = VkDescriptorBufferInfo.create(frame,
+                               dst.buffer,
+                               0,
+                               dstBufferSize);
+
+                       VkWriteDescriptorSet writeSet = VkWriteDescriptorSet.create(frame,
+                               descriptorSets.getAtIndex(0),
+                               0,
+                               0,
+                               1,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               null,
+                               bufferInfo,
+                               null);
+
+                       device.vkUpdateDescriptorSets(1, writeSet, 0, null);
+               }
+       }
+
+       /**
+        * Create the compute pipeline.  This is the shader and data layouts for it.
+        */
+       void init_pipeline() throws Exception {
+               try (Frame frame = Memory.createFrame()) {
+                       /* Set shader code */
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(frame,
+                               0,
+                               mandelbrot_cs.length() * 4,
+                               mandelbrot_cs);
+
+                       mandelbrotShader = device.vkCreateShaderModule(vsInfo, null);
+
+                       /* Link shader to layout */
+                       Memory.HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(frame, 1);
+
+                       layout_table.setAtIndex(0, descriptorSetLayout);
+
+                       VkPipelineLayoutCreateInfo pipelineinfo = VkPipelineLayoutCreateInfo.create(frame,
+                               0,
+                               1,
+                               layout_table,
+                               0,
+                               null);
+
+                       pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, null);
+
+                       /* Create pipeline */
+                       VkComputePipelineCreateInfo pipeline = VkComputePipelineCreateInfo.create(frame,
+                               0,
+                               pipelineLayout,
+                               null,
+                               0);
+
+                       VkPipelineShaderStageCreateInfo stage = pipeline.getStage();
+
+                       stage.setStage(VK_SHADER_STAGE_COMPUTE_BIT);
+                       stage.setModule(mandelbrotShader);
+                       stage.setName(frame, mandelbrot_entry);
+
+                       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()) {
+                       VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(frame,
+                               0,
+                               computeQueueIndex);
+
+                       commandPool = device.vkCreateCommandPool(poolinfo, null);
+
+                       VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame,
+                               commandPool,
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               1);
+
+                       // should it take a scope?
+                       commandBuffers = device.vkAllocateCommandBuffers(cmdinfo);
+
+                       /* Fill command buffer with commands for later operation */
+                       VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.create(frame,
+                               VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+                               null);
+
+                       commandBuffers.get(0).vkBeginCommandBuffer(beginInfo);
+
+                       /* Bind the compute operation and data */
+                       commandBuffers.get(0).vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.get(0));
+                       commandBuffers.get(0).vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, descriptorSets, 0, null);
+
+                       /* Run it */
+                       commandBuffers.get(0).vkCmdDispatch(WIDTH, HEIGHT, 1);
+
+                       commandBuffers.get(0).vkEndCommandBuffer();
+               }
+       }
+
+       /**
+        * Execute the pre-created command buffer.
+        *
+        * A fence is used to wait for completion.
+        */
+       void execute() throws Exception {
+               try (Frame frame = Memory.createFrame()) {
+                       VkSubmitInfo submitInfo = VkSubmitInfo.create(frame);
+
+                       submitInfo.setCommandBufferCount(0, 1);
+                       submitInfo.setCommandBuffers(0, commandBuffers);
+
+                       /* Create fence to mark the task completion */
+                       VkFence fence;
+                       Memory.HandleArray<VkFence> fences = VkFence.createArray(frame, 1);
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame);
+
+                       // maybe this should take a HandleArray<Fence> rather than being a constructor
+                       fence = device.vkCreateFence(fenceInfo, null);
+                       fences.set(0, fence);
+
+                       /* Await completion */
+                       computeQueue.vkQueueSubmit(1, submitInfo, fence);
+
+                       int VK_TRUE = 1;
+                       int res;
+                       do {
+                               res = device.vkWaitForFences(1, fences, VK_TRUE, 1000000);
+                       } while (res == VkResult.VK_TIMEOUT);
+
+                       device.vkDestroyFence(fence, null);
+               }
+       }
+
+       void shutdown() {
+               device.vkDestroyCommandPool(commandPool, null);
+               device.vkDestroyPipeline(computePipeline.getAtIndex(0), null);
+               device.vkDestroyPipelineLayout(pipelineLayout, null);
+               device.vkDestroyShaderModule(mandelbrotShader, null);
+
+               device.vkDestroyDescriptorPool(descriptorPool, null);
+               device.vkDestroyDescriptorSetLayout(descriptorSetLayout, null);
+
+               device.vkFreeMemory(dst.memory(), null);
+               device.vkDestroyBuffer(dst.buffer(), null);
+
+               device.vkDestroyDevice(null);
+               instance.vkDestroyInstance(null);
+       }
+
+       /**
+        * Accesses the gpu buffer, converts it to RGB byte, and saves it as a pam file.
+        */
+       void save_result() throws Exception {
+               try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+                       MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
+                       byte[] pixels = new byte[WIDTH * HEIGHT * 3];
+
+                       // this is super-slow!
+                       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));
+                       }
+
+                       device.vkUnmapMemory(dst.memory());
+
+                       pam_save("mandelbrot.pam", WIDTH, HEIGHT, 3, pixels);
+               }
+       }
+
+
+       /**
+        * Trivial pnm format image output.
+        */
+       void pam_save(String name, int width, int height, int depth, byte[] pixels) throws IOException {
+               try (FileOutputStream fos = new FileOutputStream(name)) {
+                       fos.write(String.format("P6\n%d\n%d\n255\n", width, height).getBytes());
+                       fos.write(pixels);
+                       System.out.printf("wrote: %s\n", name);
+               }
+       }
+
+       static Memory.IntArray loadSPIRV(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());
+                       int length = Channels.newChannel(is).read(bb);
+
+                       bb.position(0);
+                       bb.limit(length);
+                       return new Memory.IntArray(MemorySegment.ofByteBuffer(bb));
+               }
+       }
+
+       /**
+        * This finds the memory type index for the memory on a specific device.
+        */
+       static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+               VkMemoryType mtypes = memory.getMemoryTypes();
+
+               for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+                       if (((1 << i) & typeMask) != 0 && ((mtypes.getPropertyFlags(i) & query) == query))
+                               return i;
+               }
+               return -1;
+       }
+
+       public static int VK_MAKE_API_VERSION(int variant, int major, int minor, int patch) {
+                return (variant << 29) | (major << 22) | (minor << 12) | patch;
+        }
+
+       void demo() throws Exception {
+               mandelbrot_cs = loadSPIRV("mandelbrot.bin");
+
+               init_instance();
+               //init_debug();
+               init_device();
+
+               dst = init_buffer(dstBufferSize,
+                       VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                       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("Done.");
+
+               shutdown();
+       }
+
+
+       public static void main(String[] args) throws Throwable {
+               System.loadLibrary("vulkan");
+
+               new TestVulkan().demo();
+       }
+}
diff --git a/test-vulkan/template/VkDevice-part.java b/test-vulkan/template/VkDevice-part.java
new file mode 100644 (file)
index 0000000..0de01d2
--- /dev/null
@@ -0,0 +1,62 @@
+
+// cleaner?
+ResourceScope deviceScope = ResourceScope.newSharedScope();
+
+       static final MethodHandle vkAllocateCommandBuffers$FH = Memory.downcall(
+               "vkAllocateCommandBuffers",
+               FunctionDescriptor.of(
+                       Memory.INT,
+                       Memory.POINTER.withName("device"),
+                       Memory.POINTER.withName("pAllocateInfo"),
+                       Memory.POINTER.withName("pCommandBuffers")));
+       /**
+        * VkResult vkAllocateCommandBuffers ( VkDevice const VkCommandBufferAllocateInfo* VkCommandBuffer* )
+        * 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 {
+               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));
+
+                       res$code = (int)vkAllocateCommandBuffers$FH.invokeExact(
+                               (Addressable)address(),
+                               (Addressable)Memory.address(pAllocateInfo),
+                               (Addressable)Memory.address(pCommandBuffers));
+                       if (res$code == VkResult.VK_SUCCESS) return pCommandBuffers;
+               } catch (Throwable t) { throw new RuntimeException(t); }
+               throw new Exception(String.format("failcode %d", res$code));
+       }
+
+       static final MethodHandle vkMapMemory$FH = Memory.downcall(
+               "vkMapMemory",
+               FunctionDescriptor.of(
+                       Memory.INT,
+                       Memory.POINTER.withName("device"),
+                       Memory.POINTER.withName("memory"),
+                       Memory.LONG.withName("offset"),
+                       Memory.LONG.withName("size"),
+                       Memory.INT.withName("flags"),
+                       Memory.POINTER.withName("ppData")));
+       /**
+        * VkResult vkMapMemory ( VkDevice VkDeviceMemory VkDeviceSize VkDeviceSize VkMemoryMapFlags void** )
+        * Success Codes: VK_SUCCESS
+        *   Error Codes: VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_MEMORY_MAP_FAILED
+        */
+       public MemorySegment vkMapMemory(VkDeviceMemory memory, long offset, long size, int flags, ResourceScope scope)throws Exception {
+               int res$code;
+               try (Frame frame = Memory.createFrame()) {
+                       MemorySegment ppData = frame.allocatePointer();
+                       res$code = (int)vkMapMemory$FH.invokeExact(
+                               (Addressable)address(),
+                               (Addressable)Memory.address(memory),
+                               (long)offset,
+                               (long)size,
+                               (int)flags,
+                               (Addressable)Memory.address(ppData));
+                       if (res$code == VkResult.VK_SUCCESS) return MemorySegment.ofAddress(ppData.getAtIndex(Memory.POINTER, 0), size, scope);
+               } catch (Throwable t) { throw new RuntimeException(t); }
+               throw new Exception(String.format("failcode %d", res$code));
+       }
diff --git a/test-vulkan/template/VkInstance-part.java b/test-vulkan/template/VkInstance-part.java
new file mode 100644 (file)
index 0000000..dcd50e6
--- /dev/null
@@ -0,0 +1,79 @@
+
+// cleaner?
+ResourceScope instanceScope = ResourceScope.newSharedScope();
+
+       static final MethodHandle vkEnumeratePhysicalDevices$FH = Memory.downcall(
+               "vkEnumeratePhysicalDevices",
+               FunctionDescriptor.of(
+                       Memory.INT,
+                       Memory.POINTER.withName("instance"),
+                       Memory.POINTER.withName("pPhysicalDeviceCount"),
+                       Memory.POINTER.withName("pPhysicalDevices")));
+       /**
+        * VkResult vkEnumeratePhysicalDevices ( VkInstance uint32_t* VkPhysicalDevice* )
+        */
+       public Memory.HandleArray<VkPhysicalDevice> vkEnumeratePhysicalDevices()throws Exception {
+               int res$code;
+               try (Frame frame = Memory.createFrame()) {
+                       MemorySegment count = frame.allocateInt();
+
+                       res$code = (int)vkEnumeratePhysicalDevices$FH.invokeExact(
+                               (Addressable)address(),
+                               (Addressable)count.address(),
+                               (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));
+
+                               res$code = (int)vkEnumeratePhysicalDevices$FH.invokeExact(
+                                       (Addressable)address(),
+                                       (Addressable)count.address(),
+                                       (Addressable)devices.address());
+
+                               if (res$code == VkResult.VK_SUCCESS)
+                                       return devices;
+                       }
+               } catch (Throwable t) { throw new RuntimeException(t); }
+               throw new Exception(String.format("failcode %d", res$code));
+       }
+
+       static final MethodHandle vkEnumeratePhysicalDeviceGroups$FH = Memory.downcall(
+               "vkEnumeratePhysicalDeviceGroups",
+               FunctionDescriptor.of(
+                       Memory.INT,
+                       Memory.POINTER.withName("instance"),
+                       Memory.POINTER.withName("pPhysicalDeviceGroupCount"),
+                       Memory.POINTER.withName("pPhysicalDeviceGroupProperties")));
+       /**
+        * VkResult vkEnumeratePhysicalDeviceGroups ( VkInstance uint32_t* VkPhysicalDeviceGroupProperties* )
+        */
+       public VkPhysicalDeviceGroupProperties vkEnumeratePhysicalDeviceGroups() throws Exception {
+               int res$code;
+               try (Frame frame = Memory.createFrame()) {
+                       MemorySegment count = frame.allocateInt();
+
+                       res$code = (int)vkEnumeratePhysicalDeviceGroups$FH.invokeExact(
+                               (Addressable)address(),
+                               (Addressable)count.address(),
+                               (Addressable)MemoryAddress.NULL);
+
+                       if (res$code == VkResult.VK_SUCCESS) {
+                               VkPhysicalDeviceGroupProperties properties =
+                                       new VkPhysicalDeviceGroupProperties(
+                                               MemorySegment.allocateNative(count.get(Memory.INT, 0) * VkPhysicalDeviceGroupProperties.LAYOUT.byteSize(), 64, instanceScope),
+                                               instanceDispatch);
+
+                               res$code = (int)vkEnumeratePhysicalDeviceGroups$FH.invokeExact(
+                                       (Addressable)address(),
+                                       (Addressable)count.address(),
+                                       (Addressable)properties.address());
+
+                               if (res$code == VkResult.VK_SUCCESS)
+                                       return properties;
+                       }
+
+               } catch (Throwable t) { throw new RuntimeException(t); }
+               throw new Exception(String.format("failcode %d", res$code));
+       }