--- /dev/null
+
+CFLAGS=-g -fPIC
+
+all::
+ mkdir -p bin
+
+all:: bin/libapi.so
+
+bin/api.o: api.c api.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+bin/libapi.so: bin/api.o
+ $(CC) -o $@ -shared $^
+
+clean:
+ rm -rf bin
+
+.PHONY: clean all
--- /dev/null
+
+#define API_FOO "12"
+#define API_MAKEVERSION(a, b) (a <<16) | b
+#define API_VERSION API_MAKEVERSION(1, 0)
+#define API_A 12UL
+#define API_B 12L
+#define API_C 12U
+#define API_D 12.0
+#define API_E 12.0f
+
+enum api_enum {
+ OK,
+ NOT_OK
+};
+
+union number {
+ int int32;
+ float float32;
+};
+
+struct foo {
+ int a;
+ int b;
+ struct anon *c;
+};
+
+typedef void (*foo_fn1)(float x, float y);
+typedef float (*foo_fn2)(float x, float y);
+
+struct data {
+ struct data *next;
+
+ int a;
+ int b;
+ int c:3;
+ unsigned d:5;
+ int (*test_a)(void);
+
+ enum api_enum status;
+#if 0
+ int (*test_b)(int a, int b, int c);
+ int (*test_c)(int d, int (*foo)(int e, int f), int g);
+ void (*test_d)(void);
+
+ foo_fn test_e;
+ void (*test_f)(foo_fn h);
+
+
+ char array1d[12];
+ char array2d[12][3];
+
+ struct foo foo;
+#endif
+};
+
+void print_data(struct data *data);
+
+struct api {
+ void (*funca)(int a);
+ int (*funcb)(int b);
+ int (*funcc)(float b);
+};
+
+void *api_func(const char *name);
+void api_free(struct api *api);
+struct api *api_create(void);
+void api_void(struct api *, int *);
+
+void api_data(struct api *, int size, void *data);
+
+// constructor which returns object
+struct api *api_new_a(void);
+// constructor which returns object, rc via pointer
+struct api *api_new_b(int *rc);
+// constructor which returns value in pointer-holder, and error via return code
+int api_new_c(struct api **apip);
+// constructor which returns value in pointer-holder, error via ?
+void api_new_d(struct api **apip);
+// constructor which returns value in pointer-holder, and error via return code holder
+void api_new_e(struct api **apip, int *rc);
+
+/*
+struct list_node {
+ struct list_node *succ;
+ struct list_node *pred;
+ char name[64-16];
+};
+*/
--- /dev/null
+
+package apigen;
+
+use strict;
+use Data::Dumper;
+require genconfig;
+
+my %renameTable = (
+ 'studly-caps' => sub { my $s = shift; $s =~ s/(?:^|_)(.)/\U$1/g; return $s; },
+ 'camel-case' => sub { my $s = shift; $s =~ s/(?:_)(.)/\U$1/g; return $s; },
+ 'upper-leadin' => sub { my $s = shift; $s =~ s/^([^_]+)/\U$1/; return $s; },
+ 'identity' => sub { return $_[0]; },
+ 'call' => sub {
+ my $s = shift;
+
+ if ($s =~ m/\(/) {
+ $s =~ s/u32:|u64:/p/g;
+ $s =~ s/\$\{([^\}]+)\}/$1/g;
+ $s =~ s/[\(\)]/_/g;
+ $s =~ s/^/Call/;
+ }
+ $s;
+ }
+
+);
+
+my %defaultTable = (
+ 'struct:<default>' => {
+ name => '<default>',
+ items => [],
+ options => [ 'default=none', 'access=rw' ],
+ regex => qr/^struct:<default>$/,
+ type => 'struct'
+ },
+ 'union:<default>' => {
+ name => '<default>',
+ items => [],
+ options => [ 'default=none', 'access=rw' ],
+ regex => qr/^union:<default>$/,
+ type => 'union'
+ },
+ 'call:<default>' => {
+ name => '<default>',
+ items => [],
+ options => [ 'rename=call', 'access=r' ],
+ regex => qr/^call:<default>$/,
+ type => 'call'
+ },
+);
+
+# hmm, so do i iterate 'control' and find matches in 'api'
+# or do i iterate 'api' and find matches in 'control'?
+
+sub new {
+ my $class = shift;
+ my $file = shift;
+ my $self = { };
+
+ $self->{api} = genconfig::loadControlFile($file);
+ $self->{index} = {};
+ foreach my $obj (@{$self->{api}}) {
+ $self->{index}->{"$obj->{type}:$obj->{name}"} = $obj;
+ }
+ foreach my $k (keys %defaultTable) {
+ $self->{index}->{$k} = $defaultTable{$k} if !defined($self->{index}->{$k});
+ }
+
+ $self->{data} = {};
+ while ($#_ >= 0) {
+ my $name = shift;
+ my $info = loadAPIFile($name);
+
+ $self->{data} = { %{$self->{data}}, %{$info} };
+ }
+
+ analyseAPI($self);
+ preprocess($self);
+
+ # add phantom 'api' entries for anything
+ foreach my $s (findDependencies($self)) {
+ my $n = "$s->{type}:$s->{name}";
+ my $def = $self->{index}->{"$s->{type}:<default>"};
+
+ die "no default for implicit dependency $n" if (!$def);
+
+ my $obj = {
+ name => $s->{name},
+ items => $def->{items},
+ options => $def->{options},
+ regex => qr/^$n$/,
+ type => $def->{type}
+ };
+
+ print " implicit $n\n";
+ push @{$self->{api}}, $obj;
+ $self->{index}->{$n} = $obj;
+ }
+
+ postprocess($self);
+
+ bless $self, $class;
+
+ return $self;
+}
+
+sub renameFunction {
+ my $api = shift;
+ my $name = shift;
+ $renameTable{$name};
+}
+
+# find data objects matching top-level data objects
+# return [ (obj, def) ] pairs in a list
+# if there are multiple matches only return the first
+sub findObjects {
+ my $api = shift;
+ my $type = shift;
+ my @list;
+ my %seen = ();
+
+ #print Dumper($api->{data});
+
+ foreach my $obj (grep { $_->{type} eq $type} @{$api->{api}}) {
+ my $s = $api->{data}->{"$type:$obj->{name}"};
+ print "? $obj->{name} regex $obj->{regex} s=$s\n";
+ if ($s) {
+ next if $seen{$s->{name}}++;
+ push @list, [ $obj, $s ];
+ } else {
+ foreach my $k (grep { $_ =~ $obj->{regex} } keys(%{$api->{data}})) {
+ $s = $api->{data}->{$k};
+ next if $seen{$s->{name}}++;
+ #print "+ $k\n";
+ push @list, [ $obj, $s ];
+ }
+ }
+ }
+
+ return @list;
+}
+
+sub findAllObjects {
+ my $api = shift;
+ my @list;
+
+ foreach my $obj (@{$api->{api}}) {
+ print "? $obj->{name}\n";
+ foreach my $dat (grep { $_->{name} =~ $obj->{regex} } values(%{$api->{data}})) {
+ #print "+ $dat->{name}\n";
+ push @list, [ $obj, $dat ];
+ }
+ }
+
+ return @list;
+}
+
+# TODO: check these next few against the one in generate-api,
+
+# find option(s) in an obj or inc
+sub option {
+ my $obj = shift;
+ my $name = shift;
+ my $rx = qr/^$name=/;
+
+ return grep { $_ =~ m/$rx/ } @{$obj->{options}};
+}
+
+# find value of first option of the given name
+sub optionValue {
+ my $name = shift;
+ my $or = shift;
+ my $rx = qr/$name/;
+
+ foreach my $obj (@_) {
+ foreach my $opt (@{$obj->{options}}) {
+ return $1 if ($opt =~m/^$rx=(.*)/);
+ }
+ }
+ $or;
+}
+
+# look for all matching options of the given name
+# multiple objects are searched, the first one with
+# the given parameter overrides the rest.
+# name, object, object *
+sub optionValues {
+ my $name = shift;
+ my $rx = qr/$name/;
+ my @list;
+
+ foreach my $obj (@_) {
+ foreach my $opt (@{$obj->{options}}) {
+ push @list, $1 if ($opt =~m/^$rx=(.*)/);
+ }
+ last if ($#list >= 0);
+ }
+ @list;
+}
+
+# find first occurance of a flag
+sub optionFlag {
+ my $name = shift;
+
+ foreach my $obj (@_) {
+ foreach my $opt (@{$obj->{options}}) {
+ return 1 if ($opt eq $name);
+ }
+ }
+ 0;
+}
+
+# find live/desired items of a given type under a particular object
+# returns list of [ $inc, $field/argument ]
+sub findItems {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my $mode = shift;
+ my %visited = ();
+ my @fields = ();
+
+ #print Dumper($s);
+
+ my @all = @{$s->{items}};
+ my %index;
+
+ foreach my $m (@all) {
+ $index{$m->{name}} = $m;
+ }
+
+ #print "find mode $mode in $s->{name} $s->{type}\n";
+ #print Dumper($s);
+
+ foreach my $inc (grep { $_->{mode} eq $mode } @{$obj->{items}}) {
+ my $d = $index{$inc->{match}};
+
+ if ($d) {
+ next if $visited{$d->{type}.':'.$d->{name}}++;
+ push @fields, [ $inc, $d ];
+ } else {
+ foreach my $d (grep { $_->{name} =~ m/$inc->{regex}/ } @all) {
+ next if $visited{$d->{type}.':'.$d->{name}}++;
+ push @fields, [ $inc, $d ];
+ }
+ }
+ }
+
+ # FIXME: rather than all, use 'struct:<default>' options for fallback
+ if (optionValue('default', 'all', $obj) eq 'all') {
+ foreach my $d (@all) {
+ next if $visited{$d->{type}.':'.$d->{name}}++;
+ # TODO: other things might need moving over
+ my $inc = {};
+ $inc->{"$mode:rename"} = $obj->{"$mode:rename"} if $obj->{"$mode:rename"};
+ push @fields, [ $inc, $d ];
+ }
+ }
+
+ return @fields;
+}
+
+sub findAllItems {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my %visited = ();
+ my @fields = ();
+
+ #print Dumper($obj);
+
+ my @all = @{$s->{items}};
+ my %index;
+
+ foreach my $m (@all) {
+ $index{$m->{name}} = $m;
+ }
+
+ foreach my $inc (@{$obj->{items}}) {
+ my $d = $index{$inc->{match}};
+
+ if ($d) {
+ next if $visited{$d->{type}.':'.$d->{name}}++;
+ push @fields, [ $inc, $d ];
+ } else {
+ foreach my $d (grep { $_->{name} =~ m/$inc->{regex}/ } @all) {
+ next if $visited{$d->{type}.':'.$d->{name}}++;
+ push @fields, [ $inc, $d ];
+ }
+ }
+ }
+
+ if (optionValue('default', 'all', $obj) eq 'all') {
+ #print "* add all items\n";
+ foreach my $d (@all) {
+ next if $visited{$d->{type}.':'.$d->{name}}++;
+ push @fields, [ $obj, $d ];
+ }
+ }
+
+ return @fields;
+}
+
+# ######################################################################
+
+sub addDependencies {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my $add = shift;
+
+ #print "add deps for '$s->{name}'\n";
+ if ($s->{type} =~ m/^(struct|union)$/) {
+ # include embedded structures always
+ foreach my $d (grep { $_->{type} =~ m/^(struct|union):/ && $_->{deref} =~ m/\[\d+\$\{|^\$\{/ } @{$s->{items}}) {
+ #print " embedded $d->{name} $d->{deref}\n";
+ $add->($d->{type});
+ }
+
+ # include selected fields optionally
+ if ($obj) {
+ foreach my $i (findAllItems($api, $obj, $s)) {
+ my ($inc, $d) = @{$i};
+ #print " selected $d->{name} $d->{type} $d->{deref}\n";
+ $add->($d->{type}) if ($d->{type} =~ m/^(struct|union|func|call|enum):/);
+ }
+ }
+ } elsif ($s->{type} =~ m/^(call|func)/) {
+ # for calls/func need all fields
+ foreach my $d (grep { $_->{type} =~ m/^(struct|union|func|call|enum):/ } @{$s->{items}}, $s->{result}) {
+ #print " argument $d->{name} $d->{type} $d->{deref}\n";
+ $add->($d->{type});
+ }
+ }
+}
+
+# find all extra types used by the api requested
+sub findDependencies {
+ my $api = shift;
+ my %data = %{$api->{data}};
+ my %seen;
+ my %deps;
+ my $setdeps = sub { my $d = shift; $deps{$d} = 1; };
+
+ foreach my $obj (@{$api->{api}}) {
+ if ($obj->{type} eq 'library') {
+ foreach my $inc (@{$obj->{items}}) {
+ print "? $inc->{regex}\n";
+ foreach my $n (grep { $_ =~ m/$inc->{regex}/ } keys %data) {
+ my $s = $data{$n};
+
+ print "+ $n\n";
+
+ $seen{$n}++;
+ addDependencies($api, $obj, $s, $setdeps);
+ }
+ }
+ } elsif ($obj->{type} =~ m/^(struct|union|call|func|enum|define)$/) {
+ foreach my $n (grep { $_ =~ m/$obj->{regex}/ } keys %data) {
+ my $s = $data{$n};
+
+ $seen{$n}++;
+ addDependencies($api, $obj, $s, $setdeps);
+ }
+ }
+ }
+
+ # at this point 'seen' contains everything explicitly requested
+ # and deps is anything else they need but not referenced directly
+ # recursively grab anything else
+
+ my @list = ();
+ my @stack = sort keys %deps;
+ my $pushstack = sub { my $d = shift; push @stack, $d; };
+ while ($#stack >= 0) {
+ my $n = shift @stack;
+ my $s;
+
+ next if $seen{$n}++;
+
+ $s = $data{$n};
+
+ if ($s) {
+ print "Add referent: $n\n";
+ addDependencies($api, $api->{index}->{"$s->{type}:<default>"}, $s, $pushstack);
+ } elsif ($n =~ m/^(.*):(.*)$/) {
+ print "Add anonymous: $n\n";
+ # type not know, add anonymous
+ $s = {
+ name => $2,
+ type => $1,
+ size => 0,
+ items => [],
+ };
+ $api->{data}->{$n} = $s;
+ }
+
+ # maybe it should have some skeleton metadata?
+ # depends on where it's used i suppose
+ push @list, $s;
+ }
+
+ print "Added ".($#list+1)." dependencies\n";
+ return @list;
+}
+
+# ######################################################################
+
+sub loadAPIFile {
+ my $file = shift;
+ my $info;
+
+ unless ($info = do $file) {
+ die "couldn't parse $file: $@" if $@;
+ die "couldn't import $file: $!" unless defined $info;
+ die "couldn't run $file" unless $info;
+ }
+
+ return $info;
+}
+
+sub parseRename {
+ my $how = shift;
+ my $rename = $renameTable{'identity'};
+
+ foreach my $n (split /,/,$how) {
+ my $old = $rename;
+ my $new = $renameTable{$n};
+
+ if ($n =~ m@^s/(.*)/(.*)/$@) {
+ my $rx = qr/$1/;
+ my $rp = $2;
+ $rename = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
+ } elsif ($new) {
+ $rename = sub { my $s=shift; $s = $old->($s); return $new->($s); };
+ } else {
+ my $x = $n;
+ $rename = sub { return $x; };
+ }
+ }
+ $rename;
+}
+
+# take a signature-name and convert it to mangled java name
+sub renameCall {
+ my $new = shift;
+
+ if ($new =~ m/\(/) {
+ $new =~ s/u32:|u64:/p/g;
+ $new =~ s/\$\{([^\}]+)\}/$1/g;
+ $new =~ s/[\(\)]/_/g;
+ $new =~ s/^/Call/;
+ }
+ $new;
+}
+
+# pre-process {data}
+sub preprocess {
+ my $api = shift;
+
+ # Find any anonymous types and add them in
+ my %anonymous = ();
+ foreach my $s (values %{$api->{data}}) {
+ # FIXME: just do this in export.cc and export-defines
+ $s->{items} = $s->{fields} if $s->{fields};
+ $s->{items} = $s->{arguments} if $s->{arguments};
+ $s->{items} = $s->{values} if $s->{values};
+
+ foreach my $m (grep { $_->{type} =~ m/struct:|union:/} @{$s->{items}}) {
+ $anonymous{$m->{type}} = 1 if !defined($api->{data}->{$m->{type}});
+ }
+ }
+
+ foreach my $k (sort keys %anonymous) {
+ print " anon $k\n";
+ if ($k =~ m/^(.*):(.*)$/) {
+ $api->{data}->{$k} = {
+ name => $2,
+ type => $k,
+ size => 0,
+ items => [],
+ };
+ }
+ }
+
+ my $rename = {};
+ foreach my $i (findObjects($api, 'struct')) {
+ my ($obj, $dat) = @{$i};
+
+ if ($obj->{rename}) {
+ my $old = $dat->{name};
+ my $new = $obj->{rename}->($old);
+ my $oldkey = $obj->{type}.':'.$old;
+ my $newkey = $obj->{type}.':'.$new;
+
+ $rename->{$oldkey} = $newkey;
+ }
+ }
+
+ $api->{rename} = $rename;
+}
+
+# preprocess {api}
+sub analyseAPI {
+ my $api = shift;
+
+ # Note that type:name regexes always start at the beginning
+
+ foreach my $obj (@{$api->{api}}) {
+ if ($obj->{name} =~ m@^/(.*)/$@) {
+ $obj->{regex} = qr/^$obj->{type}:$1/;
+ } else {
+ $obj->{regex} = qr/^$obj->{type}:$obj->{name}$/;
+ }
+
+ foreach my $opt (@{$obj->{options}}) {
+ if ($opt =~ m/^(.*:?rename)=(.*)$/) {
+ $obj->{$1} = parseRename($2);
+ }
+ }
+
+ my $defmode = ($obj->{type} eq 'library' ? 'func' : 'field');
+ foreach my $inc (@{$obj->{items}}) {
+ my $match = $inc->{match};
+ my $mode = $defmode;
+
+ if ($inc->{match} =~ m/^(.*):(.*)$/) {
+ $match = $2;
+ $mode = $1;
+ }
+
+ $inc->{mode} = $mode;
+ if ($match =~ m@^/(.*)/$@) {
+ $inc->{regex} = $mode ne 'field' ? qr/$mode:$1/ : qr/$1/;
+ } else {
+ $inc->{regex} = $mode ne 'field' ? qr/^$mode:$match$/ : qr/^$match$/;
+ }
+
+ foreach my $opt (@{$inc->{options}}) {
+ if ($opt =~ m/^rename=(.*)$/) {
+ #print "option $opt ".Dumper($inc);
+ $inc->{"$mode:rename"} = parseRename($1);
+ }
+ }
+
+ $inc->{"$mode:rename"} = $obj->{"$mode:rename"} if (!(defined($inc->{"$mode:rename"})) && defined($obj->{"$mode:rename"}));
+ }
+ }
+}
+
+# transfer info from {api} to {data}
+sub postprocess {
+ my $api = shift;
+ my $seen = {};
+ my %data = %{$api->{data}};
+
+ # apply func stuff
+ foreach my $obj (grep {$_->{type} eq 'func'} @{$api->{api}}) {
+ my @list;
+
+ if ($api->{data}->{$obj->{name}}) {
+ push @list, ${data}{$obj->{name}};
+ } else {
+ push @list, map { $data{$_} } grep { $_ =~ m/$obj->{regex}/ } keys %data;
+ }
+
+ foreach my $s (@list) {
+ my $isconstructor = optionFlag('constructor', $obj);
+ my $isstatic = optionFlag('static', $obj);
+
+ $s->{constructor} = $isconstructor if $isconstructor;
+ $s->{static} = $isstatic if $isstatic;
+ $s->{rename} = $obj->{"$obj->{type}:rename"} ? $obj->{"$obj->{type}:rename"}->($s->{name}) : $s->{name};
+
+ foreach my $inc (@{$obj->{items}}) {
+ my @items;
+
+ my $array = optionFlag('array', $inc, $obj);
+ my $arraysize = optionValue('array-size', undef, $inc);
+ my $instance = optionFlag('instance', $inc);
+ my $success = optionValue('success', undef, $inc);
+
+ if ($inc->{match} eq '<result>') {
+ push @items, $s->{result};
+ } else {
+ @items = grep { $_->{name} =~ m/$inc->{regex}/ } @{$s->{items}};
+ }
+
+ foreach my $m (@items) {
+ $m->{array} = $array if $array;
+ $m->{arraysize} = $arraysize if defined($arraysize);
+ $m->{instance} = $instance if $instance;
+ $m->{success} = $success if defined($success);
+ }
+ }
+ print Dumper({func=>$s, obj=>$obj});
+ }
+
+ }
+
+}
+
+1;
--- /dev/null
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# method template
+# 'invoke' is a simple string template
+# variables are defined by method.pm
+code method {
+ # normal function invocation
+ invoke {{
+ static final MethodHandle {name}$FH = Memory.downcall("{name}", {function-descriptor});
+ public {static}{java-result} {rename}({java-arguments}) {
+ {native-output-define}
+ {native-result-define}
+ try {create-frame}{
+ {native-output-init}
+ {native-result-assign}{name}$FH.invokeExact({native-call});
+ {native-output-copy}
+ {result-test}{
+ {java-result-assign}
+ {on-success}
+ {java-result-return}
+ }
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ {result-throw}
+ }
+ }}
+
+ # callback function/types
+ downcall {{
+ public static Memory.FunctionPointer<{rename}> downcall(MemoryAddress addr$, ResourceScope scope$) {
+ NativeSymbol symbol$ = NativeSymbol.ofAddress("{rename}", addr$, scope$);
+ MethodHandle {rename}$FH = Memory.downcall(symbol$, descriptor());
+ return new Memory.FunctionPointer<{rename}>(
+ symbol$,
+ ({java-arguments}) -> {
+ {native-output-define}
+ {native-result-define}
+ try {create-frame}{
+ {native-output-init}
+ {native-result-assign}{rename}$FH.invokeExact({native-call});
+ {native-output-copy}
+ {result-test}{
+ {java-result-assign}
+ {on-success}
+ {java-result-return}
+ }
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ });
+ }
+ }}
+
+ upcall {{
+ public static Memory.FunctionPointer<{rename}> upcall({rename} target$, ResourceScope scope$) {
+ interface Trampoline {
+ {java-result} call({native-arguments});
+ }
+ Trampoline trampoline = ({native-arguments}) -> {
+ // frame? scope?
+ {trampoline-result-define}target$.call({java-call});
+ {trampoline-result-return}
+ };
+ return new Memory.FunctionPointer<>(
+ Memory.upcall(
+ trampoline,
+ "call",
+ "{java-signature}",
+ descriptor(),
+ scope$),
+ target$);
+ }
+ }}
+}
+
+
+# structs - normal structs
+# handle - anonymous structs
+# library - library template
+
+code class {
+ library {{
+package {package};
+import jdk.incubator.foreign.*;
+import java.lang.invoke.*;
+import {package}.Memory.*;
+
+public class {name} {
+{defines}
+{enums}
+{funcs}
+}
+ }}
+ constants {{
+package {package};
+
+public interface {name} {
+{enums}
+{defines}
+}
+ }}
+ struct {{
+package {package};
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.*;
+import java.lang.invoke.*;
+import {package}.Memory.*;
+
+public class {rename} implements Memory.Addressable {
+
+ public final MemorySegment segment;
+
+ private {rename}(MemorySegment segment) {
+ this.segment = segment;
+ }
+
+ public static {rename} create(MemorySegment segment) {
+ return new {rename}(segment);
+ }
+
+ public static {rename} create(MemoryAddress address, ResourceScope scope) {
+ return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope)) : null;
+ }
+
+ public static {rename} create(SegmentAllocator frame) {
+ return create(frame.allocate(LAYOUT));
+ }
+
+ @Override
+ public final MemoryAddress address() {
+ return segment.address();
+ }
+
+ @Override
+ public final ResourceScope scope() {
+ return segment.scope();
+ }
+
+ public final MemorySegment segment() {
+ return segment;
+ }
+
+{defines}
+{enums}
+{accessors}
+{methods}
+{layout}
+{varhandles}
+}
+}}
+ struct-array {{
+package {package};
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.*;
+import java.lang.invoke.*;
+import {package}.Memory.*;
+
+public class {rename} implements Memory.Addressable, Memory.Array<{rename}> {
+
+ public final MemorySegment segment;
+
+ private {rename}(MemorySegment segment) {
+ this.segment = segment;
+ }
+
+ public static {rename} create(MemorySegment segment) {
+ return new {rename}(segment);
+ }
+
+ public static {rename} create(MemoryAddress address, ResourceScope scope) {
+ return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope)) : null;
+ }
+
+ public static {rename} create(SegmentAllocator frame) {
+ return create(frame.allocate(LAYOUT));
+ }
+
+ public static {rename} createArray(MemoryAddress address, ResourceScope scope) {
+ return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope)) : null;
+ }
+
+ @Override
+ public final MemoryAddress address() {
+ return segment.address();
+ }
+
+ @Override
+ public final ResourceScope scope() {
+ return segment.scope();
+ }
+
+ public final MemorySegment segment() {
+ return segment;
+ }
+
+ @Override
+ public long length() {
+ return segment.byteSize() / LAYOUT.byteSize();
+ }
+
+ @Override
+ public {rename} getAtIndex(long index) {
+ try {
+ return {rename}.create((MemorySegment){name}$SH.invokeExact(segment, index));
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+{defines}
+{enums}
+{accessors}
+{methods}
+{layout}
+ final static MethodHandle {name}$SH = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(PathElement.sequenceElement());
+{varhandles}
+}
+}}
+ handle {{
+package {package};
+import jdk.incubator.foreign.*;
+import java.lang.invoke.*;
+import {package}.Memory.*;
+
+public class {rename} implements Memory.Addressable {
+
+ MemoryAddress address;
+ ResourceScope scope;
+
+ private {rename}(MemoryAddress address, ResourceScope scope) {
+ this.address = address;
+ this.scope = scope;
+ }
+
+ public static {rename} create(MemoryAddress address, ResourceScope scope) {
+ return MemoryAddress.NULL != address ? new {rename}(address, scope) : null;
+ }
+
+ @Override
+ public MemoryAddress address() {
+ return address;
+ }
+
+ @Override
+ public ResourceScope scope() {
+ return scope;
+ }
+
+{defines}
+{enums}
+{methods}
+}
+}}
+ call {{
+package {package};
+import jdk.incubator.foreign.*;
+import java.lang.invoke.*;
+import {package}.Memory.*;
+
+@FunctionalInterface
+public interface {rename} {
+ {java-result} call({java-arguments});
+
+ public static FunctionDescriptor descriptor() {
+ return {function-descriptor};
+ }
+
+{upcall}
+{downcall}
+}
+}}
+}
+
+# do I want this to be perl code as template or just a template?
+code getset {
+ get set=value={getnative} {{
+ public {type} get{rename}() {
+ return {tojava};
+ }
+ }}
+ geti set=value={getnative} set=segment=segment {{
+ public {type} get{rename}AtIndex(long index) {
+ // option a: resolve an offset segment and asme as above with set=segment=segment
+ // option b: an indexed varhandle
+ MemorySegment segment = segment().asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize());
+ return {tojava};
+ }
+ }}
+ set set=value=value {{
+ public void set{rename}({type} value) {
+ {setnative};
+ }
+ }}
+ seti set=value=value set=segment=segment {{
+ public void set{rename}AtIndex(long index, {type} value) {
+ MemorySegment segment = segment().asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize());
+ {setnative};
+ }
+ }}
+}
+
+code getsetelement {
+ get set=index=i {{
+ public {typei} get{rename}Element(long i) {
+ return {getnativei};
+ }
+ }}
+ set set=index=i set=value=value {{
+ public void set{rename}Element(long i, {typei} value) {
+ {setnativei};
+ }
+ }}
+}
+
+code getsetelement2d {
+ get set=index0=i set=index1=j {{
+ public {typei} get{rename}Element(long i, long j) {
+ return {getnativei};
+ }
+ }}
+ set set=index0=i set=index1=j set=value=value {{
+ public void set{rename}Element(long i, long j, {typei} value) {
+ {setnativei};
+ }
+ }}
+}
+
+code getbyvalue {
+ get {{
+ public {type} get{rename}() {
+ try {
+ return {getnative};
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }}
+}
--- /dev/null
+package code;
+
+use strict;
+
+use File::Path qw(make_path);
+use File::Basename;
+use Data::Dumper;
+use List::Util qw(first);
+
+require genapi;
+
+my %typeSizes = (
+ i8 => 'byte', u8 => 'byte',
+ i16 => 'short', u16 => 'short',
+ i32 => 'int', u32 => 'int',
+ i64 => 'long', u64 => 'long',
+ f32 => 'float',
+ f64 => 'double',
+);
+
+my %typeSuffix = (
+ 'long' => 'L',
+ 'float' => 'f'
+);
+
+my %defineType = (
+ %typeSizes,
+ string => 'String'
+);
+
+my %typePrefix = (
+ i8 => '(byte)',
+ u8 => '(byte)',
+ i16 => '(short)',
+ u16 => '(short)',
+ string => '"'
+);
+
+my %typeSuffix = (
+ u64 => 'L',
+ i64 => 'L',
+ f32 => 'f',
+ string => '"'
+);
+
+my %typeSignature = (
+ 'byte' => 'B',
+ 'short' => 'S',
+ 'int' => 'I',
+ 'long' => 'J',
+ 'float' => 'F',
+ 'double' => 'D',
+ 'void' => 'V',
+ 'MemorySegment' => 'Ljdk/incubator/foreign/MemorySegment;',
+ 'MemoryAddress' => 'Ljdk/incubator/foreign/MemoryAddress;',
+);
+
+# creates per-field type info, used by methods and structs
+# the names are bit naff here
+sub scanFields {
+ my $api = shift;
+ my $s = shift;
+ my $data = $api->{data};
+
+ my @members = ();
+ foreach my $m (defined($s->{result}) ? $s->{result} : (), @{$s->{items}}) {
+ my $info = $api->findType($m);
+ my $type = $info->{type};
+ my $match = $info->{match};
+ my $typeSizes = $code::typeSizes;
+
+ foreach my $k (keys %{$type->{items}}) {
+ my $f = $type->{items}->{$k};
+
+ if (defined $f) {
+ my $e = eval $f;
+ if (!defined $e) {
+ print "$typeSizes{$match->{ctype}}\n";
+
+ print "$s->{name} field=$m->{name} deref=$m->{deref} regex=$type->{regex} $k=\n$f\n";
+ print "match=".Dumper($match);
+ print "error: $! $@\n";
+ die;
+ } else {
+ #print "$m->{name} $type->{regex} $k = $f = $e\n";
+ }
+ $match->{$k} = $e;
+ }
+ }
+
+ # hmm this should probably be in loop above, but there's clashes with things like {type}
+ foreach my $o (grep { defined $m->{$_} } 'tonative') {
+ $match->{$o} = findCode($api, $m->{$o});
+ }
+
+ $match->{name} = $m->{name};
+ $match->{rename} = $m->{rename};
+ $match->{segment} = 'segment()';
+ $match->{scope} = 'scope()';
+
+ push @members, { field=>$m, type=>$type, match=>$match };
+ }
+ @members;
+}
+
+sub findCode {
+ my $api = shift;
+ my $v = shift;
+
+ if ($v =~ m/^(code:.+)=(.*)$/) {
+ my $t = $api->{index}->{$1};
+ my $l = genapi::findItem($t, $2);
+
+ die "Uknown template '$1.$2'" if !defined($t) || !defined($l);
+ $l->{literal};
+ } else {
+ $v;
+ }
+}
+
+sub formatFunctionDescriptor {
+ my $api = shift;
+ my $members = shift;
+ my $d;
+
+ if ($members->[0]->{field}->{deref} eq 'void') {
+ shift @$members;
+ $d = "FunctionDescriptor.ofVoid(";
+ } else {
+ $d = "FunctionDescriptor.of(";
+ }
+
+ $d .= join(', ', map { formatTemplate($_->{match}->{layout}, $_->{match}) } @$members);
+ $d .= ')';
+
+ return $d;
+}
+
+sub formatFunctionSignature {
+ my $api = shift;
+ my $members = shift;
+ my $result = shift @$members;
+
+ return '('.join('', map { $typeSignature{$_->{match}->{carrier}} } @$members).')'
+ .$typeSignature{$result->{match}->{carrier}};
+}
+
+sub formatStructLayout {
+ my $api = shift;
+ my $s = shift;
+ my $members = shift;
+ my $count = 0;
+ my $lastOffset = 0;
+ my $maxSize = 8;
+ my $layout;
+
+ $layout = "\tpublic static final GroupLayout LAYOUT = MemoryLayout.$s->{type}Layout(\n\t\t";
+
+ foreach my $i (@$members) {
+ my $m = $i->{field};
+ my $type = $i->{type};
+ my $match = $i->{match};
+
+ $maxSize = bitfieldSize($m) if (bitfieldSize($m) > $maxSize);
+
+ if ($match->{layout}) {
+ if ($m->{offset} > $lastOffset) {
+ $layout .= ",\n\t\t" if $count++;
+ $layout .= 'MemoryLayout.paddingLayout('.($m->{offset} - $lastOffset).')';
+ }
+ $layout .= ",\n\t\t" if $count++;
+ $layout .= formatTemplate($match->{layout}, $match).".withName(\"$m->{name}\")";
+ $lastOffset = $m->{offset} + $m->{size};
+ }
+ }
+ if ($s->{size} > $lastOffset) {
+ $layout .= ",\n\t\t" if ($count++ > 0);
+ $layout .= 'MemoryLayout.paddingLayout('.($s->{size} - $lastOffset).')';
+ }
+
+ $layout .= "\n\t).withBitAlignment($maxSize);\n";
+ $layout;
+}
+
+sub formatDefine {
+ my $api = shift;
+ my $s = shift;
+
+ my $d = join "\n\t", map {
+ my $type = $_->{type};
+ my $name = $_->{name};
+ my $value = $_->{value};
+
+ "public static final $defineType{$type} $name = $typePrefix{$type}$value$typeSuffix{$type};";
+ } @{$s->{items}};
+
+ return "\t$d\n";
+}
+
+sub formatEnum {
+ my $api = shift;
+ my $s = shift;
+ my $type = $defineType{$s->{value_type}};
+
+ my $d = join "\n\t", "// enum $s->{name}", map {
+ my $name = $_->{name};
+ my $value = $_->{value};
+
+ "public static final $type $name = $typePrefix{$type}$value$typeSuffix{$type};";
+ } @{$s->{items}};
+
+ return "\t$d\n";
+}
+
+sub formatTemplate {
+ my $template = shift;
+ my $vars = shift;
+ my $prefix = shift;
+ my $result;
+
+ #print "apply-template vars=".Dumper($vars);
+
+ while ($template =~ m/^(.*?)\{([\w-]+)\}(.*)$/s) {
+ $result .= $1;
+
+ #print " $2 -> $vars->{$2}\n";
+
+ if (defined $vars->{$2}) {
+ $template = $vars->{$2}.$3;
+ } else {
+ $result .= "{$2}";
+ $template = $3;
+ }
+ }
+ $result .= $template;
+ $result =~ s/^/$prefix/gm;
+ $result;
+}
+
+# takes a template entry applies options and then formats it
+# applyTemplate(template-item, vars)
+sub applyTemplate {
+ my $code = shift;
+ my $match = shift;
+ my $vars = \%{$match};
+
+ foreach my $set (genapi::optionValuesAll('set', $code)) {
+ $vars->{$1} = $2 if ($set =~ m/^(\w+)=(.*)/);
+ }
+
+ formatTemplate($code->{literal}, $vars);
+}
+
+sub bitfieldSize {
+ my $m = shift;
+ my $size = $m->{size};
+
+ return 64 if ($size > 32);
+ return 32 if ($size > 16);
+ return 16 if ($size > 8);
+ return 8;
+}
+
+sub bitfieldType {
+ my $m = shift;
+ my $size = $m->{size};
+ return 'long' if ($size > 32);
+ return 'int' if ($size > 16);
+ return 'short' if ($size > 8);
+ return 'byte';
+}
+
+sub bitfieldIndex {
+ use integer;
+ my $m = shift;
+
+ $m->{offset} / bitfieldSize($m);
+}
+
+sub bitfieldOffset {
+ use integer;
+ my $m = shift;
+
+ $m->{offset} & (bitfieldSize($m) - 1);
+}
+
+sub bitfieldMask {
+ use integer;
+ my $m = shift;
+
+ sprintf("0x%x", ((1 << $m->{size}) - 1) << bitfieldOffset($m)).$typeSuffix{bitfieldType($m)};
+}
+
+1;
while (@ARGV) {
my $cmd = shift;
+ if ($cmd =~ m/^(-[^-])(.+)/) {
+ $cmd = $1;
+ unshift @ARGV, $2;
+ }
+
if ($cmd eq "-t") {
$package = shift;
} elsif ($cmd eq "-d") {
my $conf = new genconfig2({ include => \@includes }, $control);
$defs = $conf->{objects};
@exports = grep { $_->{type} eq 'define' } @{$defs};
+ foreach $export (@exports) {
+ $export->{options}->[0] = $conf->findInclude($export->{options}->[0]);
+ }
} else {
$defs = loadControlFile($control);
@exports = @{$defs->{define}};
foreach $inc (@{$export->{items}}) {
my @options = @{$inc->{options}};
- if ($inc->{match} =~ m@^/(.*)/$@) {
- print "$export->{name} - $inc->{match} - regex $1\n";
- $inc->{regex} = qr/$1/;
- } else {
- $inc->{regex} = qr/^$inc->{match}$/;
- }
+ next if ($inc->{match} =~ m/^(define|enum):/on);
$inc->{mode} = "include";
foreach $o (@{$inc->{options}}) {
$inc->{mode} = $o;
}
}
+
+ if ($inc->{match} =~ m@^/(.*)/$@) {
+ print "$export->{name} - $inc->{match} - regex $1\n";
+ $inc->{regex} = qr/$1/;
+ } elsif ($inc->{mode} =~ m/^file-/) {
+ $inc->{regex} = qr@/$inc->{match}$|$inc->{match}$@;
+ } else {
+ $inc->{regex} = qr/^$inc->{match}$/;
+ }
+
if ($inc->{mode} =~ m/include/) {
$includes += 1;
} else {
my @options = @{$export->{options}};
- foreach $o (@options) {
- if ($o =~ m/header=(.*)/) {
- $export->{header} = $1;
- } elsif ($o =~ m/default=(.*)/) {
+ $export->{header} = $options[0];
+
+ foreach $o (@options[1..$#options]) {
+ if ($o =~ m/default=(.*)/) {
$export->{default} = $1;
- } elsif ($#options == 0) {
- $export->{header} = $o;
} else {
print STDERR "unknown defines option '$o'\n";
}
my $header = $export->{header};
if (!defined($rawDefines{$header})) {
- $rawDefines{$header} = scanDefines($header, { CPPFLAGS=>$cppflags, keep_comments=>0 });
+ $rawDefines{$header} = scanDefines($header, { CPPFLAGS=>$CPPFLAGS, keep_comments=>0 });
}
$export->{rawDefines} = $rawDefines{$header};
}
-open (my $fh, ">", "$output~") || die("can't open $output~ for writing");
-
+# output the .c file
+open (my $fh, ">", "$output-gen.c~") || die("can't open $output-gen,c~ for writing");
export($fh, \@exports);
-
close ($fh) || die("error writing");
-rename "$output~",$output || die("error overwriting $output");
+rename "$output-gen.c~","$output-gen.c" || die("error overwriting $output-gen.c");
+
+# compile it
+print "cc -o $output-gen $CPPFLAGS $output-gen.c\n";
+system("cc -o $output-gen $CPPFLAGS $output-gen.c") == 0 || die("error compiling $output-gen.c");
+
+# execute it
+print "$output-gen $output~\n";
+system("$output-gen $output~") == 0 || die("error executing $output-gen");
+print "mv $output~\ $output\n";
+rename "$output~","$output" || die("error overwriting $output");
exit 0;
# this is effectively a function overloading system so that the
# compiler can select the datatype of the evaluation of the
# definition.
+ # it should probably be c++
print $fp <<END;
/* note unsigned types are output as signed for java compatability */
/* unsigned long long might not be 64 bit i guess, could use sizeof i suppose */
# }
sub scanDefines {
my $header = shift;
- my %o = %{shift @_};
+ my $o = shift;
my $lastc = "";
my $source;
my $sourceLine;
print STDERR "Scanning $header\n";
- open (my $in,"-|","cpp -dD ".($o{keep_comments} ? '-CC' : '')." $o{CPPFLAGS} $header") || die("Can't find include file: $header");
+ print STDERR "cpp -dD ".($o->{keep_comments} ? '-CC' : '')." $o->{CPPFLAGS} $header\n";
+
+ open (my $in,"-|","cpp -dD ".($o->{keep_comments} ? '-CC' : '')." $o->{CPPFLAGS} $header") // die("Can't find include file: $header");
while (<$in>) {
# line markers
if (m/^\# (\d+) \"([^\"]*)/) {
}
# accumulate comments
# single line comments override multi-line
- if ($o{keep_comments}) {
+ if ($o->{keep_comments}) {
if (m@/\*(.*)\*/@) {
do {
$lastc = $1 if $lastc eq "";
};
push @{$def{$1}}, $target;
} elsif (/\S/) {
- die("invalid line: %_");
+ die("invalid line: '$_'");
}
}
int plugin_is_GPL_compatible; // must be defined for the plugin to run
static FILE *output_file;
+static char *output_path; // name
+static char *output_tmp; // name~
static int output_enabled = 1;
static int debug_level = 0;
struct node *node;
while ((node = list_remhead(list))) {
- if (debug_level > 2)
+ if (debug_level > 0)
fprintf(stderr, " free: %s\n", node->name);
free(node);
}
}
+static void todump_add(tree type, const char *name) {
+ struct node *node = node_alloc(type, name);
+
+ list_add(&todump, node);
+}
+
+static void todump_addhead(tree type, const char *name) {
+ struct node *node = node_alloc(type, name);
+
+ list_addhead(&todump, node);
+}
+
// returns 0 if type has no size (i.e VOID_TYPE)
static bool is_struct_or_union(const_tree type) {
return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
case RECORD_TYPE:
case UNION_TYPE:
if (TYPE_IDENTIFIER(field_type)) {
- list_add(&todump, node_alloc(field_type, NULL));
+ todump_add(field_type, NULL);
buffer_add(b, "${");
buffer_add(b, value_name(field_type));
} else {
char *name = stack_path(&context_stack, "_");
- list_add(&todump, node_alloc(field_type, name));
+ todump_add(field_type, name);
buffer_add(b, "${");
buffer_add(b, name);
case RECORD_TYPE:
case UNION_TYPE:
if (TYPE_IDENTIFIER(field_type)) {
- list_add(&todump, node_alloc(field_type, NULL));
+ todump_add(field_type, NULL);
buffer_add(b, "${");
buffer_add(b, value_name(field_type));
} else {
char *name = stack_path(&context_stack, "_");
- list_add(&todump, node_alloc(field_type, name));
+ todump_add(field_type, name);
buffer_add(b, "${");
buffer_add(b, name);
// note it is added to todump in reverse
buffer_init(&b, 256);
export_desc(field, field_type, &b);
- list_addhead(&todump, node_alloc(field_type, b.data));
+ todump_addhead(field_type, b.data);
generate(" type => 'call:%s', ", b.data);
if (debug_level > 0) {
fprintf(stderr, "save for later param type %p root type %p '%s'\n", field_type, root_type, b.data);
}
}
- buffer_init(&b, 256);
- export_cdesc(field, field_type, &b);
- free(b.data);
+ //buffer_init(&b, 256);
+ //export_cdesc(field, field_type, &b);
+ //free(b.data);
break;
}
} else {
char *name = stack_path(&context_stack, "_");
- list_add(&todump, node_alloc(field_type, name));
+ todump_add(field_type, name);
generate(" type => '%s:%s',", us, name);
free(name);
}
if (fwd) {
// use the forward reference to find the names
if (debug_level > 0)
- fprintf(stderr, "found forward reference @ %p\n", fwd);
+ fprintf(stderr, "found forward reference @ %p\n", fwd->type);
+
+ // so sometimes at this point the parameters stack has items we should
+ // remove ... but not always. apart from leaving garbage on the
+ // stack it doesn't seem to upset the output
+
name = fwd->list.head;
} else {
// paramter names are in the paramters list
}
}
+// try to find a filename for this node
+// it isn't really good enough for filtering, e.g. enums have no name
+static const char *source_filename(tree type) {
+ if (DECL_P(type)) {
+ expanded_location xloc = expand_location(DECL_SOURCE_LOCATION(type));
+ return xloc.file;
+ } else if (is_struct_or_union(type)) {
+ for (tree field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) {
+ return source_filename((field));
+ }
+ }
+ // shrug
+ return "<unknown>";
+}
+
/*
Main entry point for exporting any type.
*/
tree deftype;
tree target;
- if (debug_level > 1)
- fprintf(stderr, "export_type(%s, %s)\n", ZTREE_CODE(type), names);
-
switch (TREE_CODE(type)) {
case TYPE_DECL: {
if (!names) {
return;
names = IDENTIFIER_POINTER(name);
}
- if (hash_lookup(&dumped, names))
+ if (hash_lookup(&dumped, names)) {
+ if (debug_level > 1)
+ fprintf(stderr, " - type already output, skipping");
return;
+ }
hash_put(&dumped, node_alloc(type, names));
- if (debug_level > 1)
- fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
-
deftype = TREE_TYPE(type);
target = target_type(deftype);
+
+ if (debug_level > 1)
+ fprintf(stderr, "export: %-16s %s\n", ZTREE_CODE(type), names);
+
switch (TREE_CODE(target)) {
case FUNCTION_TYPE: {
// function pointer typdef
hash_put(&dumped, node_alloc(type, names));
if (debug_level > 1)
- fprintf(stderr, "export type func decl %s\n", names);
+ fprintf(stderr, "export: %-16s %s\n", ZTREE_CODE(type), names);
generate("'func:%s' => { name => '%s', type => 'func',", names, names);
hash_put(&dumped, node_alloc(type, names));
if (debug_level > 1)
- fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+ fprintf(stderr, "export: %-16s %s\n", ZTREE_CODE(type), names);
generate("'call:%s' => { name => '%s', type => 'call',", names, names);
generate(" ctype => '%s',", print_generic_expr_to_str(type));
hash_put(&dumped, node_alloc(type, names));
if (debug_level > 1)
- fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+ fprintf(stderr, "export: %-16s %s\n", ZTREE_CODE(type), names);
generate("'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
target = target_type(deftype);
if (debug_level > 1) {
- fprintf(stderr, "field_decl type is '%s\n", ZTREE_CODE(deftype));
- fprintf(stderr, "type name is '%s'\n", TYPE_IDENTIFIER(deftype) ? IDENTIFIER_POINTER(TYPE_IDENTIFIER(deftype)) : "<anon>");
- fprintf(stderr, "target is '%s'\n", ZTREE_CODE(target));
+ fprintf(stderr, "field: %-16s %s %s -> %s\n", ZTREE_CODE(deftype), names,
+ TYPE_IDENTIFIER(deftype) ? IDENTIFIER_POINTER(TYPE_IDENTIFIER(deftype)) : "<anon>",
+ ZTREE_CODE(target));
}
// nb almost same as next case below, but we don't save the param info here
buffer_init(&b, 256);
export_desc(deftype, target, &b);
- list_addhead(&todump, node_alloc(target, b.data));
+ todump_addhead(target, b.data);
if (debug_level > 0)
fprintf(stderr, "save for later param type %p '%s'\n", target, b.data);
buffer_init(&b, 256);
export_desc(deftype, target, &b);
- list_addhead(&todump, node_alloc(target, b.data));
+ todump_addhead(target, b.data);
if (debug_level > 0)
fprintf(stderr, "save for later param type %p '%s'\n", target, b.data);
}
if (debug_level > 0)
- fprintf(stderr, "(push parameter '%s')\n", names);
+ fprintf(stderr, "(push parameter '%s' %s)\n", names, source_filename(type));
stack_push(¶meters, node_alloc(type, names));
break; }
fprintf(stderr, "finish_type\n");
if (debug_level > 1)
debug_tree(type);
+ if (debug_level > 2)
+ dump_node(type, TDF_ALL_VALUES, stderr);
export_type(type, NULL);
}
fprintf(stderr, "finish_decl %s\n", ZTREE_CODE(type));
if (debug_level > 1)
debug_tree(type);
+ if (debug_level > 2)
+ dump_node(type, TDF_ALL_VALUES, stderr);
export_type(type, NULL);
}
generate("# %s\n", n->name);
generate("}\n");
- fclose(output_file);
if (debug_level > 0)
fprintf(stderr, "unhandled paramters:\n");
list_clear(¶meters);
+
+ if (fclose(output_file) != 0
+ || rename(output_tmp, output_path) != 0) {
+ perror(output_tmp);
+ exit(EXIT_FAILURE);
+ }
}
int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) {
exit(EXIT_FAILURE);
}
- output_file = fopen(output, "w");
+ output_path = xstrdup(output);
+ output_tmp = (char *)xmalloc(strlen(output)+2);
+ strcpy(stpcpy(output_tmp, output), "~");
+
+ output_file = fopen(output_tmp, "w");
if (NULL == output_file) {
perror(output);
exit(EXIT_FAILURE);
generate("{\n");
+ //register_callback(plugin_info->base_name, PLUGIN_INCLUDE_FILE, plugin_include_file, NULL);
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);
register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);
--- /dev/null
+#!/usr/bin/perl
+
+# usage
+# -t package target package
+# -d directory output root
+# -v verbose
+# -a datafile add datafile to the dataset, can be from export.so or export-defines, etc
+
+use Data::Dumper;
+
+use File::Path qw(make_path);
+use File::Basename;
+
+use strict;
+
+use Carp 'verbose';
+$SIG{ __DIE__ } = sub { Carp::confess( @_ ) };
+
+my $scriptPath = dirname(__FILE__);
+push @INC,$scriptPath;
+
+require genapi;
+require code;
+require method;
+
+$Data::Dumper::Indent = 1;
+
+my $apidef = "api.api";
+my $vars = {
+ package => '',
+ output => 'bin',
+ verbose => 0,
+};
+my @apilist;
+
+while (@ARGV) {
+ my $cmd = shift;
+
+ if ($cmd =~ m/^(-[^-])(.+)/) {
+ $cmd = $1;
+ unshift @ARGV, $2;
+ }
+
+ if ($cmd eq "-t") {
+ $vars->{package} = shift;
+ } elsif ($cmd eq "-d") {
+ $vars->{output} = shift;
+ } elsif ($cmd eq "-I") {
+ push @{$vars->{include}}, shift;
+ } elsif ($cmd eq "-v") {
+ $vars->{verbose}++;
+ } elsif ($cmd eq "-a") {
+ push @apilist, shift;
+ } else {
+ $apidef = $cmd;
+ }
+}
+
+push @{$vars->{include}}, $scriptPath;
+
+print Dumper($vars) if $vars->{verbose};
+
+my $api = new genapi($apidef, $vars, @apilist);
+
+#print Dumper($api);
+
+# TODO: make a module for this.
+copySkeletonFile($api, 'Memory.java');
+copySkeletonFile($api, 'Frame.java');
+
+exportLibraries($api);
+exportStructs($api);
+exportConstants($api);
+exit 0;
+
+# copies a skeleton file and patches it to the target package
+sub copySkeletonFile {
+ my $api = shift;
+ my $name = shift;
+ my $src = "$scriptPath/template/$name";
+ my $dst = $api->{vars}->{package};
+
+ $dst =~ s@\.@/@g;
+ $dst = "$api->{vars}->{output}/$dst/$name";
+
+ make_path(dirname($dst));
+
+ open (my $d, ">", $dst.'~') || die ("Cannot open '$src' for writing");
+ open (my $s, "<", $src) || die ("Cannot open '$dst' for reading");
+
+ while (<$s>) {
+ s/^package .*;/package $api->{vars}->{package};/o;
+ print $d $_;
+ }
+
+ close $s;
+ close $d;
+
+ rename ($dst.'~', $dst) || die ("unable to rename $d: $!");
+}
+
+sub formatFunction {
+ my $api = shift;
+ my $c = shift;
+ my $template = $api->{index}->{'code:method'};
+ my $invoke = genapi::findItem($template, 'invoke');
+ my $info = new method($api, $c);
+
+ #print 'function='.Dumper($c);
+ #print 'members='.Dumper(\@members);
+ #print 'info='.Dumper($info);
+
+ my $code;
+
+ foreach my $l (split /\n/,Dumper($info->{vars}).Dumper($info->{arguments})) {
+ $code .= "// $l\n";
+ }
+
+ $code .= code::applyTemplate($invoke, $info->{vars});
+ $code =~ s/^\s*\n//osgm;
+
+ return $code;
+}
+
+sub formatCall {
+ my $api = shift;
+ my $c = shift;
+ my $template = $api->{index}->{'code:method'};
+ my $upcall = genapi::findItem($template, 'upcall');
+ my $downcall = genapi::findItem($template, 'downcall');
+ my $info = new method($api, $c);
+
+ #print 'function='.Dumper($c);
+ #print 'members='.Dumper(\@members);
+
+ my $code;
+ foreach my $l (split /\n/,Dumper($info)) {
+ $code .= "//$l\n";
+ }
+
+ $info->{vars}->{downcall} = ($c->{access} =~ m/r/) ? code::applyTemplate($downcall, $info->{vars}) : '';
+ $info->{vars}->{upcall} = ($c->{access} =~ m/w/) ? code::applyTemplate($upcall, $info->{vars}) : '';
+
+ return $code.code::applyTemplate(genapi::findItem($api->{index}->{'code:class'}, 'call'), $info->{vars});
+}
+
+sub formatItems {
+ my $api = shift;
+ my $inc = shift;
+ my $res = shift;
+ my @list;
+
+ @list = grep {
+ $api->{output}->{"$_->{type}:$_->{name}"}++;
+ $res->{seen}->{"$_->{type}:$_->{name}"}++ == 0
+ } $api->findMatches($inc);
+
+ if ($inc->{type} eq 'func') {
+ push @{$res->{func}}, map { formatFunction($api, $_) } @list;
+ } elsif ($inc->{type} eq 'define') {
+ push @{$res->{define}}, map { code::formatDefine($api, $_) } @list;
+ } elsif ($inc->{type} eq 'enum') {
+ push @{$res->{enum}}, map { code::formatEnum($api, $_) } @list;
+ } elsif ($inc->{type} eq 'call') {
+ push @{$res->{enum}}, map { formatCall($api, $_) } @list;
+ } else {
+ die;
+ }
+}
+
+sub formatLibrary {
+ my $api = shift;
+ my $obj = shift;
+ my $res = shift;
+ my $data = $api->{data};
+ my $d;
+
+ print "library $obj->{name}\n";
+
+ foreach my $inc (@{$obj->{items}}) {
+ if ($inc->{type} eq 'library') {
+ $api->{output}->{"$inc->{match}"}++;
+ formatLibrary($api, $api->{index}->{$inc->{match}}, $res);
+ } elsif ($inc->{type} =~ m/func|call|define|enum/) {
+ print " $inc->{match}\n";
+ formatItems($api, $inc, $res);
+ } elsif ($inc->{type} ne 'field') {
+ die;
+ }
+ }
+}
+
+sub findTemplate {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my $data = $api->{data};
+ my $def = $api->{index}->{"$s->{type}:<default>"};
+ my $tmp = genapi::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
+
+ if ($tmp =~ m/^(.+)=(.+)$/) {
+ return genapi::findItem($api->{index}->{$1}, $2);
+ }
+ die;
+
+}
+
+# TODO: embedded structs
+sub formatStruct {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my $data = $api->{data};
+ my $seen = {};
+ my $structTemplate = findTemplate($api, $obj, $s);
+
+ my @members = code::scanFields($api, $s);
+ my @membersOutput = grep { $_->{field}->{output} } @members;
+
+ my $varhandles = join '', map { code::formatTemplate($_->{match}->{varhandle}, $_->{match}, "\t") } @membersOutput;
+ my $accessors = join "\n", map {
+ my ($m, $type, $match) = ($_->{field}, $_->{type}, $_->{match});
+ map {
+ my $accessor = $api->{index}->{$_};
+
+ map { code::applyTemplate($_, $match) } grep { $_ } map { genapi::findItem($accessor, $_) } map {
+ my @list = ();
+ if ($_ =~ m/r/o) {
+ push @list, 'get';
+ push @list, 'geti' if $_ =~ m/i/o;
+ }
+ if ($_ =~ m/w/o) {
+ push @list, 'set';
+ push @list, 'seti' if $_ =~ m/i/o;
+ }
+ @list;
+ } $m->{access};
+ } split(/,/,genapi::optionValue('template', undef, $type));
+ } @membersOutput;
+
+ my $res = {
+ library => [],
+ func => [],
+ define => [],
+ enum => [],
+ call => [],
+ seen => {}
+ };
+
+ formatLibrary($api, $obj, $res);
+
+ my $vars = {
+ %{$api->{vars}},
+ layout => code::formatStructLayout($api, $s, \@members),
+ rename => $s->{rename},
+ name => $s->{name},
+ varhandles => $varhandles,
+ accessors => $accessors,
+ methods => join("\n", @{$res->{func}}),
+ defines => join("\n", @{$res->{define}}),
+ enums => join("\n", @{$res->{enum}}),
+ };
+
+ my $code;
+ foreach my $l (split /\n/,Dumper($s, \@members)) {
+ $code .= "//$l\n";
+ }
+
+ $api->{output}->{"$s->{type}:$s->{name}"} = 1;
+
+ return $code.code::applyTemplate($structTemplate, $vars);
+}
+
+# output all libraries
+sub exportLibraries {
+ my $api = shift;
+ my $data = $api->{data};
+ my $template = $api->{index}->{'code:class'};
+ my $library = genapi::findItem($template, 'library');
+
+ foreach my $obj (grep { $_->{type} eq 'library' } @{$api->{api}}) {
+ next if $api->{output}->{"$obj->{type}:$obj->{name}"};
+ next if $obj->{output} != 1;
+
+ my $res = {
+ library => [],
+ func => [],
+ define => [],
+ enum => [],
+ call => [],
+ seen => {}
+ };
+
+ formatLibrary($api, $obj, $res);
+
+ my $vars = {
+ %{$api->{vars}},
+ name => $obj->{name},
+ defines => join("\n", @{$res->{define}}),
+ enums => join("\n", @{$res->{enum}}),
+ funcs => join("\n", @{$res->{func}}),
+ calls => join("\n", @{$res->{call}}),
+ };
+
+ export($api, $obj->{name}, code::applyTemplate($library, $vars));
+ }
+}
+
+sub export {
+ my $api = shift;
+ my $name = shift;
+ my $text = shift;
+
+ my $f = $api->openOutput($name);
+ print $f $text;
+ $api->closeOutput($name, $f);
+}
+
+sub formatClass {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+
+ if ($s->{type} =~ m/struct|union/) {
+ return formatStruct($api, $obj, $s);
+ } elsif ($s->{type} eq 'call') {
+ return formatCall($api, $s);
+ } else {
+ die;
+ }
+}
+
+sub exportStructs {
+ my $api = shift;
+ my $data = $api->{data};
+
+ # first those directly referenced
+ foreach my $obj (grep { $_->{type} =~ m/call|struct|union/ } @{$api->{api}}) {
+ my @list = $api->findMatches($obj);
+
+ print "gen ".($#list+1)." $obj->{type} $obj->{name}\n";
+ foreach my $s (@list) {
+ next if $api->{output}->{"$s->{type}:$s->{name}"};
+ print " $s->{name}\n";
+
+ export($api, $s->{rename}, formatClass($api, $obj, $s));
+ }
+ }
+
+ # then anything else left using the default outputs
+ foreach my $s (grep { $_->{output} && ($_->{type} =~ m/struct|union|call/) && !$api->{output}->{"$_->{type}:$_->{name}"} } @{$api->{api}}) {
+ my $obj = $data->{"$s->{type}:<default>"};
+ export($api, $s->{rename}, formatClass($api, $obj, $s));
+ }
+}
+
+# exports any define/enum not yet included elsehwere
+# TODO: this is sort of not very useful
+sub exportConstants {
+ my $api = shift;
+ my $data = $api->{data};
+ my $template = $api->{index}->{'code:class'};
+ my $constant = genapi::findItem($template, 'constants');
+
+ # hmm, not sure if i should use obj or just the values directly here
+ foreach my $s (grep { $_->{type} =~ m/define|enum/ } values %{$api->{data}}) {
+ next if $api->{output}->{"$s->{type}:$s->{name}"};
+ next if !$s->{output};
+
+ my $defines = '';
+ my $enums = '';
+
+ $defines = code::formatDefine($api, $s) if $s->{type} eq 'define';
+ $enums = code::formatEnum($api, $s) if $s->{type} eq 'enum';
+
+ my $vars = {
+ %{$api->{vars}},
+ name => $s->{name},
+ defines => $defines,
+ enums => $enums,
+ };
+
+ my $code;
+ foreach my $l (split /\n/,Dumper($s)) {
+ $code .= "//$l\n";
+ }
+
+ export($api, $s->{name}, $code.code::applyTemplate($constant, $vars));
+ }
+}
--- /dev/null
+
+package method;
+
+use strict;
+
+require code;
+
+sub fieldScope {
+ my $m = shift;
+
+ if ($m->{scope} =~ m/^explicit/) {
+ 'scope$';
+ } elsif ($m->{scope} eq 'instance') {
+ 'scope()';
+ } else {
+ 'ResourceScope.globalScope()';
+ }
+}
+sub fieldScopeAction {
+ my $m = shift;
+
+ if ($m->{field}->{scope} =~ m/^explicit,(.*)$/) {
+ return code::formatTemplate("scope\$.addCloseAction(() -> $1", { %{$m->{match}}, value => 'res$' });
+ } elsif ($m->{field}->{scope} =~ m/^explicit$/) {
+ 'scope$.addCloseAction(() -> res$.close());';
+ } else {
+ ();
+ }
+}
+sub apply {
+ my $t = shift;
+ my $m = shift;
+ return code::formatTemplate($t, $m->{match});
+}
+
+sub new {
+ my $class = shift;
+ my $api = shift;
+ my $c = shift;
+ my @members = code::scanFields($api, $c);
+ my $result = shift @members;
+
+ my $self = {
+ result => $result,
+ arguments => \@members,
+ vars => \%{$api->{vars}},
+ method => $c,
+ };
+ my $info = $self->{vars};
+
+ $info->{name} = $c->{name};
+ $info->{rename} = $c->{rename};
+
+ my @list = map { apply('{type} {name}', $_) } grep { $_->{field}->{output} } @members;
+ push @list, 'ResourceScope scope$' if ($c->{scope} =~ m/explicit/);
+ $info->{'java-arguments'} = join ', ', @list;
+ $info->{'native-arguments'} = join ', ', map { apply('{carrier} {name}', $_) } @members;
+
+ # for native downcalls
+ $info->{'native-result-define'} = ($result->{match}->{type} ne 'void') ? apply('{carrier} result$;', $result) : '';
+ $info->{'native-result-assign'} = ($result->{match}->{type} ne 'void') ? apply('result$ = ({carrier})', $result) : '';
+ $info->{'native-call'} = join ', ', map {
+ my $m = $_->{field};
+
+ if ($m->{instance}) {
+ "(jdk.incubator.foreign.Addressable)address()";
+ } else {
+ my $name = $_->{match}->{name};
+
+ if ($m->{output} == 0 && ($m->{deref} =~ m/^u64:/)) {
+ $name .= '$h';
+ } elsif ($m->{output} == 0 && $m->{implied}) {
+ $name = $m->{implied};
+ } elsif (defined($m->{'array-size-source'})) {
+ # or some function?
+ $name = "Memory.size($m->{'array-size-source'}->{name})";
+ }
+ code::formatTemplate("{tonative}", { %{$_->{match}}, value=>$name })
+ }
+ } @members;
+
+ # for java upcalls
+ $info->{'java-call'} = join ', ', map { code::formatTemplate('{tojava}', { %{$_->{match}}, value=>'{name}' }) } grep { $_->{field}->{output} } @members;
+
+ $info->{'java-signature'} = code::formatFunctionSignature($api, [$result, @members]);
+ $info->{'function-descriptor'} = code::formatFunctionDescriptor($api, [$result, @members]);
+
+ # hidden output arguments
+ # TODO: what about in/out arguments? they just fall out by not specifying them as scoped?
+ my @output = grep { $_->{field}->{output} == 0 && ($_->{field}->{deref} =~ m/^u64:/) && $_->{field}->{instance} == 0} @members;
+
+ $info->{'native-output-define'} = join "\n\t\t", map { apply('{carrieri} {name};', $_) } @output;
+ $info->{'native-output-init'} = join ";\n\t\t\t", map { apply('{type} {name}$h = {type}.createArray(1, frame$);', $_) } @output;
+ $info->{'native-output-copy'} = join ";\n\t\t\t", map { apply('{name} = {name}$h.get(0);', $_) } @output;
+
+ # also required for some tonative types?
+ my $x = grep { $_->{match}->{type} eq 'String' } @members;
+ $info->{'create-frame'} = ($#output >= 0 || grep { $_->{match}->{type} eq 'String' } @members) ? '(Frame frame$ = Memory.createFrame()) ' : '';
+
+ # result code handling
+ if ($c->{success}) {
+ $info->{'result-code'} = $c->{success}->{name};
+
+ # success test
+ if ($c->{success}->{success} eq '!null') {
+ $info->{'result-test'} = "if ($c->{success}->{name} != MemoryAddress.NULL) ";
+ $info->{'result-throw'} = 'throw new NullPointerException();';
+ } else {
+ $info->{'result-test'} = 'if ('.join('||', map { "$c->{success}->{name} == $_" } split(/,/,$c->{success}->{success})).') ';
+ $info->{'result-throw'} = 'throw new RuntimeException("error="+'.$c->{success}->{name}.');';
+ }
+ } else {
+ $info->{'result-test'} = '';
+ $info->{'result-throw'} = '';
+ }
+
+ # success actions
+ my @onsuccess = ();
+
+ push @onsuccess, code::findCode($api, $c->{onsuccess}) if defined($c->{onsuccess});
+
+ if (defined($c->{return})) {
+ # nb: this is only used for output parameters
+ my $res = (grep { $_->{field} == $c->{return} } $result, @members)[0];
+
+ $info->{'java-result'} = $res->{match}->{typei};
+ push @onsuccess, fieldScopeAction($res);
+ #push @onsuccess, code::formatTemplate('return {tojavai};', { %{$res->{match}}, value => $res->{field}->{name}, scope=>fieldScope($res->{field}) });
+
+ $info->{'java-result-assign'} = code::formatTemplate('{typei} res$ = {tojavai};', { %{$res->{match}}, value => $res->{field}->{name}, scope=>fieldScope($res->{field}) });
+ $info->{'java-result-return'} = 'return res$;';
+ $info->{'trampoline-result-define'} = 'error';
+ $info->{'trampoline-result-return'} = 'error';
+ } elsif ($result->{field}->{output} && $result->{match}->{type} ne 'void') {
+ $info->{'java-result'} = $result->{match}->{type};
+ push @onsuccess, fieldScopeAction($result);
+
+ $info->{'java-result-assign'} = code::formatTemplate('{type} res$ = {tojava};', { %{$result->{match}}, value => 'result$', scope=>fieldScope($result->{field}) });
+ $info->{'java-result-return'} = 'return res$;';
+
+ $info->{'trampoline-result-define'} = apply('{type} res$ = ', $result);
+ $info->{'trampoline-result-return'} = code::formatTemplate('return {tonative};', { %{$result->{match}}, value => 'res$' });
+ } else {
+ $info->{'java-result'} = 'void';
+ $info->{'java-result-assign'} = '';
+ $info->{'java-result-return'} = 'return;';
+ $info->{'trampoline-result-define'} = '';
+ $info->{'trampoline-result-return'} = '';
+ }
+ $info->{'on-success'} = join("\n\t\t\t\t", @onsuccess);
+
+ $info->{'static'} = $c->{static} ? 'static ' : '';
+
+ bless $self, $class;
+ $self;
+}
--- /dev/null
+
+#
+# simple tokeniser
+#
+# tokens are
+# 'self' : '{' ';' '}'
+# 'token':'\S+'
+# 'literal': '{{.*}}\n' may span multiple lines but final }}
+# must be at end of line
+
+package tokenise;
+
+use Data::Dumper;
+use strict;
+
+sub new {
+ my $class = shift;
+ my $path = shift;
+ my $self = {
+ state => 0,
+ line => [],
+ colno => 0,
+ type => 'none',
+ files => [],
+ file => { path=>$path, lineno=>0, lines=>[] }
+ };
+
+ open (my $in,"<",$path) || die("unable to open '$path': $@");
+ @{$self->{file}->{lines}} = <$in>;
+ close $in;
+
+ bless $self, $class;
+ return $self;
+}
+
+# include a file at the current point
+sub include {
+ my $self = shift;
+ my $path = shift;
+
+ print "include '$path'\n";
+
+ push @{$self->{files}}, $self->{file};
+
+ $self->{file} = { path=>$path, lineno=>0, lines=>[] };
+
+ open (my $in,"<",$path) || die("unable to open '$path': $@");
+ @{$self->{file}->{lines}} = <$in>;
+ close $in;
+
+ $self->{state} = 0;
+ # should it swallow the rest of {line}?
+}
+
+sub next {
+ my $self = shift;
+ my $token;
+
+ #print "next $self->{state} $#{$self->{lines}} $#{$self->{line}}\n";
+ # on entry state is in [0, 3, 4]
+ while ($#{$self->{file}->{lines}} >= 0 || $#{$self->{line}} >= 0 || $#{$self->{files}} >= 0) {
+ if ($#{$self->{line}} < 0) {
+ if ($#{$self->{file}->{lines}} < 0) {
+ $self->{file} = pop @{$self->{files}};
+ }
+ my $t = shift @{$self->{file}->{lines}};
+ $t =~ s/(?:^\#.*|\s+\#.*)//;
+ push @{$self->{line}}, split //,$t;
+ $self->{file}->{lineno} += 1;
+ $self->{colno} = 0;
+ }
+ my $c = shift @{$self->{line}};
+
+ $self->{colno} += 1;
+
+ #printf("$self->{state} '$c'=\$%02x\n", ord($c));
+
+ if ($self->{state} == 0) {
+ if (($c =~ m/\s/)) {
+ next;
+ } elsif ($c =~ m/[;\}]/) {
+ $self->{type} = $c;
+ #print "char: $c\n";
+ return $c;
+ } elsif ($c eq '{') {
+ if ($#{$self->{line}} >= 0 && $self->{line}->[0] eq '{') {
+ shift @{$self->{line}};
+ $self->{state} = 3;
+ } else {
+ $self->{type} = $c;
+ #print "char: $c\n";
+ return $c;
+ }
+ } elsif ($c) {
+ $token .= $c;
+ $self->{state} = 1;
+ }
+ } elsif ($self->{state} == 1) {
+ if ($c eq ';') {
+ unshift @{$self->{line}}, $c;
+ $self->{state} = 2;
+ $self->{type} = 'token';
+ #print "tok: $token\n";
+ return $token;
+ } elsif ($c =~ m/\S/) {
+ $token .= $c;
+ } else {
+ $self->{state} = 0;
+ $self->{type} = 'token';
+ #print "tok: $token\n";
+ return $token;
+ }
+ } elsif ($self->{state} == 2) {
+ $self->{state} = 0;
+ #print "char: $c\n";
+ return $c;
+ } elsif ($self->{state} == 3) {
+ if ($c eq '}' && $#{$self->{line}} == 1 && $self->{line}->[0] eq '}') {
+ shift @{$self->{line}};
+ $self->{state} = 0;
+ $self->{type} = 'literal';
+ #print "lit: $token\n";
+ return $token;
+ } else {
+ $token .= $c;
+ }
+ }
+ }
+
+ die "invalid state=$self->{state}" if $self->{state} != 0;
+
+ return undef;
+}
+
+1;
--- /dev/null
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+#
+# TODO: add a 'mode' to type matching
+# this woudld allow per-field overrides, e.g. char * field use String or MemorySegment or ByteArray depending on option
+
+# special case for bitfields
+# TODO: well this is bit of a mess, maybe should use <init> like code:method?
+type /bitfield/ copy=<common> {
+ layout;
+ type {{ bitfieldType($m) }}
+ carrier {{ "{type}" }}
+ tonative {{ "({type})({value})" }}
+ tojava {{ "({type})({value})" }}
+
+ getshiftl {{ bitfieldSize($m) - $m->{size} - bitfieldOffset($m) }}
+ getshiftr {{ bitfieldSize($m) - $m->{size} }}
+ getshiftop {{ ($m->{type} =~ m/^i/) ? ">>" : ">>>" }}
+
+ setmask {{ bitfieldMask($m) }}
+ setoffset {{ bitfieldOffset($m) }}
+
+ varindex {{ bitfieldIndex($m) }}
+
+ getnative {{
+ "({type})((({type}){name}\$VH.get({segment}) << {getshiftl}) {getshiftop} {getshiftr})"
+ }}
+ setnative {{
+ "$m->{name}\$VH.set({segment}, ({type})((({type}){name}\$VH.get({segment}) & ~{setmask}) | ((({value}) << {setoffset}) & {setmask})))"
+ }}
+ varhandle {{ "final static VarHandle {name}\$VH = MemoryLayout.sequenceLayout(Memory.".uc(bitfieldType($m)).").varHandle(MemoryLayout.PathElement.sequenceElement({varindex}));\n" }}
+}
+
+# void, only for return type
+type /void/ {
+ type {{ 'void' }}
+ carrier {{ 'void' }}
+ getnative;
+ setnative;
+ tonative;
+ tojava;
+}
+
+# 1d array of pointer to pointer
+type /^\[(\d+)u64:u64.*\]$/ {
+}
+
+# 1d array of pointer to primitive
+type /^\[(?<length>\d+)u64:(?<ctype>[uif]\d+)\]$/
+ copy=<pointer> template=code:getbyvalue,code:getsetelement {
+
+ type {{ !$m->{array} ? 'PointerArray' : 'HandleArray<{typei}>' }}
+ layout {{ 'MemoryLayout.sequenceLayout({length}, Memory.POINTER)' }}
+ tojava {{ !$m->{array} ? 'PointerArray.createArray({value}, {length}, {scope})' : 'HandleArray.createArray({value}, {length}, {typei}::create, {scope})' }}
+
+ getsegment {{ '(MemorySegment){name}$SH.invokeExact({segment})' }}
+ getnative {{ !$m->{array} ? 'PointerArray.create({getsegment})' : 'HandleArray.create({getsegment}, (a, s) -> {typei}.createArray(a, Long.MAX_VALUE, s))' }}
+ setnative;
+ varhandle {{
+ 'final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));'."\n".
+ 'final static VarHandle {name}$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"), MemoryLayout.PathElement.sequenceElement());'."\n"
+ }}
+
+ typei {{ ucfirst($typeSizes{$match->{ctype}}).'Array' }}
+ getnativei {{ '{typei}.createArray((MemoryAddress){name}$EH.get({segment}, {index}), Long.MAX_VALUE, {scope})' }}
+ setnativei {{ '{name}$EH.set({segment}, {index}, (jdk.incubator.foreign.Addressable)Memory.address({value}))' }}
+
+}
+
+# 1d array of pointer to type (struct or union or function?)
+type /^\[(?<length>\d+)u64:\$\{(\w+)\}\]$/ {
+ type {{ "HandleArray<$data->{$m->{type}}->{rename}>" }}
+ layout {{ "MemoryLayout.sequenceLayout({length}, Memory.POINTER)" }}
+}
+
+# 1d array of (struct or union or function?)
+type /^\[(?<length>\d+)\$\{(\w+)\}\]$/ {
+ type {{ "$data->{$m->{type}}->{rename}" }}
+ layout {{ "MemoryLayout.sequenceLayout({length}, {type}.LAYOUT)" }}
+}
+
+# 1d array of primitive
+type /^\[(?<length>\d+)(?<ctype>[uif]\d+)\]$/
+ copy=<pointer> template=code:getbyvalue,code:getsetelement {
+ layout {{ "MemoryLayout.sequenceLayout({length}, Memory.".uc($typeSizes{$match->{ctype}}).")" }}
+ type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+ tojava {{ "{type}.createArray({value}, $match->{length}, {scope})" }}
+ tonative {{ "(jdk.incubator.foreign.Addressable)Memory.address({value}" }}
+
+ getnative {{ "{type}.create((MemorySegment)$m->{name}\$SH.invokeExact({segment}))" }}
+ setnative;
+ varhandle {{
+ "final static MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n".
+ "final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement());\n"
+ }}
+
+ typei {{ "$typeSizes{$match->{ctype}}" }}
+ getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index})" }}
+ setnativei {{ "$m->{name}\$EH.set({segment}, {index}, ({typei}){value})" }}
+}
+
+# 2d array of primitive
+type /^\[(?<length0>\d+)\[(?<length1>\d+)(?<ctype>[uif]\d+)\]\]$/
+ copy=<pointer> template=code:getbyvalue,code:getsetelement2d {
+ layout {{ "MemoryLayout.sequenceLayout({length0}, MemoryLayout.sequenceLayout({length1}, Memory.".uc($typeSizes{$match->{ctype}})."))" }}
+ type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+ tojava {{ "{type}.create({value})" }}
+ tonative {{ "(jdk.incubator.foreign.Addressable)Memory.address({value})" }}
+
+ getnative {{ "{type}.create((MemorySegment)$m->{name}\$SH.invokeExact({segment}))" }}
+ setnative;
+
+ typei {{ "$typeSizes{$match->{ctype}}" }}
+ getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index0}, {index1}))" }}
+ setnativei {{ "$m->{name}\$EH.set({segment}, {index0}, {index1}, ({typei}){value}))" }}
+
+ varhandle {{
+ "final static MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n".
+ "final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement(), MemotyLayout.PathElement.sequenceElement());\n"
+ }}
+}
+
+# function pointer
+type /^u64:\(/ copy=<pointer> {
+ type {{ "FunctionPointer<$data->{$m->{type}}->{rename}>" }}
+ tojava {{ "$data->{$m->{type}}->{rename}.downcall({value}, {scope})" }}
+}
+
+# **primitive
+type /^u64:u64:(?<ctype>[uif]\d+)$/ copy=<pointer> {
+ length {{ $m->{'array-size'} ? 'get'.($m->{'array-size'}->{rename}).'()' : 'Long.MAX_VALUE' }}
+ type {{ !$m->{array} ? 'PointerArray' : 'HandleArray<{typei}>' }}
+ tojava {{
+ !$m->{array}
+ ? 'PointerArray.createArray({value}, {length}, {scope})'
+ : 'HandleArray.createArray({value}, {length}, (a, s) -> {typei}.createArray(a, {length}, s), {scope})'
+ }}
+ typei {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+}
+
+# **type
+type /^u64:u64:\$\{(\w+)\}$/ copy=<pointer> {
+ length {{ $m->{'array-size'} ? 'get'.($m->{'array-size'}->{rename}).'()' : 'Long.MAX_VALUE' }}
+ type {{ !$m->{array} ? 'PointerArray' : 'HandleArray<{typei}>' }}
+ tojava {{
+ !$m->{array}
+ ? 'PointerArray.createArray({value}, {length}, {scope})'
+ : 'HandleArray.createArray({value}, {length}, {typei}::create, {scope})'
+ }}
+ # tojavai ... ?
+ carrieri {{ "MemoryAddress" }}
+ typei {{ "$data->{$m->{type}}->{rename}" }}
+ tojavai {{ "{typei}.create({value}, {scope})" }}
+}
+
+# **void
+type /^u64:u64:v$/ copy=<pointer> {
+ typei {{ 'PointerArray' }}
+ type {{ "HandleArray<{typei}>" }}
+ tojava {{ "HandleArray.createArray({value}, Long.MAX_VALUE, PointerArray"."::create, {scope})" }}
+}
+
+# *primitive, or string for [iNNu8]
+
+# idea for when i implement 'flag' matching for types
+# *i8 with 'array' flag
+# TODO: length, multiple flags?
+type /^u64:(?<ctype>i8)$/ select=array copy=<pointer> {
+ type {{ ucfirst($typeSizes{$match->{ctype}}).'Array' }}
+ tojava {{ '{type}.createArray({value}, Long.MAX_VALUE, {scope})' }}
+ carrieri {{ "{typei}" }}
+ typei {{ $typeSizes{$match->{ctype}} }}
+ tojavai {{ "({typei}){value}" }}
+}
+
+# *i8 with 'segment' flag, length must be supplied (somewhere??)
+type /^u64:(?<ctype>i8)$/ select=segment copy=<pointer> {
+ type {{ 'MemorySegment' }}
+ tojava {{ '{value}' }}
+ carrieri {{ "{typei}" }}
+ typei {{ 'byte' }}
+ tojavai {{ "({typei}){value}" }}
+}
+
+# *i8 with no flag = String
+type /^u64:(?<ctype>i8)$/ copy=<pointer> {
+ type {{ 'String' }}
+ tonative {{ '(jdk.incubator.foreign.Addressable)frame$.copy({value})' }}
+
+ setnative {{ '{name}$VH.set({segment}, {copynative})' }}
+ copynative {{ 'SegmentAllocator.nativeAllocator(segment.scope()).allocateUtf8String({value})' }}
+
+ tojava {{ '({value}).getUtf8String(0L)' }}
+}
+
+# *Primitive fallback
+type /^u64:(?<ctype>[uif]\d+)$/ copy=<pointer> {
+ type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+ tonative {{ '(jdk.incubator.foreign.Addressable)Memory.address({value})' }}
+ tojava {{ ucfirst($typeSizes{$match->{ctype}})."Array.createArray({value}, Long.MAX_VALUE, {scope})" }}
+ carrieri {{ "{typei}" }}
+ typei {{ $typeSizes{$match->{ctype}} }}
+ tojavai {{ "({typei}){value}" }}
+}
+
+# *type struct? handle?
+type /^u64:\$\{(\w+)\}$/ copy=<pointer> {
+ type {{ "$data->{$m->{type}}->{rename}" }}
+ tojava {{ "$data->{$m->{type}}->{rename}.create({value}, {scope})" }}
+}
+
+# *void with size
+type /^u64:v$/ select=array-size copy=<pointer> {
+ type {{ 'MemorySegment' }}
+ tojava {{ "MemorySegment.ofAddress({value}, $m->{'array-size'}->{name}, {scope})" }}
+}
+
+# *void
+type /^u64:v$/ copy=<pointer> {
+}
+
+# primitive
+type /^(?<ctype>[uif]\d+)$/ copy=<common> {
+ layout {{ "Memory.".uc($typeSizes{$match->{ctype}}) }}
+ carrier {{ "$typeSizes{$match->{ctype}}" }}
+ type {{ "$typeSizes{$match->{ctype}}" }}
+ tonative {{ '({type})({value})' }}
+ tojava {{ '({type})({value})' }}
+}
+
+# type inline
+type /^\$\{(\w+)\}$/ byvalue template=code:getbyvalue {
+ layout {{ '{type}.LAYOUT' }}
+ carrier {{ 'MemoryAddress' }}
+ type {{ "$data->{$m->{type}}->{rename}" }}
+ tojava {{ '{type}.create({value}, {scope})' }}
+ tonative {{ '(jdk.incubator.foreign.Addressable)Memory.address({value})' }}
+ getnative {{ '{type}.create(({carrier}){name}$SH.invokeExact({segment}), {scope})' }}
+ setnative;
+ varhandle {{ 'final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));'."\n" }}
+}
+
+type <common> template=code:getset {
+ getnative {{ '({carrier}){name}$VH.get({segment})' }}
+ setnative {{ '{name}$VH.set({segment}, {tonative})' }}
+ varhandle {{ 'final static VarHandle {name}$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"));'."\n" }}
+ tojava {{ '{value}' }}
+ tonative {{ '{value}' }}
+}
+
+type <pointer> copy=<common> {
+ layout {{ 'Memory.POINTER' }}
+ carrier {{ 'MemoryAddress' }}
+ type {{ 'MemoryAddress' }}
+ tonative {{ '(jdk.incubator.foreign.Addressable)Memory.address({value})' }}
+}
--- /dev/null
+
+JAVA_HOME?=/opt/jdk-foreign/jvm/openjdk-19-internal
+JAVAC=$(JAVA_HOME)/bin/javac
+JAVA=$(JAVA_HOME)/bin/java
+
+JAVACFLAGS=--add-modules jdk.incubator.foreign
+
+api_SOURCES := $(wildcard ../src/api/*.java)
+api_demo_SOURCES := $(wildcard src/api/test/*.java)
+apigen_DEPS := $(api_SOURCES) $(wildcard ../src/*.api) $(wildcard ../src/*.pm ../src/generate-api-2)
+
+all::
+ mkdir -p bin
+
+all:: bin/demo.built
+
+bin/api.built: bin/api-object.gen
+ $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \
+ $(shell find bin/java -name '*.java')
+ touch $@
+
+bin/api-object.gen: bin/api.pm bin/api-defines.pm $(apigen_DEPS)
+ ../src/generate-api-2 -v -d bin/java -t proto.apiobject -a ./bin/api.pm -a ./bin/api-defines.pm api-object.api
+ touch $@
+
+bin/api-defines.pm: ../api/api.h ../src/export-defines api-object.api
+ ../src/export-defines --hack-new-format-2 -v -I.. -d $@ api-object.api
+
+bin/api.pm: ../api/api.h ../src/export.so
+ gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@ ./$< -o /dev/null
+
+bin/demo.built: $(api_demo_SOURCES) bin/api.built
+ $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(api_demo_SOURCES)
+ touch $@
+
+demo: bin/demo.built
+ $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+ -Djava.library.path=../api/bin -cp bin/classes \
+ api.test.TestAPI
+
+clean:
+ rm -rf bin
+
+.PHONY: demo clean all
--- /dev/null
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# maps functions to objects
+
+include types.api
+include code.api
+
+struct <default> default=all field:rename=studly-caps access=rw {
+}
+call <default> default=all call:rename=call access=rw {
+}
+
+struct api {
+ enum://;
+ define:API;
+ library:api-static;
+ library:api-calls;
+}
+
+struct data {
+ func:print_data instance:0 rename=print;
+}
+
+library api-static func:rename=s/^api_//,camel-case {
+ api_create success:result$=!null scope:result$=global;
+ api_func;
+
+ # different forms of constructor
+ api_new_a scope:result$=global;
+ api_new_b scope:result$=global success:rc=0;
+ api_new_c scope:apip=global success:result$=0 return:apip;
+ api_new_d scope:apip=global success:api=!null return:apip;
+ api_new_e scope:apip=global success:rc=0 return:apip;
+}
+
+# instance methods using library-level options
+library api-calls func:rename=s/^api_//,camel-case instance:0 {
+ api_free;
+ api_data array-size:data=size;
+ api_void rename=avoid;
+}
+
+# grab all defines that come from api.h
+define API api/api.h {
+ api.h file-include;
+}
--- /dev/null
+
+package api.test;
+
+import jdk.incubator.foreign.*;
+
+import proto.apiobject.*;
+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((byte)3);
+ a.setD((byte)4);
+
+
+ b.setA(5);
+ b.setB(6);
+ b.setC((byte)255);
+ b.setD((byte)255);
+
+ b.setTestA(cb);
+
+ System.out.println("from a");
+ a.print();
+ System.out.println("from b");
+ b.print();
+
+ //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("funca"), scope);
+ System.out.printf(" %s\n", funca.symbol());
+ funca.function().call(12);
+
+ api api = proto.apiobject.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);
+ }
+ }
+}
--- /dev/null
+
+CFLAGS=-g -fPIC
+
+JAVA_HOME?=/opt/jdk-foreign/jvm/openjdk-19-internal
+JAVAC=$(JAVA_HOME)/bin/javac
+JAVA=$(JAVA_HOME)/bin/java
+
+JAVACFLAGS=--add-modules jdk.incubator.foreign
+
+api_SOURCES := $(wildcard ../src/api/*.java)
+api_demo_SOURCES := $(wildcard src/api/test/*.java)
+apigen_DEPS := $(api_SOURCES) $(wildcard ../src/*.api) $(wildcard ../src/*.pm ../src/generate-api-2)
+
+all::
+ mkdir -p bin
+
+all:: bin/demo.built
+
+bin/api.built: bin/api-static.gen
+ $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes \
+ $(shell find bin/java -name '*.java')
+ touch $@
+
+bin/api-static.gen: bin/api.pm bin/api-defines.pm api-static.api $(apigen_DEPS)
+ ../src/generate-api-2 -v -d bin/java -t proto.apistatic -a ./bin/api.pm -a ./bin/api-defines.pm api-static.api
+ touch $@
+
+bin/api-defines.pm: ../api/api.h ../src/export-defines api-static.api
+ ../src/export-defines --hack-new-format-2 -I.. -d $@ api-static.api
+
+bin/api.pm: ../api/api.h ../src/export.so
+ gcc -fplugin=../src/export.so -I.. -fplugin-arg-export-output=$@ $< -o /dev/null
+
+bin/demo.built: $(api_demo_SOURCES) bin/api.built
+ $(JAVAC) $(JAVACFLAGS) -cp bin/classes -d bin/classes $(api_demo_SOURCES)
+ touch $@
+
+demo: bin/demo.built
+ $(JAVA) --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+ -Djava.library.path=../api/bin -cp bin/classes \
+ api.test.TestAPI
+
+clean:
+ rm -rf bin
+
+.PHONY: demo clean all
--- /dev/null
+
+Introduction
+------------
+
+Small generator and prototyping area for a c library.
+
+This is mostly to test out ideas like pattern matching for mostly
+automatic api conversion.
--- /dev/null
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# example of a simple 'static' library with all functions
+# and constants in one class and no special handling
+
+include types.api
+include code.api
+
+# collect all functions and constants into one object
+library APILib load=api {
+ enum://;
+ define:API;
+ func://;
+}
+
+struct <default> default=all access=rw field:rename=studly-caps {
+}
+
+call <default> access=rw call:rename=call {
+}
+
+# grab all defines in api.h
+define API api/api.h {
+ api.h file-include;
+}
import jdk.incubator.foreign.*;
-import proto.api.*;
-import static proto.api.APILib.*;
+import proto.apistatic.*;
+import static proto.apistatic.APILib.*;
import java.lang.invoke.*;
public class TestAPI {
a.setNext(b);
a.setA(1);
a.setB(2);
- a.setC(3); //a.setC((byte)3);
- a.setD(4); //a.setD((byte)4);
+ a.setC((byte)3);
+ a.setD((byte)4);
b.setA(5);
+++ /dev/null
-
-CFLAGS=-g -fPIC
-
-JAVA_HOME?=/opt/jdk-foreign/jvm/openjdk-19-internal
-JAVAC=$(JAVA_HOME)/bin/javac
-JAVA=$(JAVA_HOME)/bin/java
-
-JAVACFLAGS=--add-modules jdk.incubator.foreign
-
-api_SOURCES := $(wildcard ../src/api/*.java)
-api_demo_SOURCES := $(wildcard src/api/test/*.java)
-
-all::
- mkdir -p bin
-
-all:: bin/demo.built bin/libapi.so
-
-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) bin/api-defines.pm api.api
- ../src/generate-native -v -d bin/java -t proto.api -a ./bin/api.pm -a ./bin/api-defines.pm api.api
- touch $@
-
-bin/api-defines.pm: api.h ../src/export-defines api.api
- ../src/export-defines -d bin/api-defines.c api.api
- $(CC) -o bin/api-defines -I. bin/api-defines.c
- bin/api-defines $@~
- mv $@~ $@
-
-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 $@ $<
-
-bin/libapi.so: bin/api.o
- $(CC) -o $@ -shared $^
-
-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: demo clean all
+++ /dev/null
-
-# collect all functions and the defines we want
-library APILib load=api {
- define:api-defines
- func://
-}
-
-# grab all structures
-struct // default=all field:rename=studly-caps {
-}
-
-# grab all defines in api.h
-define api-defines api.h {
- api.h file-include
-}
+++ /dev/null
-
-#define API_FOO "12"
-#define API_MAKEVERSION(a, b) (a <<16) | b
-#define API_VERSION API_MAKEVERSION(1, 0)
-#define API_A 12UL
-#define API_B 12L
-#define API_C 12U
-#define API_D 12.0
-#define API_E 12.0f
-
-struct data {
- struct data *next;
-
- int a;
- int b;
- 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);
-
-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);
-
-struct list_node {
- struct list_node *succ;
- struct list_node *pred;
- char name[64-16];
-};