Extract more symbolic information from the tree.
[panamaz] / src / generate
1 #!/usr/bin/perl
2
3 # usage
4 # 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]]*
5 #  -d dir
6 #    root output directory
7 #  -t package
8 #    output package
9 #  --enclosing-type type
10 #    If supplied, all structures and classes are written to an enclosing class
11 #  -s struct-root-pattern
12 #    provide one or more patterns for matching structure roots.
13 #    all dependencies are automatically included.
14 #    if no pattern is provided, all match.
15 #  --struct-file file
16 #    provide a filename with exact structure names in it.  These are
17 #    used as roots(?)
18 #  -c class
19 #    specify class name to generate
20 #  -llib
21 #    specify link library used by class
22 #  -f func-pattern
23 #    function name pattern to include for the current class
24 #  --func-file file
25 #    point to a filename with exact function names in it, one per line ('#' is a comment).
26 #  -e enum-pattern
27 #    enum name pattern to include for the current class
28 #  --enum-file file
29 #    filename with enums in it
30
31 # TODO: scan all functions and include any types they use as struct roots
32 # TODO: some way to specify external types
33
34 @matchStruct = ();
35 $meta = "";
36 # @classes = ( { name => 'class', match => [ func-pattern, ... ], match_file => [ file, ... ], enum => [ enum-pattern, ... ], enum_file => [ file, ...] } )
37 @classes = ();
38 %class = ();
39 $output = ".";
40 # map call signatures to a class name
41 %callMap = ();
42 $package = "";
43
44 while (@ARGV) {
45     my $cmd = shift(@ARGV);
46
47     if ($cmd eq "-f") {
48         my $v = shift(@ARGV);
49         push @{$class{match}}, qr/$v/;
50     } elsif ($cmd eq "--func-file") {
51         my $file = shift(@ARGV);
52
53         push @{$class{match_file}}, $file;
54         push @{$class{match}}, readMatchFile($file);
55     } elsif ($cmd eq "-e") {
56         my $v = shift(@ARGV);
57         push @{$class{enum}}, qr/$v/;
58     } elsif ($cmd eq "--enum-file") {
59         my $file = shift(@ARGV);
60         push @{$class{enum_file}}, $file;
61         push @{$class{enum}}, readMatchFile($file);
62     } elsif ($cmd eq "-s") {
63         my $v = shift(@ARGV);
64         push @matchStruct, qr/$v/;
65     } elsif ($cmd eq "--struct-file") {
66         my $file = shift(@ARGV);
67         push @matchStruct, readMatchFile($file);
68     } elsif ($cmd eq "-t") {
69         $package = shift(@ARGV);
70     } elsif ($cmd eq "-c") {
71         my %new = (
72             name => shift(@ARGV),
73             match => [],
74             match_file => [],
75             enum => [],
76             enum_file => [],
77             libs => []);
78         push @classes, \%new;
79         %class = %new;
80         print "new:\n".Dumper(\%class);
81     } elsif ($cmd =~ m/^-l(.*)/) {
82         push @{$class{libs}}, $1;
83     } elsif ($cmd eq "-d") {
84         $output = shift(@ARGV);
85     } elsif ($cmd eq "--enclosing-type") {
86         $enclosingType = shift(@ARGV);
87     } else {
88         $meta = $cmd;
89     }
90 }
91
92 use Data::Dumper;
93 #exit 0;
94
95 require $meta;
96
97 # box types for primitives
98 %map_box = (
99     "long" => "Long",
100     "int" => "Integer",
101     "short" => "Short",
102     "char" => "Character",
103     "float" => "Float",
104     "double" => "Double",
105     "byte" => "Byte",
106     "void" => "Void"
107     );
108
109 sub readMatchFile {
110     my $path = shift @_;
111     my @lines = ();
112
113     open(my $f,"<$path");
114     while (<$f>) {
115         chop;
116         next if m/^#/;
117
118         #push @lines, qr/\^$_\$/;
119         push @lines, $_;
120     }
121     close($f);
122
123     my $all = join ('|', @lines);
124
125     return qr/^($all)$/;
126 }
127
128 sub camelCase {
129     my $name = shift @_;
130
131     $name =~ s/_(.)/uc($1)/eg;
132
133     return $name;
134 }
135
136 sub StudlyCaps {
137     my $name = shift @_;
138
139     $name =~ s/^(.)/uc($1)/e;
140     $name =~ s/_(.)/uc($1)/eg;
141
142     return $name;
143 }
144
145
146 sub structSignature {
147     my %struct = %{shift(@_)};
148     my $union = shift(@_);
149     my $sig = "";
150     my @fields = @{$struct{fields}};
151     my $offset = 0;
152
153     my $inbf = 0;
154     my $bfoffset = 0;
155     my $bfstart = 0;
156     my $bfsig = "";
157
158     for $fi (@fields) {
159         my %field = %{$fi};
160         my $off = $field{offset};
161
162         # bitfields, this only handles 1x u64 bitfield section
163         #  They need to: align to u32/u64
164         #  Group fields into one full u32/u64
165         # TODO: check alignment @ start?
166         # TODO: clean up and complete
167         # TODO: bitfields in unions are probably broken
168         if ($field{ctype} eq 'bitfield') {
169             if ($inbf) {
170                 if ($off - $offset) {
171                     $bfsig .= "x";
172                     $bfsig .= ($off - $offset);
173                 }
174                 $bfsig .= $field{type};
175                 $bfsig .= "($field{name})";
176                 $offset = $off + $field{size};
177             } else {
178                 $inbf = 1;
179                 $bfsig = $field{type};
180                 $bfsig .= "($field{name})";
181                 $offset = $off + $field{size};
182                 $bfstart = $field{offset};
183             }
184
185             if ($union) {
186                 $inbf = 0;
187
188                 if (($offset - $bfstart) == 32) {
189                     $bfsig = "u32=[$bfsig]";
190                 } elsif (($offset - $bfstart) < 32) {
191                     $bfsig .= "x";
192                     $bfsig .= 32 - ($offset - $bfstart);
193                     $offset = $bfstart + 32;
194                     $bfsig = "u32=[$bfsig]";
195                 } elsif (($offset - $bfstart) == 64) {
196                     $bfsig = "u64=[$bfsig]";
197                 } elsif (($offset - $bfstart) < 64) {
198                     $bfsig .= "x";
199                     $bfsig .= 64 - ($offset - $bfstart);
200                     $offset = $bfstart + 64;
201                     $bfsig = "u64=[$bfsig]";
202                 }
203
204                 $sig .= $bfsig;
205                 $sig .= "|" if ($union && $fi != @fields[$#fields]);
206             }
207             next;
208         } elsif ($inbf) {
209             if (($offset - $bfstart) == 32) {
210                 $bfsig = "u32=[$bfsig]";
211             } elsif (($offset - $bfstart) < 32) {
212                 $bfsig .= "x";
213                 $bfsig .= 32 - ($offset - $bfstart);
214                 $offset = $bfstart + 32;
215                 $bfsig = "u32=[$bfsig]";
216             } elsif (($offset - $bfstart) == 64) {
217                 $bfsig = "u64=[$bfsig]";
218             } elsif (($offset - $bfstart) < 64) {
219                 $bfsig .= "x";
220                 $bfsig .= 64 - ($offset - $bfstart);
221                 $offset = $bfstart + 64;
222                 $bfsig = "u64=[$bfsig]";
223             }
224             $sig .= $bfsig;
225             $inbf = 0;
226         }
227
228         # skip to next offset if necessary
229         if ($off > $offset) {
230             $sig .= "x";
231             $sig .= ($off - $offset);
232         }
233         $offset = $off + $field{size};
234
235         # normal field processing
236         if ($field{deref}) {
237             my $deref = $field{deref};
238
239             # HACK: function -> Void
240         #   if ($field{debug} eq 'function') {
241         #       $sig .= "u64($field{name}):v";
242         #    } els
243                 if ($deref =~ m/^(u\d\d)(:.*)/) {
244                 $sig .= "$1($field{name})$2";
245             } else {
246                 $sig .= "$deref($field{name})";
247             }
248         } else {
249             if ($field{type} =~ m/(struct|union):(.*)/) {
250                 $sig .= "\${$2}";
251             } elsif ($field{type} =~ m/([iuf])(\d+)/) {
252                 $sig .= $1;
253                 $sig .= $2;
254             } elsif ($field{type} eq 'void') {
255                 $sig .= "v";
256             } elsif ($field{type} eq 'enum') {
257                 # FIXME: set type in compiler
258                 $sig .= "u32";
259             }
260
261             $sig .= "($field{name})";
262         }
263
264         $sig .= "|" if ($union && $fi != @fields[$#fields]);
265     }
266
267     # finish any trailing bitfield
268     # TODO: cleanup
269     if ($inbf) {
270         if (($offset - $bfstart) == 32) {
271             $bfsig = "u32=[$bfsig]";
272         } elsif (($offset - $bfstart) < 32) {
273             $bfsig .= "x";
274             $bfsig .= 32 - ($offset - $bfstart);
275             $offset = $bfstart + 32;
276             $bfsig = "u32=[$bfsig]";
277         } elsif (($offset - $bfstart) == 64) {
278             $bfsig = "u64=[$bfsig]";
279         } elsif (($offset - $bfstart) < 64) {
280             $bfsig .= "x";
281             $bfsig .= 64 - ($offset - $bfstart);
282             $offset = $bfstart + 64;
283             $bfsig = "u64=[$bfsig]";
284         }
285         #$bfsig .= "]";
286         $sig .= $bfsig;
287     }
288
289     return "[".$sig."]";
290 }
291
292 sub funcSignature {
293     my %func = %{shift(@_)};
294     my $sig = "";
295     my @params = @{$func{arguments}};
296
297     for $pi (@params) {
298         my %param = %{$pi};
299
300         if ($param{deref}) {
301             # HACK: function to void
302             if ($param{debug} eq "function") {
303                 $sig .= "u64:v";
304             } else {
305                 $sig .= $param{deref};
306             }
307         } else {
308             if ($param{type} =~ m/struct:(.*)/) {
309                 $sig .= "\${$1}";
310             } elsif ($param{type} =~ m/([iuf])(\d*)/) {
311                 $sig .= $1;
312                 $sig .= $2;
313             } elsif ($param{type} eq "void") {
314                 $sig .= "v";
315             }
316         }
317     }
318
319     my %result = %{$func{result}};
320     my $ret = "";
321
322     if ($result{deref}) {
323         $ret .= $result{deref};
324     } else {
325         if ($result{type} =~ m/^struct:(.*)/) {
326             $ret .= "\${$1}";
327         } elsif ($result{type} =~ m/^([iuf])(\d+)/) {
328             $ret .= $1;
329             $ret .= $2;
330         } elsif ($result{type} eq "void") {
331             $ret .= "v";
332         }
333     }
334
335     return "($sig)$ret";
336 }
337
338 sub deref {
339     my $type = shift @_;
340     my $ref = shift @_;
341
342     while ($ref) {
343         if ($ref =~ m/\[\d*(.*)\]/) {
344             my $sub = deref($type, $1);
345
346             return "Array<$sub>";
347         } elsif ($ref =~ m/^u64:(.*)/) {
348             $type = "Pointer<$type>";
349             $ref = $1;
350         } else {
351             last;
352         }
353     }
354     return $type;
355 }
356
357 sub typeToJava {
358     my %param = %{shift(@_)};
359     my $type = $param{type};
360     my $ref = $param{deref};
361
362     if ($type =~ m/^struct:(.*)/) {
363         $type = StudlyCaps($1);
364     } elsif ($type =~ m/call:/) {
365         # this re-writes ref to remove one pointer-to as the Callback absorbs it.
366         $type = "Callback<".$callMap{$type}.">";
367         $type || die ("No mapping for type ".Dumper(\%param));
368         $ref =~ s/^u(32|64)://;
369     } elsif ($type =~ m/^enum:(.*)/) {
370         # TODO: other enum options
371         $type = "int";
372     } elsif ($type eq "void") {
373         $type = "void";
374     } elsif ($type =~ m/^([iu])(\d*)/) {
375         my $sign = $1;
376         my $size = $2;
377
378         if ($size <= 8) {
379             $type = "byte";
380         } elsif ($size <= 16) {
381             if ($sign eq "i") {
382                 $type = "short";
383             } else {
384                 $type = "char";
385             }
386         } elsif ($size <= 32) {
387             $type = "int";
388         } else {
389             $type = "long";
390         }
391     } elsif ($type =~ m/^[f](\d*)$/) {
392         my $size = $1;
393
394         if ($size == 32) {
395             $type = "float";
396         } elsif ($size == 64) {
397             $type = "double";
398         }
399     }
400
401     if ($ref) {
402         $type = $map_box{$type} if ($map_box{$type});
403         $type = deref($type, $ref);
404     }
405
406     return $type;
407 }
408
409 sub testMatch {
410     my $name = shift @_;
411
412     if (@_) {
413         for $pat (@_) {
414             if ($name =~ /$pat/) {
415                 return 1;
416             }
417         }
418         return 0;
419     } else {
420         return 1;
421     }
422 }
423
424 # find all matching structures and then all that they require
425 sub findStructs {
426     my %all = %{shift @_};
427     my @match = @_;
428     my @stack = grep {
429         my %e = %{$all{$_}};
430         $e{type} =~ m/(struct|union)/ && testMatch($e{name}, @match);
431     } keys %all;
432     my %visit = ();
433
434     while (@stack) {
435         my $test = shift @stack;
436
437         if (!$visit{$test}) {
438             my %struct = %{$all{$test}};
439
440             $visit{$test} = 1;
441
442             if (%struct) {
443                 print "class: $struct{name}\n";
444                 # find all types this one uses
445                 for $f (@{$struct{fields}}) {
446                     my %field = %{$f};
447
448                     if ($field{type} =~ m/^(struct|union):(.*)/) {
449                         if (!$set{$field{type}}) {
450                             $set{$field{type}} = $all{$field{type}};
451                             push @stack, $field{type};
452                         }
453                     }
454                 }
455             } else {
456                 # this is an anon type, typically used for handles
457                 $test =~ m/^(struct|union):(.*)/;
458                 print " anon: $2\n";
459                 my %rec = (
460                     type => 'struct',
461                     name => $2,
462                     size => 0
463                     );
464                 $data{$test} = \%rec;
465             }
466         }
467     }
468     return keys %visit;
469 }
470
471 sub findDefinition {
472     my %all = %{shift @_};
473     my $type = shift @_;
474     my @match = @_;
475     my @stack = grep {
476         my %e = %{$all{$_}};
477         $e{type} eq $type && testMatch($e{name}, @match);
478     } keys %all;
479
480     return @stack;
481 }
482
483 # ######################################################################
484
485 # setup section
486
487 # find all classes used by functions
488 my %roots = ();
489 for $c (@classes) {
490     my %class = %{$c};
491     my @libs = @{$class{libs}};
492     my @match = @{$class{match}};
493
494     for $k (findDefinition(\%data, 'func', @match)) {
495         my %func = %{$data{$k}};
496         my @params = @{$func{arguments}};
497
498         for $pi (@params) {
499             my %param = %{$pi};
500
501             if ($param{type} =~ m/^(struct|union):(.*)/) {
502                 $roots{$2} = 1;
503             }
504         }
505
506         my %result = %{$func{result}};
507
508         if ($result{type} =~ m/^(struct|union):(.*)/) {
509             $roots{$2} = 1;
510         }
511     }
512 }
513
514 # add roots for any types used by calls
515 # FIXME: only include ones used elsewhere
516 for $k (grep { $_ =~ m/^call:/n } keys %data) {
517     my %func = %{$data{$k}};
518     my @params = @{$func{arguments}};
519
520     for $pi (@params) {
521         my %param = %{$pi};
522
523         if ($param{type} =~ m/^(struct|union):(.*)/) {
524             $roots{$2} = 1;
525         }
526     }
527
528     my %result = %{$func{result}};
529
530     if ($result{type} =~ m/^(struct|union):(.*)/) {
531         $roots{$2} = 1;
532     }
533 }
534
535 # Create anonymous structs for anything missing
536 for $k (keys %roots) {
537     my $s = 'struct:'.$k;
538     my $u = 'union:'.$k;
539
540     if (!$data{$u} && !$data{$s}) {
541         print " anon: $s\n";
542         my %rec = (
543             type => 'struct',
544             name => $k,
545             size => 0
546             );
547         $data{$s} = \%rec;
548     }
549 }
550
551 $all = join ('|', keys %roots);
552 if ($all) {
553     push @matchStruct, qr/^($all)$/;
554 }
555 print "structures:\n";
556 print Dumper(@matchStruct);
557
558 # make a map for all callbacks (call: type) to generated names
559 for $c (grep { $_ =~ m/^call:/n } keys %data) {
560     my $name = $c;
561
562     print "$c\n";
563     # enum maybe to int?
564
565     $name =~ s/^call:/Call/;
566     $name =~ s/\$\{[^\}]*\}/L/g;
567     $name =~ s/[ui](64|32):/p/g;
568     $name =~ s/[ui]64/J/g;
569     $name =~ s/[ui]32/I/g;
570     $name =~ s/[ui]8/B/g;
571     $name =~ s/f32/F/g;
572     $name =~ s/f64/D/g;
573     $name =~ s/[\[\]\(\)]/_/g;
574
575     $callMap{$c} = "$name";
576 }
577
578 print "call mappings\n";
579 print Dumper(\%callMap);
580
581 # ######################################################################
582 # Start output
583 my $dst;
584
585 use File::Basename;
586 use File::Path qw(make_path);
587
588 if ($package ne "") {
589     $packagePrefix = $package.".";
590 }
591
592 if ($enclosingType) {
593     my $classname = $packagePrefix.$enclosingType;
594
595     $classname =~ s@\.@/@g;
596
597     my $path = $output."/".$classname.".java";
598     my $dir = dirname($path);
599     my $class = basename($path, ".java");
600
601     print "path $path\n";
602     print "dirname $dir\n";
603
604     make_path($dir);
605     open ($dst, ">$path");
606
607     if ($package ne "") {
608         print $dst "package $package;\n";
609     }
610
611     print $dst <<END;
612 import java.foreign.Libraries;
613 import java.foreign.annotations.*;
614 import java.foreign.memory.*;
615 import java.lang.invoke.MethodHandles;
616 END
617     print $dst "public class $class {\n";
618 }
619
620 # Dump structures
621 for $k (findStructs(\%data, @matchStruct)) {
622     my %struct = %{$data{$k}};
623     my @fields = @{$struct{fields}};
624     my $signature = structSignature(\%struct, ($struct{type} eq "union"));
625     my $name = StudlyCaps($struct{name});
626
627     if (!$enclosingType) {
628         my $classname = $packagePrefix.$name;
629
630         open ($dst, ">$path");
631         $classname =~ s@\.@/@g;
632
633         my $path = $output."/".$classname.".java";
634         my $dir = dirname($path);
635         my $class = basename($path, ".java");
636         make_path($dir);
637         open ($dst, ">$path");
638
639         if ($package ne "") {
640             print $dst "package $package;\n";
641         }
642         print $dst <<END;
643 import java.foreign.annotations.*;
644 import java.foreign.memory.*;
645 END
646     }
647
648     # any in-line structures need to be added to the resolutionContext
649     # TODO: only include actual inline, not pointers
650     my %resolve = ();
651     for $fi (@fields) {
652         my %field = %{$fi};
653
654         if ($field{type} =~ m/^(struct|union):(.*)/) {
655             $resolve{StudlyCaps($2).".class"} = 1;
656         }
657     }
658     my $resolve = join (",", keys %resolve);
659
660     print $dst "\@NativeStruct(value=\"$signature($struct{name})\"";
661     print $dst ", resolutionContext={$resolve}" if ($resolve);
662     print $dst ")\n";
663     print $dst "public interface $name extends Struct<$name> {\n";
664
665     for $fi (@fields) {
666         my %field = %{$fi};
667         my $type = typeToJava(\%field);
668         my $cc = StudlyCaps($field{name});
669
670         print $dst "\t\@NativeGetter(value=\"$field{name}\")\n";
671         print $dst "\tpublic $type get$cc();\n";
672
673         print $dst "\t\@NativeSetter(value=\"$field{name}\")\n";
674         print $dst "\tpublic void set$cc($type value);\n";
675     }
676
677     print $dst "}\n";
678
679     if (!$enclosingType) {
680         close($dst);
681     }
682 }
683
684 # Dump classes for library linkage
685 for $c (@classes) {
686     my %class = %{$c};
687     my @libs = @{$class{libs}};
688     my @match = @{$class{match}};
689
690     if (!$enclosingType) {
691         my $classname = $packagePrefix.$class{name};
692
693         open ($dst, ">$path");
694         $classname =~ s@\.@/@g;
695
696         my $path = $output."/".$classname.".java";
697         my $dir = dirname($path);
698         my $class = basename($path, ".java");
699         make_path($dir);
700         open ($dst, ">$path");
701
702         if ($package ne "") {
703             print $dst "package $package;\n";
704         }
705         print $dst <<END;
706 import java.foreign.Libraries;
707 import java.foreign.annotations.*;
708 import java.foreign.memory.*;
709 import java.lang.invoke.MethodHandles;
710 END
711     }
712
713     print $dst "\@NativeHeader(libraries={";
714     print $dst join(",", map { "\"$_\"" } @libs);
715     print $dst "})\n";
716     print $dst "public interface $class{name} {\n";
717
718     # enums to ints
719     # TODO: interfaces?
720     # TODO: static lib class?
721     # typedef enums might appear twice in the data, so ignore duplicates
722     # also, some api's have multiple definitions (?)
723     my %visited = ();
724     my @match_enum = @{$class{enum}};
725     for $k (sort(findDefinition(\%data, 'enum', @match_enum))) {
726         my %enum = %{$data{$k}};
727         my @values = @{$enum{values}};
728         my $type = "int";
729
730         if ($enum{value_type} =~ m/^[ui](\d+)/) {
731             $type = "long" if ($1 > 32)
732         }
733
734         print $dst "\n\t// enum $enum{name}\n";
735         for $vi (@values) {
736             my %value = %{$vi};
737
738             if (!$visited{$value{label}}) {
739                 #print $dst "\tpublic static final $type $value{label} = ($type)$value{value};\n";
740                 print $dst "\tpublic static final $type $value{label} = $value{value};\n";
741                 $visited{$value{label}} = 1;
742             }
743         }
744     }
745
746     # functions
747     print "class $class{name} -> match:\n".Dumper(\@match);
748     
749     for $k (sort(findDefinition(\%data, 'func', @match))) {
750         my %func = %{$data{$k}};
751         my @params = @{$func{arguments}};
752         my $signature = funcSignature(\%func);
753         my $name = ($func{name});
754         my $result = typeToJava(\%{$func{result}});
755
756         print $dst "\n\t\@NativeFunction(value=\"$signature\")\n";
757         print $dst "\tpublic $result $name(";
758
759         for $pi (@params) {
760             my %param = %{$pi};
761             my $type = typeToJava($pi);
762
763             print $dst "$type $param{name}";
764             print $dst ", " if ($pi != $params[$#params]);
765         }
766
767         print $dst ");\n";
768     }
769
770     print $dst "\n";
771     print $dst "\tpublic static final $class{name} bind = Libraries.bind(MethodHandles.lookup(), $class{name}.class);\n";
772
773     print $dst "}\n";
774
775     if (!$enclosingType) {
776         close($dst);
777     }
778 }
779
780 # Dump callbacks
781 # TODO: only those used by classes and functions that were exported
782 for $c (keys %callMap) {
783     my %call = %{$data{$c}};
784     my $name = $callMap{$c};
785     my @params = @{$call{arguments}};
786     my $result = typeToJava(\%{$call{result}});
787
788     if (!$enclosingType) {
789         my $classname = $packagePrefix.$name;
790
791         open ($dst, ">$path");
792         $classname =~ s@\.@/@g;
793
794         my $path = $output."/".$classname.".java";
795         my $dir = dirname($path);
796         my $class = basename($path, ".java");
797         make_path($dir);
798         open ($dst, ">$path");
799
800         if ($package ne "") {
801             print $dst "package $package;\n";
802         }
803         print $dst <<END;
804 import java.foreign.Libraries;
805 import java.foreign.annotations.*;
806 import java.foreign.memory.*;
807 END
808     }
809
810     # any in-line structures need to be added to the resolutionContext
811     # TODO: only include actual inline, not pointers
812     my %resolve = ();
813     my @list = @params;
814     unshift(@list,$call{result});
815     for $pi (@list) {
816         my %param = %{$pi};
817
818         if ($param{type} =~ m/^(struct|union):(.*)/) {
819             $resolve{StudlyCaps($2).".class"} = 1;
820         }
821     }
822     my $resolve = join (",", keys %resolve);
823
824     # FIXME: use something other than name to store this
825     print $dst "\@FunctionalInterface\n";
826     print $dst "\@NativeCallback(value=\"$call{name}\"";
827     print $dst ", resolutionContext={$resolve}" if ($resolve);
828     print $dst ")\n";
829     print $dst "public interface $name {\n";
830     print $dst "\tpublic $result fn(";
831
832     for $pi (@params) {
833         my %param = %{$pi};
834         my $type = typeToJava($pi);
835
836         print $dst "$type $param{name}";
837         print $dst ", " if ($pi != $params[$#params]);
838     }
839
840     print $dst ");\n";
841     print $dst "}\n";
842
843     if (!$enclosingType) {
844         close($dst);
845     }
846 }
847
848 # Finish off
849 if ($enclosingType) {
850     print $dst "}\n";
851     close($dst);
852 }