Partial move to new generator.
[panamaz] / src / method.pm
1
2 package method;
3
4 use strict;
5
6 require code;
7
8 sub fieldScope {
9         my $m = shift;
10
11         if ($m->{scope} =~ m/^explicit/) {
12                 'scope$';
13         } elsif ($m->{scope} eq 'instance') {
14                 'scope()';
15         } else {
16                 'ResourceScope.globalScope()';
17         }
18 }
19 sub fieldScopeAction {
20         my $m = shift;
21
22         if ($m->{field}->{scope} =~ m/^explicit,(.*)$/) {
23                 return code::formatTemplate("scope\$.addCloseAction(() -> $1", { %{$m->{match}}, value => 'res$' });
24         } elsif ($m->{field}->{scope} =~ m/^explicit$/) {
25                 'scope$.addCloseAction(() -> res$.close());';
26         } else {
27                 ();
28         }
29 }
30 sub apply {
31         my $t = shift;
32         my $m = shift;
33         return code::formatTemplate($t, $m->{match});
34 }
35
36 sub new {
37         my $class = shift;
38         my $api = shift;
39         my $c = shift;
40         my @members = code::scanFields($api, $c);
41         my $result = shift @members;
42
43         my $self = {
44                 result => $result,
45                 arguments => \@members,
46                 vars => \%{$api->{vars}},
47                 method => $c,
48         };
49         my $info = $self->{vars};
50
51         $info->{name} = $c->{name};
52         $info->{rename} = $c->{rename};
53
54         my @list =  map { apply('{type} {name}', $_) } grep { $_->{field}->{output} } @members;
55         push @list, 'ResourceScope scope$' if ($c->{scope} =~ m/explicit/);
56         $info->{'java-arguments'} = join ', ', @list;
57         $info->{'native-arguments'} = join ', ', map { apply('{carrier} {name}', $_) } @members;
58
59         # for native downcalls
60         $info->{'native-result-define'} = ($result->{match}->{type} ne 'void') ? apply('{carrier} result$;', $result)    : '';
61         $info->{'native-result-assign'} = ($result->{match}->{type} ne 'void') ? apply('result$ = ({carrier})', $result) : '';
62         $info->{'native-call'} = join ', ', map {
63                 my $m = $_->{field};
64
65                 if ($m->{instance}) {
66                         "(jdk.incubator.foreign.Addressable)address()";
67                 } else {
68                         my $name = $_->{match}->{name};
69
70                         if ($m->{output} == 0 && ($m->{deref} =~ m/^u64:/)) {
71                                 $name .= '$h';
72                         } elsif ($m->{output} == 0 && $m->{implied}) {
73                                 $name = $m->{implied};
74                         } elsif (defined($m->{'array-size-source'})) {
75                                 # or some function?
76                                 $name = "Memory.size($m->{'array-size-source'}->{name})";
77                         }
78                         code::formatTemplate("{tonative}", { %{$_->{match}}, value=>$name })
79                 }
80         } @members;
81
82         # for java upcalls
83         $info->{'java-call'} = join ', ', map { code::formatTemplate('{tojava}', { %{$_->{match}}, value=>'{name}' }) } grep { $_->{field}->{output} } @members;
84
85         $info->{'java-signature'} = code::formatFunctionSignature($api, [$result, @members]);
86         $info->{'function-descriptor'} = code::formatFunctionDescriptor($api, [$result, @members]);
87
88         # hidden output arguments
89         # TODO: what about in/out arguments?  they just fall out by not specifying them as scoped?
90         my @output = grep {     $_->{field}->{output} == 0 && ($_->{field}->{deref} =~ m/^u64:/) && $_->{field}->{instance} == 0} @members;
91
92         $info->{'native-output-define'} = join "\n\t\t",        map { apply('{carrieri} {name};', $_)                                                           } @output;
93         $info->{'native-output-init'} = join ";\n\t\t\t",       map { apply('{type} {name}$h = {type}.createArray(1, frame$);', $_)     } @output;
94         $info->{'native-output-copy'} = join ";\n\t\t\t",       map { apply('{name} = {name}$h.get(0);', $_)                                            } @output;
95
96         # also required for some tonative types?
97         my $x =  grep { $_->{match}->{type} eq 'String' } @members;
98         $info->{'create-frame'} = ($#output >= 0 || grep { $_->{match}->{type} eq 'String' } @members) ? '(Frame frame$ = Memory.createFrame()) ' : '';
99
100         # result code handling
101         if ($c->{success}) {
102                 $info->{'result-code'} = $c->{success}->{name};
103
104                 # success test
105                 if ($c->{success}->{success} eq '!null') {
106                         $info->{'result-test'} = "if ($c->{success}->{name} != MemoryAddress.NULL) ";
107                         $info->{'result-throw'} = 'throw new NullPointerException();';
108                 } else {
109                         $info->{'result-test'} = 'if ('.join('||', map { "$c->{success}->{name} == $_" } split(/,/,$c->{success}->{success})).') ';
110                         $info->{'result-throw'} = 'throw new RuntimeException("error="+'.$c->{success}->{name}.');';
111                 }
112         } else {
113                 $info->{'result-test'} = '';
114                 $info->{'result-throw'} = '';
115         }
116
117         # success actions
118         my @onsuccess = ();
119
120         push @onsuccess, code::findCode($api, $c->{onsuccess}) if defined($c->{onsuccess});
121
122         if (defined($c->{return})) {
123                 # nb: this is only used for output parameters
124                 my $res = (grep { $_->{field} == $c->{return} } $result, @members)[0];
125
126                 $info->{'java-result'} = $res->{match}->{typei};
127                 push @onsuccess, fieldScopeAction($res);
128                 #push @onsuccess, code::formatTemplate('return {tojavai};', { %{$res->{match}}, value => $res->{field}->{name}, scope=>fieldScope($res->{field}) });
129
130                 $info->{'java-result-assign'} = code::formatTemplate('{typei} res$ = {tojavai};', { %{$res->{match}}, value => $res->{field}->{name}, scope=>fieldScope($res->{field}) });
131                 $info->{'java-result-return'} = 'return res$;';
132                 $info->{'trampoline-result-define'} = 'error';
133                 $info->{'trampoline-result-return'} = 'error';
134         } elsif ($result->{field}->{output} && $result->{match}->{type} ne 'void') {
135                 $info->{'java-result'} = $result->{match}->{type};
136                 push @onsuccess, fieldScopeAction($result);
137
138                 $info->{'java-result-assign'} = code::formatTemplate('{type} res$ = {tojava};', { %{$result->{match}}, value => 'result$', scope=>fieldScope($result->{field}) });
139                 $info->{'java-result-return'} = 'return res$;';
140
141                 $info->{'trampoline-result-define'} = apply('{type} res$ = ', $result);
142                 $info->{'trampoline-result-return'} = code::formatTemplate('return {tonative};', { %{$result->{match}}, value => 'res$' });
143         } else {
144                 $info->{'java-result'} = 'void';
145                 $info->{'java-result-assign'} = '';
146                 $info->{'java-result-return'} = 'return;';
147                 $info->{'trampoline-result-define'} = '';
148                 $info->{'trampoline-result-return'} = '';
149         }
150         $info->{'on-success'} = join("\n\t\t\t\t", @onsuccess);
151
152         $info->{'static'} = $c->{static} ? 'static ' : '';
153
154         bless $self, $class;
155         $self;
156 }