3 # -*- Mode:perl; perl-indent-level:4;tab-width:4; -*-
5 # TODO: global defaults and/or pattern matching on api targets, partially done
6 # TODO: perhaps an option which just dumps everything it finds at some level
7 # TODO: the code here isn't a complete mess but could be improved
8 # - parameterise some functions
9 # TODO: map int to boolean where appropriate
10 # TODO: arrays with specified lengths passed as arguments could be size-checked in function stubs.
11 # TODO: error codes -> exceptions?
12 # TODO: auto-loading of libraries (library xx load=blah) option.
16 use File::Path qw(make_path);
18 $scriptPath = dirname(__FILE__);
26 # -t package target package
27 # -d directory output root
29 # -a datafile add datafile to the dataset, can be from export.so or export-defines, etc
32 i8 => 'byte', u8 => 'byte',
33 i16 => 'short', u16 => 'short',
34 i32 => 'int', u32 => 'int',
35 i64 => 'long', u64 => 'long',
40 # or just use some formatting function table
60 my %intSizes = ( 8 => 'byte', 16 => 'short', 32 => 'int', 64 => 'long' );
77 "MemorySegment" => "Ljdk/incubator/foreign/MemorySegment;",
78 "MemoryAddress" => "Ljdk/incubator/foreign/MemoryAddress;",
82 'studly-caps' => sub { my $s = shift; $s =~ s/(?:^|_)(.)/\U$1/g; return $s; },
83 'camel-case' => sub { my $s = shift; $s =~ s/(?:_)(.)/\U$1/g; return $s; },
84 'upper-leadin' => sub { my $s = shift; $s =~ s/^([^_]+)/\U$1/; return $s; },
85 'identity' => sub { return $_[0]; },
95 } elsif ($cmd eq "-d") {
97 } elsif ($cmd eq "-v") {
99 } elsif ($cmd eq "-a") {
105 unless ($info = do $file) {
106 die "couldn't parse $file: $@" if $@;
107 die "couldn't import $file: $!" unless defined $info;
108 die "couldn't run $file" unless $info;
111 %data = (%data, %{$info});
117 my $api = loadControlFile($apidef);
120 analyseAndFixTypes();
123 $s = $data{'struct:AVCodecContext'};
124 $obj = findAPIObject($api, 'struct', $s->{name});
127 foreach $m (@{$s->{fields}}) {
128 $inc = findAPIField($obj, $m->{name});
129 print " $m->{name} - $inc\n";
133 my $toDump = analyseDependencies(\%data, findRoots($api));
137 print Dumper(\%data);
142 # find api.struct that matches a given struct name
148 print "find api for $type:$name\n" if $verbose;
149 foreach $obj ( @{$api->{$type}} ) {
150 next if $obj->{name} eq '<default>';
151 print " $obj->{name} ? $name\n" if $verbose;
152 if ($obj->{name} =~ m@/(.*)/@) {
154 return $obj if ($name =~ m/$rx/);
155 } elsif ($obj->{name} eq $name) {
160 print " -> fallback=$type:<default>\n" if $verbose && $api->{"$type:<default>"};
161 return $api->{"$type:<default>"};
164 # sub findAPIStruct {
168 # foreach $obj ( @{$api->{struct}} ) {
169 # next if $obj->{name} eq '<default>';
170 # print "$obj->{name} ? $name\n" if $verbose;
171 # if ($obj->{name} =~ m@/(.*)/@) {
173 # return $obj if ($name =~ m/$rx/);
174 # } elsif ($obj->{name} eq $name) {
179 # return $api->{'struct:<default>'};
186 foreach $inc (grep { $_->{mode} eq 'field' } @{$obj->{items}}) {
187 return $inc if $name =~ m/$inc->{regex}/;
191 # find all directly referenced types and field types from api
196 foreach $obj ( @{$api->{library}} ) {
197 foreach $inc ( @{$obj->{items}} ) {
198 if ($inc->{mode} eq 'func') {
199 my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
201 foreach $func (@list) {
202 $seen{"func:$func->{name}"} ++;
208 # all defines included
209 foreach $def ( @{$api->{define}} ) {
210 $seen{"define:$def->{name}"} ++;
213 foreach $obj ( @{$api->{struct}}, @{$api->{func}}) {
216 if ($obj->{name} =~ m@/(.*)/@) {
217 my $rx = "$obj->{type}:$1";
218 push @list, grep { $_ =~ m/$rx/ } keys %data;
220 push @list, "$obj->{type}:$obj->{name}";
229 delete $seen{'struct:<default>'};
230 delete $seen{'func:<default>'};
231 delete $seen{'call:<default>'};
233 my @list = sort keys %seen;
238 # analyse dependencies of the supplied roots
239 # only fields actually referenced in the api.def file are included
240 # \%seen = \%data, \@roots
241 sub analyseDependencies {
243 my @roots = @{shift @_};
246 print "Finding dependencies of $#roots roots\n";
248 while ($#roots >= 0) {
249 my $name = shift @roots;
250 my $s = $data{$name};
253 next if $seen{$name}++;
255 print "visit $name $s->{name}\n" if $verbose;
257 if ($s->{type} =~ m/struct|union/) {
258 my $obj = findAPIObject($api, 'struct', $s->{name});
259 if ($obj->{default} eq 'all') {
260 push @list, @{$s->{fields}};
262 push @list, grep { findAPIField($obj, $_->{name}) } @{$s->{fields}};
264 } elsif ($s->{type} =~ m/func|call/) {
265 @list = @{$s->{arguments}};
266 push @list, $s->{result};
270 my $type = $m->{type};
272 print " item $m->{name} '$type'\n" if $verbose;
273 if ($m->{ctype} =~ m/enum (.*)/) {
277 push @roots,$type if $data{$type};
281 foreach $name (sort grep { m/:/ } keys %seen) {
288 # find which api->thing->items applies to a given field name, if any
296 #print "search for $target.$name in $type.$mode\n";
297 # what about exclude?
298 foreach $obj ( @{$api->{$type}} ) {
299 if ($obj->{name} eq $target) {
300 #print " found $target\n";
301 foreach $inc (grep { $_->{mode} eq $mode } @{$obj->{items}}) {
302 #print " check $inc->{match}\n";
303 return $inc if $name =~ m/$inc->{regex}/;
312 foreach $obj ( @{$api->{struct}}, @{$api->{library}}, @{$api->{func}}) {
313 $obj->{access} = 'rw';
314 $obj->{default} = 'all';
315 $obj->{rename} = $renameTable{'identity'};
316 $obj->{'func:rename'} = $renameTable{'identity'};
317 $obj->{'field:rename'} = $renameTable{'identity'};
318 foreach $o (@{$obj->{options}}) {
319 if ($o =~ m/^default=(none|all)$/) {
320 $obj->{default} = $1;
321 } elsif ($o =~ m/^access=([rwi]+)$/) {
323 } elsif ($o =~ m@^(rename|field:rename|func:rename)=(.*)@) {
326 if ($obj->{name} eq 'SwsContext') {
327 print "SwsContext rename = $o\n";
330 foreach $n (split /,/,$2) {
331 my $old = $obj->{$target};
332 my $new = $renameTable{$n};
334 if ($n =~ m@^s/(.*)/(.*)/$@) {
337 $obj->{$target} = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
339 $obj->{$target} = sub { my $s=shift; $s = $old->($s); return $new->($s); };
345 my $defmode = $obj->{type} eq 'library' ? 'func' : 'field';
347 foreach $inc (@{$obj->{items}}) {
348 if ($inc->{match} =~ m@^(field|func|define|struct|enum):/(.*)/$@) {
349 $inc->{regex} = qr/$2/;
350 $inc->{mode} = $1; # ?? "$1-include";
351 } elsif ($inc->{match} =~ m@^(field|func|define|struct|enum):(.*)$@) {
352 $inc->{regex} = qr/^$2$/;
354 } elsif ($inc->{match} =~ m@^/(.*)/$@) {
355 $inc->{regex} = qr/$1/;
356 $inc->{mode} = $defmode;
358 $inc->{regex} = qr/^$inc->{match}$/;
359 $inc->{mode} = $defmode;
362 $inc->{rename} = $renameTable{'identity'};
363 $inc->{scope} = 'static' if $obj->{type} eq 'library';
365 # maybe depends on mode above
366 foreach $o (@{$inc->{options}}) {
367 if ($o =~ m/^access=([rwi])+/) {
369 } elsif ($o =~ m/^rename=(.*)/) {
370 foreach $n (split /,/,$1) {
371 my $old = $inc->{rename};
372 my $new = $renameTable{$n};
374 if ($n =~ m@^s/(.*)/(.*)/$@) {
377 $inc->{rename} = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
379 $inc->{rename} = sub { my $s=shift; $s = $old->($s); return $new->($s); };
382 $inc->{rename} = sub { return $x; };
385 } elsif ($o =~ m/^array-size=(.*)/) {
386 $inc->{'array_size'} = $1;
387 } elsif ($o =~ m/^array$/) {
389 } elsif ($o =~ m/^instance=(.*)/) {
390 $inc->{instance} = $1;
391 } elsif ($o =~ m/^static$/) {
392 $inc->{scope} = 'static';
393 } elsif ($o =~ m/^constructor=(.*)$/) {
394 $inc->{constructor} = $1;
395 } elsif ($o =~ m/^constructor-result=(.*)$/) {
396 $inc->{constructor_result} = $1;
397 } elsif ($o =~ m/^success=(.*)$/) {
398 $inc->{success} = $1;
403 $inc->{rename} = $obj->{"$inc->{mode}:rename"} if $inc->{rename} == $renameTable{'identity'} && $obj->{"$inc->{mode}:rename"};
406 if ($obj->{name} eq '<default>') {
407 $api->{"$obj->{type}:<default>"} = $obj;
411 $api->{'call:<default>'} = { rename => $renameTable{'identity'}, scope => 'static'} if !$api->{'call:<default>'};
415 # the exporter doesn't output anonymous structs as they might
416 # just be forward references. this fills in any missing types.
418 # anonymous functions are referenced by signature, convert any to an identifier
420 # setup typeInfo for all type references - memebers, fields, return values
421 sub analyseAndFixTypes {
424 # pass 1, fix call definition names and keys
425 foreach $old (keys %data) {
426 if ($old =~ m/^call:/) {
430 foreach $old (@replace) {
434 $new =~ s/(.*)\((.*)\)(.*)/$1Call_$2_$3/;
435 $data{$new} = $c = delete $data{$old};
436 $c->{name} =~ s/(.*)\((.*)\)(.*)/$1Call_$2_$3/;
439 # pass 2 add typeinfo and anonymous types, fix call types
440 foreach $n (keys %data) {
444 if ($s->{type} =~ m/struct|union/) {
445 @list = @{$s->{fields}};
446 } elsif ($s->{type} =~ m/func|call/) {
447 @list = @{$s->{arguments}};
448 push @list, $s->{result};
452 if ($a->{type} =~ m/(struct|union):(.*)/ && !defined($data{$a->{type}})) {
453 print "Add anonymous $1 $2\n";
454 $data{$a->{type}} = {
461 if ($a->{type} =~ m/^call:/) {
462 $a->{type} =~ s/(.*)\((.*)\)(.*)/$1Call_$2_$3/;
466 $a->{typeInfo} = analyseTypeInfo($s, $a);
470 # pass 3 create java signatures
471 foreach $n (keys %data) {
474 if ($s->{type} =~ m/^(call|func)$/) {
475 $s->{signature} = formatSignature($s);
483 return $m->{type} eq 'void' && !$m->{deref};
486 # format a single layout type item for non-bitfield types
487 # type - type record that contains type and deref
488 # withName - '.withName()' - empty, or really any other MemoryLayout adjustment functions.
489 sub formatTypeLayout {
491 my $withName = shift @_;
494 if ($m->{deref} =~ m/^(u64|u32):/) {
495 $desc .= "Memory.POINTER$withName";
496 } elsif ($m->{type} =~ m/^([iuf]\d+)$/) {
497 if ($m->{deref} =~ m/\[(\d*)u64:.*\]/) {
498 $desc .= "MemoryLayout.sequenceLayout($1, Memory.POINTER)$withName";
499 } elsif ($m->{deref} =~ m/\[(\d*).*\]/) {
500 $desc .= "MemoryLayout.sequenceLayout($1, Memory.".uc($typeSizes{$m->{type}}).")$withName";
502 $desc .= 'Memory.'.uc($typeSizes{$m->{type}})."$withName";
504 } elsif ($m->{type} =~ m/^(struct|union):(.*)/) {
506 if ($m->{deref} =~ m/\[(\d*)u64:.*\]/) {
507 $desc .= "MemoryLayout.sequenceLayout($1, Memory.POINTER)$withName";
508 } elsif ($m->{deref} =~ m/\[(\d*).*\]/) {
509 $desc .= "MemoryLayout.sequenceLayout($1, $type.LAYOUT)$withName";
511 $desc .= "$type.LAYOUT$withName";
515 die ("unknown type");
521 sub formatFunctionDescriptor {
523 my @arguments = @{$c->{arguments}};
524 my $result = $c->{result};
528 if (!isVoid($result)) {
529 $desc = "FunctionDescriptor.of(\n ";
530 $desc .= formatTypeLayout($result);
533 $desc = "FunctionDescriptor.ofVoid(\n ";
536 foreach $m (@arguments) {
537 $desc .= ",\n " if ($index++ > 0);
538 $desc .= formatTypeLayout($m, ".withName(\"$m->{name}\")");
546 sub formatSignature {
548 my @arguments = @{$c->{arguments}};
551 foreach $m (@arguments) {
552 $desc .= $typeSignature{$m->{typeInfo}->{carrier}};
556 if ($c->{result}->{typeInfo}->{type} ne 'void') {
557 $desc .= $typeSignature{$c->{result}->{typeInfo}->{carrier}};
565 # TODO: perhaps ByteArray should just be MemorySegment, kinda painful to wrap them all the time
566 sub analyseTypeInfo {
572 #print " query $s->{name} $s->{type} '$m->{name}', '$m->{type}'\n";
573 if ($s->{type} eq 'struct') {
574 $inc = findAPIItem($api, 'struct', $s->{name}, 'field', $m->{name});
575 } elsif ($s->{type} eq 'func') {
576 $inc = findAPIItem($api, 'func', $s->{name}, 'field', $m->{name});
579 # default for everything not specifically handled
580 $info->{carrier} = "MemoryAddress";
581 $info->{resolve} = "(Addressable)Memory.address(\${value})";
583 if ($m->{deref} =~ m/^(u64:|u32:)\(/) {
584 # This is a function pointer, type must be type = 'call:.*'
585 if ($m->{type} =~ m/^call:(.*)/) {
586 $info->{type} = "Memory.FunctionPointer<$1>";
587 $info->{create} = "$1.downcall(\${result}, \${scope})";
591 } elsif ($m->{type} =~ m/^([iuf]\d+)$/) {
592 if ($m->{deref} =~ m/\[(\d*)u64:.*\]/) {
593 $info->{byValue} = 1;
594 $info->{type} = "Memory.PointerArray";
595 $info->{create} = $info->{type}.".create(\${result})";
596 } elsif ($m->{deref} =~ m/\[(\d*).*\]/) {
597 # TODO: some mode thing rather than byvalue?
598 $info->{byValue} = 1;
599 $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
600 $info->{create} = $info->{type}.".create(\${result})";
601 } elsif ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
602 $info->{type} = "Memory.PointerArray";
603 $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
604 } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
605 # assume any char * is a string unless an array-size or array is specified
607 $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
608 $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
609 } elsif ($inc->{array_size}) {
610 $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
611 $info->{create} = $info->{type}.".createArray(\${result}, \${array_size}, \${scope})";
612 } elsif ($typeSizes{$m->{type}} eq 'byte') {
613 $info->{type} = 'String';
614 $info->{resolve} = "(Addressable)Memory.address(frame.copy(\${value}))";
615 $info->{create} = "(\${result}).getUtf8String(0)";
616 $info->{resolveFrame} = 1;
617 # for a function or a constructor that uses this element
618 $s->{resolveFrame} = 1;
620 # ideally length 0 but panama-foreign doesn't grok that so fuckit
621 $info->{type} = "Memory.".ucfirst($typeSizes{$m->{type}})."Array";
622 $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
625 $info->{type} = $typeSizes{$m->{type}};
626 $info->{carrier} = $typeSizes{$m->{type}};
627 $info->{resolve} = "($info->{type})(\${value})";
628 $info->{create} = "\${result}";
630 } elsif ($m->{type} =~ m/^(struct|union):(.*)/) {
632 if ($m->{deref} =~ m/\[(\d*)(.*)\]/) {
633 $info->{byValue} = 1;
634 # handle 'type name[x]' and 'type *name[x]'
637 if ($deref =~ m/^u64:u64:/) {
638 die("can't handle double-deref array");
639 } elsif ($deref =~ m/^u64:/) {
640 $info->{type} = "Memory.HandleArray<$type>";
641 $info->{create} = "Memory.HandleArray.create(\${result}, $type\:\:create, \${scope})";
643 $info->{type} = $type;
644 #$info->{create} = $info->{type}.".create(\${result}, \${scope})";
645 $info->{create} = $info->{type}.".create(\${result})";
647 } elsif ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
648 # this assumes ** is as far as it gets
649 $info->{type} = "Memory.HandleArray<$type>";
650 if ($inc->{array_size}) {
651 $info->{create} = "Memory.HandleArray.createArray(\${result}, \${array_size}, $type\:\:create, \${scope})";
653 $info->{create} = "Memory.HandleArray.createArray(\${result}, Long.MAX_VALUE, $type\:\:create, \${scope})";
655 } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
656 $info->{type} = $type;
657 $info->{create} = $info->{type}.".create(\${result}, \${scope})";
659 # FIXME: change this to a reftype or something
660 $info->{byValue} = 1;
661 $info->{type} = $type;
662 $info->{create} = $info->{type}.".create(\${result})";
664 } elsif ($m->{type} eq "void") {
665 if ($m->{deref} =~ m/^(u64:u64:|u32:u32:)/) {
666 $info->{type} = "Memory.PointerArray";
667 $info->{create} = $info->{type}.".createArray(\${result}, Long.MAX_VALUE, \${scope})";
668 } elsif ($m->{deref} =~ m/^(u64:|u32:)/) {
669 $info->{type} = "MemoryAddress";
670 $info->{create} = "\${result}";
671 $info->{resolve} = "(Addressable)\${value}";
673 $info->{type} = "void";
674 $info->{carrier} = "void";
675 delete $info->{resolve};
679 die ("unknown type");
685 # TODO: see if this can be merged with formatFunction
686 # TODO: should constructor take a resourcescope always?
687 # TODO: exceptions for errors
688 sub formatConstructor {
691 my @arguments = @{$c->{arguments}};
692 my $result = $c->{result};
697 my $name = $c->{name};
698 my $rtype = $result->{typeInfo}->{type};
699 my $otype = $data{$arguments[$inc->{constructor_result}]->{type}}->{name};
702 $name = $inc->{rename}->($name);
704 $desc .= "static " if $inc->{scope} eq 'static';
708 for $m (@arguments) {
709 if (($inc->{constructor} && $index != $inc->{constructor_result})) {
710 $desc .= ", " if ($count++ > 0);
711 $desc .= $m->{typeInfo}->{type};
712 $desc .= " $m->{name}"
716 if ($inc->{constructor}) {
717 $desc .= ", " if ($count++ > 0);
718 $desc .= "ResourceScope scope";
722 $desc .= "$result->{typeInfo}->{carrier} res\$value;\n" if ($rtype ne "void");
725 $desc .= "(Frame frame = Memory.createFrame()) " if ($c->{resolveFrame});
727 $desc .= " Memory.HandleArray<$otype> res\$holder = Memory.HandleArray.createArray(1, frame, $otype\:\:create, scope);\n" if ($inc->{constructor});
728 $desc .= " res\$value = ($result->{typeInfo}->{carrier})" if ($rtype ne "void");
729 $desc .= " " if ($rtype eq "void");
732 $desc .= "$c->{name}\$FH.invokeExact(\n ";
733 for $m (@arguments) {
734 my $resolve = $m->{typeInfo}->{resolve};
736 if ($inc->{constructor} && $index == $inc->{instance}) {
737 $desc .= ",\n " if ($index++ > 0);
738 $desc .= "(Addressable)res\$holder.address()";
739 } elsif ($inc->{scope} ne 'static' && $index == $inc->{instance}) {
740 $desc .= ",\n " if ($index++ > 0);
741 $desc .= "(Addressable)address()";
743 $desc .= ",\n " if ($index++ > 0);
746 $resolve =~ s/\$\{value\}/$m->{name}/g;
749 $desc .= "$m->{name}";
755 if ($rtype ne "void" && defined $inc->{success}) {
756 # my $create = $result->{typeInfo}->{create};
758 # # ooh, templates could insert other arguments or values as well?
759 # $create =~ s/\$\{result\}/res\$value/;
760 # if ($inc->{scope} eq 'static') {
761 # $create =~ s/\$\{scope\}/ResourceScope.globalScope()/;
763 # $create =~ s/\$\{scope\}/scope()/;
766 foreach $code (split /,/,$inc->{success}) {
767 $desc .= " if (res\$value == $code) return res\$holder.getAtIndex(0);\n";
770 $desc .= " return res\$holder.getAtIndex(0);\n";
773 $desc .= " } catch (Throwable t) { throw new RuntimeException(t); }\n";
775 # throw failures here based on res$value
776 $desc .= " return null;\n";
787 my @arguments = @{$c->{arguments}};
788 my $result = $c->{result};
793 my $name = $c->{name};
794 my $rtype = $result->{typeInfo}->{type};
796 if ($inc->{constructor}) {
797 return formatConstructor($c, $inc);
800 $name = $inc->{rename}->($name);
802 $desc .= "static " if $inc->{scope} eq 'static';
806 for $m (@arguments) {
807 if ($inc->{scope} eq 'static' || $index != $inc->{instance}) {
808 $desc .= ", " if ($count++ > 0);
809 $desc .= $m->{typeInfo}->{type};
810 $desc .= " $m->{name}"
817 $desc .= "(Frame frame = Memory.createFrame()) " if ($c->{resolveFrame});
819 $desc .= " $result->{typeInfo}->{carrier} res\$value = ($result->{typeInfo}->{carrier})" if ($rtype ne "void");
820 $desc .= " " if ($rtype eq "void");
823 $desc .= "$c->{name}\$FH.invokeExact(\n ";
824 for $m (@arguments) {
825 my $resolve = $m->{typeInfo}->{resolve};
827 if ($inc->{scope} ne 'static' && $index == $inc->{instance}) {
828 $desc .= ",\n " if ($index++ > 0);
829 $desc .= "(Addressable)address()";
831 $desc .= ",\n " if ($index++ > 0);
834 $resolve =~ s/\$\{value\}/$m->{name}/g;
837 $desc .= "$m->{name}";
843 if ($rtype ne "void") {
844 my $create = $result->{typeInfo}->{create};
846 # ooh, templates could insert other arguments or values as well?
847 $create =~ s/\$\{result\}/res\$value/;
848 if ($inc->{scope} eq 'static') {
849 $create =~ s/\$\{scope\}/ResourceScope.globalScope()/;
851 $create =~ s/\$\{scope\}/scope()/;
854 $desc .= " return $create;\n";
857 $desc .= " } catch (Throwable t) { throw new RuntimeException(t); }\n";
864 # create an interface for function pointers
865 # FiXME: this should be exportCallback to a file?
869 my @arguments = @{$c->{arguments}};
870 my $result = $c->{result};
874 my $name = $c->{name};
879 my $rtype = $result->{typeInfo}->{type};
881 $desc = "\@FunctionalInterface\n";
882 $desc .= "public interface $name {\n";
884 # the public (functional) interface
886 $desc .= " $result->{typeInfo}->{type} call(";
887 for $m (@arguments) {
888 $desc .= ", " if ($index++ > 0);
889 $desc .= $m->{typeInfo}->{type};
890 $desc .= " $m->{name}"
894 # the internal interface
896 $desc .= " \@FunctionalInterface\n";
897 $desc .= " interface Trampoline {\n ";
898 $desc .= $result->{typeInfo}->{carrier};
900 for $m (@arguments) {
901 $desc .= ", " if ($index++ > 0);
902 $desc .= $m->{typeInfo}->{carrier};
903 $desc .= " $m->{name}"
909 $desc .= " static FunctionDescriptor DESCRIPTOR() {\n";
911 my $tmp = formatFunctionDescriptor($c);
917 # Factory method for upcalls
919 $desc .= " public static Memory.FunctionPointer<$name> upcall($name target, ResourceScope scope) {\n";
920 $desc .= " Trampoline trampoline = (";
922 for $m (@arguments) {
923 $desc .= ", " if ($index++ > 0);
924 $desc .= "$m->{name}"
927 #$desc .= " try {\n";
929 $desc .= "return " if $rtype ne "void";
930 $desc .= "target.call(\n ";
932 for $m (@arguments) {
933 my $create = $m->{typeInfo}->{create};
935 $create =~ s/\$\{result\}/$m->{name}/g;
936 $create =~ s/\$\{scope\}/scope/g;
938 $desc .= ",\n " if ($index++ > 0);
942 #$desc .= " } catch (Exception x) { }{\n";
943 # FIXME: or null for address
944 #$desc .= " return 0;\n" if $rtype != "void";
948 $desc .= " return new Memory.FunctionPointer<>(\n";
949 $desc .= " Memory.upcall(\n";
950 $desc .= " trampoline,\n";
951 $desc .= " \"call\",\n";
952 $desc .= " \"$c->{signature}\",\n";
953 $desc .= " DESCRIPTOR(),\n";
954 $desc .= " scope),\n";
955 $desc .= " target);\n";
959 $desc .= " public static Memory.FunctionPointer<$name> downcall(MemoryAddress addr, ResourceScope scope) {\n";
960 $desc .= " NativeSymbol symbol = NativeSymbol.ofAddress(\"$name\", addr, scope);\n";
961 $desc .= " MethodHandle $name\$FH = Memory.downcall(symbol, DESCRIPTOR());\n";
962 $desc .= " return new Memory.FunctionPointer<$name>(\n";
963 $desc .= " symbol,\n";
965 # HACK: this is basically the same as any function call, just patch in the changes for now
966 $tmp = formatFunction($c, $obj);
968 $tmp =~ s/^(.*) ($name)\(/(/;
969 $tmp =~ s/\) \{/) -> {/;
978 # replace leading ' ' with '\t'
979 $desc =~ s/(?:\G|^) /\t/mg;
984 # some bitfield support stuff.
985 # maximum size allowed for field holder based on start offset
988 my $offset = shift @_;
990 return 64 if ($offset & 63) == 0;
991 return 32 if ($offset & 31) == 0;
992 return 16 if ($offset & 15) == 0;
993 return 8 if ($offset & 7) == 0;
1000 return 64 if ($size > 32);
1001 return 32 if ($size > 16);
1002 return 16 if ($size > 8);
1007 # returns @sizes required to hold them, based on alignment rules
1009 my $offset = shift @_;
1010 my $bits = shift @_;
1011 my $end = $offset + $bits;
1014 while ($offset < $end) {
1015 my $limit = fieldLimit($bits);
1016 my $max = fieldMaxHolder($offset);
1017 my $step = ($limit < $max) ? $limit : $max;
1030 my @fields = @{$s->{fields}};
1032 my $bitfieldIndex = 0;
1037 $desc = "MemoryLayout.$s->{type}Layout(\n ";
1039 for (my $i = 0; $i <= $#fields; $i++) {
1040 my $f = $fields[$i];
1042 if ($f->{offset} > $last) {
1043 $desc .= ",\n" if ($index++ > 0);
1044 $desc .= ' MemoryLayout.paddingLayout('.($f->{offset} - $last).')';
1047 $maxSize = fieldLimit($f->{size}) if (fieldLimit($f->{size}) > $maxSize);
1049 if ($f->{ctype} eq 'bitfield') {
1050 my $start = $f->{offset};
1051 my $end = $f->{size} + $f->{offset};
1053 my $max = fieldMaxHolder($start);
1055 # breaks bitfields into char/short/int/long blocks
1056 # TODO: need more info for mapping to get/settters
1058 #print "> $f->{name} $f->{size} @ $f->{offset}\n";
1060 while ($j <= $#fields && $fields[$j]->{ctype} eq "bitfield") {
1061 my $g = $fields[$j];
1063 #print "> $g->{name} $g->{size} @ $g->{offset}\n";
1065 if ($g->{offset} > $end || ($g->{offset} - $start >= $max)) {
1066 foreach $size (fieldHolders($start, $end - $start)) {
1067 $desc .= ",\n " if ($index++ > 0);
1068 $desc .= 'Memory.'.uc($intSizes{$size}).".withName(\"bitfield\$$bitfieldIndex\")";
1071 $desc .= ",\n " if ($index++ > 0);
1072 $desc .= 'MemoryLayout.paddingLayout('.($g->{offset}-$end).')';
1073 $start = $g->{offset};
1074 $max = fieldMaxHolder($start);
1076 $end = $g->{size} + $g->{offset};
1080 foreach $size (fieldHolders($start, $end - $start)) {
1081 $desc .= ",\n " if ($index++ > 0);
1082 $desc .= 'Memory.'.uc($intSizes{$size}).".withName(\"bitfield\$$bitfieldIndex\")";
1089 $desc .= ",\n " if ($index++ > 0);
1090 $desc .= formatTypeLayout($f, ".withName(\"$f->{name}\")");
1093 $last = $fields[$i]->{offset} + $fields[$i]->{size};
1096 if ($last < $s->{size}) {
1097 $desc .= ",\n " if ($index++ > 0);
1098 $desc .= 'MemoryLayout.paddingLayout('.($s->{size} - ${last}).')';
1102 $desc .= ".withBitAlignment($maxSize)";
1114 my $info = $m->{typeInfo};
1115 my $Name = ucfirst($rename);
1118 # info -> needsalloc?
1120 # TODO: embedded arrays are quite different setup
1122 if ($info->{byValue}) {
1123 $tmp = $info->{create};
1124 $tmp =~ s/\$\{result\}/segment/g;
1125 $tmp =~ s/\$\{scope\}/scope()/g;
1127 $desc .= " public $info->{type} get$Name() {\n";
1128 #$desc .= " MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
1129 #$desc .= " MemorySegment segment = this.segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n";
1130 $desc .= " MemorySegment segment = this.segment.asSlice($m->{name}\$byteOffset, $m->{name}\$byteSize);\n";
1131 $desc .= " return $tmp;\n";
1134 if ($access =~ m/i/) {
1135 $desc .= " public $info->{type} get$Name"."At(long index) {\n";
1136 #$desc .= " MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
1137 #$desc .= " MemoryLayout.PathElement pe = MemoryLayout.PathElement.groupElement(\"$m->{name}\");\n";
1138 #$desc .= " segment = this.segment.asSlice(LAYOUT.byteOffset(pe), LAYOUT.select(pe).byteSize());\n";
1139 $desc .= " MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index + $m->{name}\$byteOffset, $m->{name}\$byteSize);\n";
1140 $desc .= " return $tmp;\n";
1143 } elsif ($access =~ /r/) {
1144 $tmp = $info->{create};
1146 $tmp =~ s/\$\{result\}/($info->{carrier})$m->{name}\$VH.get(segment)/g;
1147 $tmp =~ s/\$\{scope\}/scope()/g;
1148 # fixme: lookup type of array size? somewhere? doesn't matter i think
1149 $tmp =~ s/\${array_size}/(long)$inc->{array_size}\$VH.get(segment)/g;
1151 $desc .= " public $info->{type} get$Name() {\n";
1152 $desc .= " return $tmp;\n";
1155 if ($access =~ m/i/) {
1156 $desc .= " public $info->{type} get$Name"."At(long index) {\n";
1157 $desc .= " MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
1158 $desc .= " return $tmp;\n";
1163 if ($access =~ m/w/ && !$info->{byValue}) {
1164 $tmp = $info->{resolve};
1165 $tmp =~ s/\$\{value\}/value/g;
1167 $desc .= " public void set$Name($info->{type} value) {\n";
1168 $desc .= " try (Frame frame = Memory.createFrame()) {\n" if ($info->{resolveFrame});
1169 $desc .= " $m->{name}\$VH.set(segment, $tmp);\n";
1170 $desc .= " }\n" if ($info->{resolveFrame});
1173 if ($access =~ m/i/) {
1174 $desc .= " public void set$Name"."At(long index, $info->{type} value) {\n";
1175 $desc .= " try (Frame frame = Memory.createFrame()) {\n" if ($info->{resolveFrame});
1176 $desc .= " MemorySegment segment = this.segment.asSlice(LAYOUT.byteSize() * index, LAYOUT.byteSize());\n";
1177 $desc .= " $m->{name}\$VH.set(segment, $tmp);\n";
1178 $desc .= " }\n" if ($info->{resolveFrame});
1190 my @fields = @{$s->{fields}};
1191 my $isHandle = $s->{size} == 0;
1192 my $doArray = $obj->{access} =~ m/i/;
1193 #my @functions = @{shift @_};
1195 print $f "package $package;\n" if $package;
1197 print $f "import jdk.incubator.foreign.*;\n";
1198 print $f "import java.lang.invoke.*;\n";
1200 print $f "public class $s->{name} implements Memory.Addressable {\n";
1202 # TODO: parameterise and use typeInfo data.
1204 print $f " MemorySegment segment;\n";
1206 print $f " private $s->{name}(MemorySegment segment) { this.segment = segment; }\n";
1207 print $f " public static $s->{name} create(MemorySegment segment) { return new $s->{name}(segment); }\n";
1208 print $f " public static $s->{name} create(MemoryAddress address, ResourceScope scope) {\n";
1209 print $f " return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope)) : null;\n";
1212 print $f " public static $s->{name} createArray(MemoryAddress address, long size, ResourceScope scope) {\n";
1213 print $f " return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, size * LAYOUT.byteSize(), scope)) : null;\n";
1216 print $f " public static $s->{name} create(Frame frame) { return create(frame.allocate(LAYOUT)); }\n";
1217 print $f " public static $s->{name} create(ResourceScope scope) { return create(MemorySegment.allocateNative(LAYOUT, scope)); }\n";
1218 print $f " public MemoryAddress address() { return segment.address(); }\n";
1219 print $f " public ResourceScope scope() { return segment.scope(); }\n";
1221 # not sure if handles need scopes
1222 print $f " MemoryAddress address;\n";
1223 print $f " ResourceScope scope;\n";
1225 print $f " private $s->{name}(MemoryAddress address, ResourceScope scope) { this.address = address; this.scope = scope;}\n";
1226 print $f " public static $s->{name} create(MemoryAddress address, ResourceScope scope) { return MemoryAddress.NULL != address ? new $s->{name}(address, scope) : null; }\n";
1227 print $f " public MemoryAddress address() { return address; }\n";
1228 print $f " public ResourceScope scope() { return scope; }\n";
1234 foreach $inc (grep { $_->{mode} eq 'define' } @{$obj->{items}}) {
1235 my $def = $data{$inc->{match}};
1237 die ("unknown define $inc->{match} in $s->{name}\n") if !$def;
1239 delete $toDump->{$inc->{match}};
1241 foreach $m (@{$def->{values}}) {
1242 print $f " /**\n ($m->{comment}) */\n" if ($m->{comment});
1243 print $f " public static final $defineType{$m->{type}} $m->{name} = $definePrefix{$m->{type}}$m->{value}$defineSuffix{$m->{type}};\n";
1247 # TODO: any enums we want to include here I suppose?
1250 foreach $m (@fields) {
1251 my $access = $obj->{access};
1252 my $rename = $obj->{'field:rename'};
1257 foreach $inc (grep { $_->{mode} eq 'field' } @{$obj->{items}}) {
1258 $matches = $m->{name} =~ m/$inc->{regex}/;
1261 $access = $inc->{access} if $inc->{access};
1262 $rename = $inc->{rename} if $inc->{rename} != $renameTable{'identity'};
1268 my $output = $matches || ($obj->{default} eq 'all');
1271 my $name = $rename ? $rename->($m->{name}) : $m->{name};
1273 print $f formatGetSet($s, $m, $name, $access, $matchinc);
1278 foreach $inc (grep { $_->{mode} eq 'func' } @{$obj->{items}}) {
1281 print "$obj->{name} match $inc->{match} regex $inc->{regex}\n" if $verbose;
1283 if ($data{$inc->{match}}) {
1284 push @list, $data{$inc->{match}};
1286 @list = grep { $_->{name} =~ m/$inc->{regex}/ } values %data;
1289 foreach $c (@list) {
1292 next if $seen{$c->{name}}++;
1294 print $f " static final MethodHandle $c->{name}\$FH = Memory.downcall(\"$c->{name}\",\n";
1295 $tmp = formatFunctionDescriptor($c);
1296 print $f "$tmp);\n";
1298 $tmp = formatFunction($c, $inc);
1299 print $f 'public '.$tmp."\n\n";
1303 # layout and varhandles
1304 if ($#fields >= 0) {
1305 print $f "static final GroupLayout LAYOUT = ".formatLayout($s).";\n";
1307 foreach $m (@fields) {
1308 print $f " // type='$m->{type}' deref='$m->{deref}' info->type ='$m->{typeInfo}->{type}'\n";
1309 if ($m->{typeInfo}->{byValue}) {
1310 print $f " static final long $m->{name}\$byteOffset = "
1311 ." LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
1312 print $f " static final long $m->{name}\$byteSize = "
1313 ."LAYOUT.select(MemoryLayout.PathElement.groupElement(\"$m->{name}\")).byteSize();\n";
1315 print $f " static final VarHandle $m->{name}\$VH = "
1316 ."LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
1324 static void check\$offset(String name, long bitoffset) {
1325 long byteoffset = bitoffset/8;
1326 long offset = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement(name));
1327 if (offset != byteoffset)
1328 throw new AssertionError(String.format("%s.offset %d != %d", name, byteoffset, offset), null);
1332 foreach $m (@fields) {
1333 print $f "check\$offset(\"$m->{name}\", $m->{offset});\n";
1335 my $bytes = $s->{size}/8;
1337 if (LAYOUT.byteSize() != $bytes)
1338 throw new AssertionError(String.format("$s->{name}.sizeof = %d != $bytes", LAYOUT.byteSize()), null);
1349 my @values = @{$s->{values}};
1350 my $jtype = $typeSizes{$s->{value_type}};
1351 my $prefix = $definePrefix{$s->{value_type}};
1352 my $suffix = $definePrefix{$s->{value_type}};
1353 print $f "package $package;\n" if $package;
1355 print $f "public interface $s->{name} {\n";
1357 foreach $v (@values) {
1358 print $f " public static final $jtype $v->{name} = $prefix$v->{value}$suffix;\n";
1364 # copies a skeleton file and patches it to the target package
1365 sub copySkeletonFile {
1369 open (my $d, ">", $dst) || die ("Cannot open '$src' for writing");
1370 open (my $s, "<", $src) || die ("Cannot open '$dst' for reading");
1373 s/^package .*;/package $package;/;
1383 $outputPath = $package;
1384 $outputPath =~ s@\.@/@g;
1385 $outputPath = "$output/$outputPath";
1387 make_path($outputPath);
1389 copySkeletonFile("$scriptPath/template/Memory.java", "$outputPath/Memory.java");
1390 copySkeletonFile("$scriptPath/template/Frame.java", "$outputPath/Frame.java");
1394 my $name = shift @_;
1397 $name = "$dir/$name.java";
1402 #print Dumper($api);
1405 foreach $obj ( @{$api->{struct}} ) {
1408 next if $obj->{name} eq '<default>';
1410 if ($obj->{name} =~ m@/(.*)/@) {
1411 my $rx = qr/struct:$1/;
1413 @list = map { s/struct://; $_ } grep { $_ =~ m/$rx/ } keys %data;
1415 push @list, $obj->{name};
1418 foreach $name (@list) {
1419 my $path = nameToPath($output, "$package.$name");
1420 my $s = $data{"struct:$name"};
1422 #print Dumper($obj);
1424 delete $toDump->{"struct:$name"};
1427 open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
1429 exportStruct($f, $s, $obj);
1433 print "No struct $name\n";
1439 foreach $lib ( @{$api->{library}} ) {
1440 my $path = nameToPath($output, "$package.$lib->{name}");
1442 open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
1444 print $f "package $package;\n";
1445 print $f "import jdk.incubator.foreign.*;\n";
1446 print $f "import java.lang.invoke.*;\n";
1448 print $f "public class $lib->{name} {\n";
1450 print $f " static ResourceScope scope() { return ResourceScope.globalScope(); }\n";
1453 foreach $inc (@{$lib->{items}}) {
1454 if ($inc->{mode} eq 'func') {
1455 my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
1456 foreach $c (@list) {
1459 print $f " static final MethodHandle $c->{name}\$FH = Memory.downcall(\"$c->{name}\",\n";
1460 $tmp = formatFunctionDescriptor($c);
1461 print $f $tmp.");\n";
1463 $tmp = formatFunction($c, $inc);
1465 print $f $tmp."\n\n";
1467 } elsif ($inc->{mode} eq 'define') {
1468 my @list = grep { $_->{type} eq $inc->{mode} && $_->{name} =~ m/$inc->{regex}/ } values %data;
1469 foreach $c (@list) {
1470 delete $toDump->{"define:$c->{name}"};
1471 foreach $m (@{$c->{fields}}) {
1472 print $f " /**\n ($m->{comment}) */\n" if ($m->{comment});
1473 print $f " public static final $defineType{$m->{type}} $m->{name} = $definePrefix{$m->{type}}$m->{value}$defineSuffix{$m->{type}};\n";
1484 print "remaining dependent types\n";
1485 foreach $k (sort grep { !m/func:/ } keys %{$toDump}) {
1491 foreach $k (sort keys %{$toDump}) {
1492 next if (!($k =~ m/call:(.+)/));
1496 my $obj = findAPIObject($api, 'call', $name);
1498 my $path = nameToPath($output, "$package.$name");
1500 open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
1502 print $f "package $package;\n";
1503 print $f "import jdk.incubator.foreign.*;\n";
1504 print $f "import java.lang.invoke.*;\n";
1506 print $f formatCallback($c, $obj);
1511 # any struct remaining in toDump (but not in api)
1512 # FIXME: how to lookup obj?
1513 foreach $k (sort keys %{$toDump}) {
1514 if ($k =~ m/struct:(.*)/) {
1517 my $path = nameToPath($output, "$package.$name");
1519 die("missing struct $name") if !$s;
1521 delete $toDump->{$k};
1523 my $obj = findAPIObject($api, 'struct', $name);
1525 open (my $f, ">", $path) || die ("Cannot open '$path' for writing");
1526 exportStruct($f, $s, $obj);
1531 # Dump enum types used by everything and not dumped elsehwere
1532 foreach $k (sort keys %{$toDump}) {
1533 if ($k =~ m/enum:(.*)/) {
1536 my $path = nameToPath($output, "$package.$name");
1538 die("missing enum $name") if !$s;
1540 open(my $f, ">", $path) || die ("Cannot open '$path' for writing");
1548 # Dump define types not dumped elsehwere
1549 foreach $k (sort keys %{$toDump}) {
1550 if ($k =~ m/define:(.*)/) {
1553 my $path = nameToPath($output, "$package.$name");
1555 die("missing define $name") if !$s;
1557 open(my $f, ">", $path) || die ("Cannot open '$path' for writing");
1559 print $f "package $package;\n" if $package;
1560 print $f "public interface $s->{name} {\n";
1562 foreach $m (@{$s->{values}}) {
1563 # some parsing problem here
1564 next if !$m->{value};
1566 print $f " /**\n ($m->{comment}) */\n" if ($m->{comment});
1567 print $f " public static final $defineType{$m->{type}} $m->{name} = $definePrefix{$m->{type}}$m->{value}$defineSuffix{$m->{type}};\n";
1579 sub loadControlFile {
1580 my $path = shift @_;
1584 open (my $d,"<",$path);
1594 } elsif (/^\s*(\S+)\s*(.*)/) {
1595 my @options = split(/\s+/,$2);
1596 push @{$target->{items}}, {
1598 options => \@options
1601 } elsif (/^(\w+)\s+(\S*)\s*(.*)\s+\{/) {
1602 my @options = split(/\s+/,$3);
1607 options => \@options,
1610 push @{$def{$1}}, $target;
1612 die("invalid line: %_");