Output enum definitions.
authorNot Zed <notzed@gmail.com>
Wed, 8 Jan 2020 20:31:32 +0000 (07:01 +1030)
committerNot Zed <notzed@gmail.com>
Wed, 8 Jan 2020 20:31:32 +0000 (07:01 +1030)
src/generate

index bc7ee2b..e6eaf8c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/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]* ]*
+# 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
 #  -llib
 #    specify link library used by class
 #  -f func-pattern
-#    function name pattern to include for the last class
+#    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, ... ] } )
+# @classes = ( { name => 'class', match => [ func-pattern, ... ], match_file => [ file, ... ], enum => [ enum-pattern, ... ], enum_file => [ file, ...] } )
 @classes = ();
-%lastclass = ();
+%class = ();
 $output = ".";
 # map call signatures to a class name
 %callMap = ();
@@ -42,11 +46,19 @@ while (@ARGV) {
 
     if ($cmd eq "-f") {
        my $v = shift(@ARGV);
-       push @{$lastclass{match}}, qr/$v/;
+       push @{$class{match}}, qr/$v/;
     } elsif ($cmd eq "--func-file") {
        my $file = shift(@ARGV);
-       push @{$lastclass{match_file}}, $file;
-       push @{$lastclass{match}}, readMatchFile($file);
+
+       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/;
@@ -56,14 +68,18 @@ while (@ARGV) {
     } elsif ($cmd eq "-t") {
        $package = shift(@ARGV);
     } elsif ($cmd eq "-c") {
-       %lastclass = (
+       my %new = (
            name => shift(@ARGV),
            match => [],
            match_file => [],
+           enum => [],
+           enum_file => [],
            libs => []);
-       push @classes, \%lastclass;
+       push @classes, \%new;
+       %class = %new;
+       print "new:\n".Dumper(\%class);
     } elsif ($cmd =~ m/^-l(.*)/) {
-       push @{$lastclass{libs}}, $1;
+       push @{$class{libs}}, $1;
     } elsif ($cmd eq "-d") {
        $output = shift(@ARGV);
     } elsif ($cmd eq "--enclosing-type") {
@@ -74,7 +90,6 @@ while (@ARGV) {
 }
 
 use Data::Dumper;
-print Dumper(@classes);
 #exit 0;
 
 require $meta;
@@ -453,12 +468,13 @@ sub findStructs {
     return keys %visit;
 }
 
-sub findFuncs {
+sub findDefinition {
     my %all = %{shift @_};
+    my $type = shift @_;
     my @match = @_;
     my @stack = grep {
        my %e = %{$all{$_}};
-       $e{type} eq "func" && testMatch($e{name}, @match);
+       $e{type} eq $type && testMatch($e{name}, @match);
     } keys %all;
 
     return @stack;
@@ -468,14 +484,14 @@ sub findFuncs {
 
 # setup section
 
-# find all classes used by functions, add them to the struct roots
+# find all classes used by functions
 my %roots = ();
 for $c (@classes) {
     my %class = %{$c};
     my @libs = @{$class{libs}};
     my @match = @{$class{match}};
 
-    for $k (findFuncs(\%data, @match)) {
+    for $k (findDefinition(\%data, 'func', @match)) {
        my %func = %{$data{$k}};
        my @params = @{$func{arguments}};
 
@@ -495,8 +511,8 @@ for $c (@classes) {
     }
 }
 
-# FIXME: only include ones used elsewhere
 # 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}};
@@ -516,6 +532,22 @@ for $k (grep { $_ =~ m/^call:/n } keys %data) {
     }
 }
 
+# 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)$/;
@@ -625,7 +657,9 @@ END
     }
     my $resolve = join (",", keys %resolve);
 
-    print $dst "\@NativeStruct(value=\"$signature($struct{name})\", resolutionContext={$resolve})\n";
+    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) {
@@ -681,7 +715,38 @@ END
     print $dst "})\n";
     print $dst "public interface $class{name} {\n";
 
-    for $k (sort(findFuncs(\%data, @match))) {
+    # 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);
@@ -742,9 +807,25 @@ 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}\")\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(";