File Coverage

blib/lib/ExtUtils/XSpp/Node/Function.pm
Criterion Covered Total %
statement 220 230 95.6
branch 87 100 87.0
condition 51 62 82.2
subroutine 33 38 86.8
pod 27 30 90.0
total 418 460 90.8


line stmt bran cond sub pod time code
1             package ExtUtils::XSpp::Node::Function;
2 21     21   8764 use strict;
  21         27  
  21         541  
3 21     21   62 use warnings;
  21         25  
  21         560  
4 21     21   65 use Carp ();
  21         28  
  21         326  
5 21     21   55 use base 'ExtUtils::XSpp::Node';
  21         26  
  21         5132  
6              
7             =head1 NAME
8              
9             ExtUtils::XSpp::Node::Function - Node representing a function
10              
11             =head1 DESCRIPTION
12              
13             An L subclass representing a single function declaration
14             such as
15              
16             int foo();
17              
18             More importantly, L inherits from this class,
19             so all in here equally applies to method nodes.
20              
21             =head1 METHODS
22              
23             =head2 new
24              
25             Creates a new C.
26              
27             Named parameters: C indicating the C++ name of the function,
28             C indicating the Perl name of the function (defaults to the
29             same as C), C can be a reference to an
30             array of C objects and finally
31             C indicates the (C++) return type of the function.
32              
33             Additionally, there are several optional decorators for a function
34             declaration (see L for a list). These can be
35             passed to the constructor as C, C, C,
36             and C. C is special in that it must be a reference
37             to an array of class names.
38              
39             =cut
40              
41             sub init {
42 113     113 1 135 my $this = shift;
43 113         624 my %args = @_;
44              
45 113         341 $this->{CPP_NAME} = $args{cpp_name};
46 113   66     432 $this->{PERL_NAME} = $args{perl_name} || $args{cpp_name};
47 113   100     348 $this->{ARGUMENTS} = $args{arguments} || [];
48 113         195 $this->{RET_TYPE} = $args{ret_type};
49 113         249 $this->{CODE} = $args{code};
50 113         252 $this->{CALL_CODE} = $args{call_code};
51 113         208 $this->{CLEANUP} = $args{cleanup};
52 113         208 $this->{POSTCALL} = $args{postcall};
53 113         212 $this->{CLASS} = $args{class};
54 113         212 $this->{CATCH} = $args{catch};
55 113         224 $this->{CONDITION} = $args{condition};
56 113         171 $this->{ALIAS} = $args{alias};
57 113         223 $this->{TAGS} = $args{tags};
58 113         180 $this->{EMIT_CONDITION} = $args{emit_condition};
59              
60 113         145 my $index = 0;
61 113         138 foreach my $arg ( @{$this->{ARGUMENTS}} ) {
  113         283  
62 123         193 $arg->{FUNCTION} = $this;
63 123         252 $arg->{INDEX} = $index;
64 123         186 ++$index;
65             }
66              
67 113 50 66     210 if (@{$this->catch} > 1
  113         380  
68 7         16 and grep {$_ eq 'nothing'} @{$this->{CATCH}})
  3         6  
69             {
70             Carp::croak( ref($this) . " '" . $this->{CPP_NAME}
71             . "' is supposed to catch no exceptions, yet"
72             . " there are exception handlers ("
73 0         0 . join(", ", @{$this->{CATCH}}) . ")" );
  0         0  
74             }
75 113         382 return $this;
76             }
77              
78             =head2 resolve_typemaps
79              
80             Fetches the L object for
81             the return type and the arguments from the typemap registry
82             and stores a reference to those objects.
83              
84             =cut
85              
86             sub resolve_typemaps {
87 113     113 1 164 my $this = shift;
88 113         137 my $index = 0;
89              
90 113 100       252 if( $this->ret_type ) {
91             $this->{TYPEMAPS}{RET_TYPE} ||=
92 110   66     411 ExtUtils::XSpp::Typemap::get_typemap_for_type( $this->ret_type );
93             }
94 113         162 foreach my $a ( @{$this->arguments} ) {
  113         272  
95 123   66     529 $this->{TYPEMAPS}{ARGUMENTS}[$index++] ||=
96             ExtUtils::XSpp::Typemap::get_typemap_for_type( $a->type );
97             }
98             }
99              
100              
101             =head2 resolve_exceptions
102              
103             Fetches the L object for
104             the C<%catch> directives associated with this function.
105              
106             =cut
107              
108             sub resolve_exceptions {
109 95     95 1 135 my $this = shift;
110              
111 95         118 my @catch = @{$this->catch};
  95         161  
112              
113 95         137 my @exceptions;
114              
115             # If this method is not hard-wired to catch nothing...
116 95 100       245 if (not grep {$_ eq 'nothing'} @catch) {
  21         47  
117 92         118 my %seen;
118 92         145 foreach my $catch (@catch) {
119 18 100       44 next if $seen{$catch}++;
120 17         69 push @exceptions,
121             ExtUtils::XSpp::Exception->get_exception_for_name($catch);
122             }
123              
124             # If nothing else, catch std::exceptions nicely
125 92 100       198 if (not @exceptions) {
126 81         210 my $typenode = ExtUtils::XSpp::Node::Type->new(base => 'std::exception');
127 81         574 push @exceptions,
128             ExtUtils::XSpp::Exception::stdmessage->new( name => 'default',
129             type => $typenode );
130             }
131             }
132              
133             # Always catch the rest with an unspecific error message.
134             # If the method is hard-wired to catch nothing, we lie to the user
135             # for his own safety! (FIXME: debate this)
136 95         416 push @exceptions,
137             ExtUtils::XSpp::Exception::unknown->new( name => '', type => '' );
138              
139 95         237 $this->{EXCEPTIONS} = \@exceptions;
140             }
141              
142             sub disable_exceptions {
143 18     18 0 20 my $this = shift;
144              
145 18         50 $this->{EXCEPTIONS} = [];
146             }
147              
148             =head2 add_exception_handlers
149              
150             Adds a list of exception names to the list of exception handlers.
151             This is mainly called by a class' C method.
152             If the function is hard-wired to have no exception handlers,
153             any extra handlers from the class are ignored.
154              
155             =cut
156              
157              
158             sub add_exception_handlers {
159 51     51 1 76 my $this = shift;
160              
161             # ignore class %catch'es if overridden with "nothing" in the method
162 51 100 100     79 if (@{$this->catch} == 1 and $this->{CATCH}[0] eq 'nothing') {
  51         107  
163 2         4 return();
164             }
165              
166             # ignore class %catch{nothing} if overridden in the method
167 49 100 66     149 if (@_ == 1 and $_[0] eq 'nothing' and @{$this->catch}) {
  2   100     3  
168 1         2 return();
169             }
170              
171 48   50     103 $this->{CATCH} ||= [];
172 48         74 push @{$this->{CATCH}}, @_;
  48         84  
173              
174 48         76 return();
175             }
176              
177             # Depending on argument style, this produces either: (style=kr)
178             #
179             # return_type
180             # class_name::function_name( args = def, ... )
181             # type arg
182             # type arg
183             # PREINIT:
184             # aux vars
185             # [ALIAS:
186             # ID = INTEGER...]
187             # [PP]CODE:
188             # RETVAL = new Foo( THIS->method( arg1, *arg2 ) );
189             # POSTCALL:
190             # /* anything */
191             # OUTPUT:
192             # RETVAL
193             # CLEANUP:
194             # /* anything */
195             #
196             # Or: (style=ansi)
197             #
198             # return_type
199             # class_name::function_name( type arg1 = def, type arg2 = def, ... )
200             # PREINIT:
201             # (rest as above)
202              
203             sub print {
204 109     109 1 156 my $this = shift;
205 109         137 my $state = shift;
206              
207 109         126 my $out = '';
208 109         257 my $fname = $this->perl_function_name;
209 109         217 my $args = $this->arguments;
210 109         213 my $ret_type = $this->ret_type;
211 109         209 my $ret_typemap = $this->{TYPEMAPS}{RET_TYPE};
212 109   100     323 my $aliases = $this->{ALIAS} || {};
213              
214 109         229 my $has_aliases = scalar(keys %$aliases);
215              
216 109 50       272 $out .= '#if ' . $this->emit_condition . "\n" if $this->emit_condition;
217              
218 109         366 my( $init, $arg_list, $call_arg_list, $code, $output, $cleanup,
219             $postcall, $precall, $alias ) = ( ('') x 9 );
220              
221             # compute the precall code, XS argument list and C++ argument list using
222             # the typemap information
223 109 100 66     353 if( $args && @$args ) {
224 85 100       203 my $has_self = $this->is_method ? 1 : 0;
225 85         132 my( @arg_list, @call_arg_list );
226 85         243 foreach my $i ( 0 .. $#$args ) {
227 123         126 my $arg = ${$args}[$i];
  123         212  
228 123         174 my $t = $this->{TYPEMAPS}{ARGUMENTS}[$i];
229 123         473 my $pc = $t->precall_code( sprintf( 'ST(%d)', $i + $has_self ),
230             $arg->name );
231              
232 123 100       301 push @arg_list, $t->cpp_type . ' ' . $arg->name .
233             ( $arg->has_default ? ' = ' . $arg->default : '' );
234              
235 123         250 my $call_code = $t->call_parameter_code( $arg->name );
236 123 100       281 push @call_arg_list, defined( $call_code ) ? $call_code : $arg->name;
237 123 100       294 $precall .= $pc . ";\n" if $pc
238             }
239              
240 85         203 $arg_list = ' ' . join( ', ', @arg_list ) . ' ';
241 85         207 $call_arg_list = ' ' . join( ', ', @call_arg_list ) . ' ';
242             }
243              
244             # If there's %alias{foo = 123} definitions, generate ALIAS section
245 109 100       198 if ($has_aliases) {
246             # order by ordinal for consistent hash-order-independent output
247             my @alias_list = map " $_ = $aliases->{$_}\n",
248 4         20 sort {$aliases->{$a} <=> $aliases->{$b}}
  1         6  
249             keys %$aliases;
250 4         9 $alias = " ALIAS:\n" . join("", @alias_list);
251             }
252              
253 109 100       351 my $retstr = $ret_typemap ? $ret_typemap->cpp_type : 'void';
254              
255             # special case: constructors with name different from 'new'
256             # need to be declared 'static' in XS
257 109 100 100     584 if( $this->isa( 'ExtUtils::XSpp::Node::Constructor' ) &&
258             $this->perl_name ne $this->cpp_name ) {
259 3         14 $retstr = "static $retstr";
260             }
261              
262 109   100     483 my $has_ret = $ret_typemap && !$ret_typemap->type->is_void;
263              
264 109 100 100     429 my $ppcode = $has_ret && $ret_typemap->output_list( '' ) ? 1 : 0;
265 109 100       207 my $code_type = $ppcode ? "PPCODE" : "CODE";
266 109         259 my $ccode = $this->_call_code( $call_arg_list );
267 109 100 100     658 if ($this->{CALL_CODE}) {
    100          
    100          
    100          
    50          
268 18         21 $ccode = join( "\n", @{$this->{CALL_CODE}} );
  18         29  
269             } elsif ($this->isa('ExtUtils::XSpp::Node::Destructor')) {
270 3         5 $ccode = 'delete THIS';
271 3         5 $has_ret = 0;
272             } elsif( $has_ret && defined $ret_typemap->call_function_code( '', '' ) ) {
273 2         6 $ccode = $ret_typemap->call_function_code( $ccode, 'RETVAL' );
274             } elsif( $has_ret ) {
275 65 100       121 if ($has_aliases) {
276 4         35 $ccode = $this->_generate_alias_conditionals($call_arg_list, 1); # 1 == use RETVAL
277             } else {
278 61         113 $ccode = "RETVAL = $ccode";
279             }
280             } elsif( $has_aliases ) { # aliases but no RETVAL
281 0         0 $ccode = $this->_generate_alias_conditionals($call_arg_list, 0); # 0 == no RETVAL
282             }
283              
284 109         143 my @catchers = @{$this->{EXCEPTIONS}};
  109         248  
285 109         195 $code .= " $code_type:\n";
286 109 100       231 $code .= " try {\n" if @catchers;
287 109 100       217 if ($precall) {
288 2         3 $code .= ' ' . $precall;
289             }
290 109 100       259 $code .= (@catchers ? ' ' : '') . ' ' . $ccode . ";\n";
291 109 100 100     268 if( $has_ret && defined $ret_typemap->output_code( '', '' ) ) {
292 1         4 my $retcode = $ret_typemap->output_code( 'ST(0)', 'RETVAL' );
293 1         2 $code .= ' ' . $retcode . ";\n";
294             }
295 109 100 100     358 if( $has_ret && defined $ret_typemap->output_list( '' ) ) {
296 1         4 my $retcode = $ret_typemap->output_list( 'RETVAL' );
297 1         2 $code .= ' ' . $retcode . ";\n";
298             }
299 109 100       193 $code .= " }\n" if @catchers;
300 109         163 foreach my $exception_handler (@catchers) {
301 185         465 my $handler_code = $exception_handler->handler_code;
302 185         363 $code .= $handler_code;
303             }
304              
305 109 100       244 $output = " OUTPUT: RETVAL\n" if $has_ret;
306              
307 109 100 100     370 if( $has_ret && defined $ret_typemap->cleanup_code( '', '' ) ) {
308 2         7 $cleanup .= " CLEANUP:\n";
309 2         7 my $cleanupcode = $ret_typemap->cleanup_code( 'ST(0)', 'RETVAL' );
310 2         6 $cleanup .= ' ' . $cleanupcode . ";\n";
311             }
312              
313 109 100       247 if( $this->code ) {
314 11         20 $code = " $code_type:\n " . join( "\n", @{$this->code} ) . "\n";
  11         18  
315 11 100       48 $output = " OUTPUT: RETVAL\n" if $code =~ m/\bRETVAL\b/;
316             }
317 109 100       250 if( $this->postcall ) {
318 3         5 $postcall = " POSTCALL:\n " . join( "\n", @{$this->postcall} ) . "\n";
  3         6  
319 3 100 50     10 $output ||= " OUTPUT: RETVAL\n" if $has_ret;
320             }
321 109 100       223 if( $this->cleanup ) {
322 3   50     14 $cleanup ||= " CLEANUP:\n";
323 3         4 my $clcode = join( "\n", @{$this->cleanup} );
  3         5  
324 3         5 $cleanup .= " $clcode\n";
325             }
326 109 100       201 if( $ppcode ) {
327 1         2 $output = '';
328             }
329              
330 109 100 100     213 if( !$this->is_method && $fname =~ /^(.*)::(\w+)$/ ) {
331 1         3 my $pcname = $1;
332 1         2 $fname = $2;
333 1         4 my $cur_module = $state->{current_module}->to_string;
334 1         10 $out .= <
335             $cur_module PACKAGE=$pcname
336              
337             EOT
338             }
339              
340 109         266 my $head = "$retstr\n"
341             . "$fname($arg_list)\n";
342 109         231 my $body = $alias . $init . $code . $postcall . $output . $cleanup;
343              
344             # cleanup potential multiple newlines because they break XSUBs
345 109         706 $body =~ s/^\s*\n//mg;
346 109         152 $body .= "\n";
347              
348 109 100       263 $this->_munge_code(\$body) if $this->has_argument_with_length;
349              
350 109         566 $out .= $head . $body;
351 109 50       234 $out .= '#endif // ' . $this->emit_condition . "\n" if $this->emit_condition;
352              
353 109         555 return $out;
354             }
355              
356             # This replaces the use of "length(varname)" with
357             # the proper name of the XS variable that is auto-generated in
358             # case of the XS length() feature. The Argument's take care of
359             # this and do nothing if they're not of the "length" type.
360             # Any additional checking "$this->_munge_code(\$code) if $using_length"
361             # is just an optimization!
362             sub _munge_code {
363 3     3   4 my $this = shift;
364 3         6 my $code = shift;
365            
366 3         5 foreach my $arg (@{$this->{ARGUMENTS}}) {
  3         4  
367 6         25 $$code = $arg->fix_name_in_code($$code);
368             }
369             }
370              
371             =head2 print_declaration
372              
373             Returns a string with a C++ method declaration for the node.
374              
375             =cut
376              
377             sub print_declaration {
378 0     0 1 0 my( $this ) = @_;
379              
380             return $this->ret_type->print . ' ' . $this->cpp_name . '( ' .
381 0 0       0 join( ', ', map $_->print, @{$this->arguments} ) . ')' .
  0         0  
382             ( $this->const ? ' const' : '' );
383             }
384              
385             =head2 perl_function_name
386              
387             Returns the name of the Perl function to generate.
388              
389             =cut
390              
391 42     42 1 113 sub perl_function_name { $_[0]->perl_name }
392              
393             =head2 is_method
394              
395             Returns whether the object at hand is a method. Hard-wired
396             to be false for C object,
397             but overridden in the L sub-class.
398              
399             =cut
400              
401 83     83 1 289 sub is_method { 0 }
402              
403             =head2 has_argument_with_length
404              
405             Returns true if the function has any argument that uses the XS length
406             feature.
407              
408             =cut
409              
410             sub has_argument_with_length {
411 109     109 1 155 my $this = shift;
412 109         129 foreach my $arg (@{$this->{ARGUMENTS}}) {
  109         233  
413 123 100       262 return 1 if $arg->uses_length;
414             }
415 106         211 return();
416             }
417              
418              
419             =begin documentation
420              
421             ExtUtils::XSpp::Function::_call_code( argument_string )
422              
423             Return something like "foo( $argument_string )".
424              
425             =end documentation
426              
427             =cut
428              
429 45     45   151 sub _call_code { return $_[0]->cpp_name . '(' . $_[1] . ')'; }
430              
431             =begin documentation
432              
433             ExtUtils::XSpp::Function::_call_code_aliased( function_alias_name, argument_string )
434              
435             Return something like "$function_alias_name( $argument_string )".
436              
437             =end documentation
438              
439             =cut
440              
441 4     4   11 sub _call_code_aliased { return $_[1] . '(' . $_[2] . ')'; }
442              
443             =begin documentation
444              
445             ExtUtils::XSpp::Function::_generate_alias_conditionals( argument_string, use_retval_bool )
446              
447             Generates if()else if()else block for XS function name aliasing (cf. the XS manual and the ix
448             variable). If use_retval_bool is true, each included function call will contain an
449             assignment to RETVAL.
450              
451             Returns the generated code.
452              
453             =end documentation
454              
455             =cut
456             sub _generate_alias_conditionals {
457 4     4   8 my ($this, $call_arg_list, $use_retval) = @_;
458 4         7 my $aliases = $this->{ALIAS};
459              
460 4 50       8 my $retval_code = $use_retval ? "RETVAL = " : "";
461 4         9 my $buf = "if (ix == 0) {\n $retval_code"
462             . $this->_call_code($call_arg_list)
463             . ";\n}\n";
464             # order by ordinal for consistent hash-order-independent output
465 4         11 foreach my $alias (sort {$aliases->{$a} <=> $aliases->{$b}} keys %$aliases)
  1         3  
466             {
467 5         25 $buf .= "else if (ix == $aliases->{$alias}) {\n "
468             . $retval_code . $this->_call_code_aliased($alias, $call_arg_list)
469             . ";\n}\n";
470             }
471 4         7 $buf .= "else\n croak(\"Panic: Invalid invocation of function alias number %i!\", (int)ix))";
472              
473             # indent
474 4         33 $buf =~ s/^/ /gm;
475 4         11 $buf =~ s/^\s+//; # first line will get special treatment...
476              
477 4         8 return $buf;
478             }
479              
480              
481             =head1 ACCESSORS
482              
483             =head2 cpp_name
484              
485             Returns the C++ name of the function.
486              
487             =head2 perl_name
488              
489             Returns the Perl name of the function (defaults to same as C++).
490              
491             =head2 set_perl_name
492              
493             Sets the Perl name of the function.
494              
495             =head2 arguments
496              
497             Returns the internal array reference of L
498             objects that represent the function arguments.
499              
500             =head2 ret_type
501              
502             Returns the C++ return type.
503              
504             =head2 code
505              
506             Returns the C<%code> decorator if any.
507              
508             =head2 set_code
509              
510             Sets the implementation for the method call (equivalent to using
511             C<%code>); takes the code as an array reference containing the lines.
512              
513             =head2 cleanup
514              
515             Returns the C<%cleanup> decorator if any.
516              
517             =head2 postcall
518              
519             Returns the C<%postcall> decorator if any.
520              
521             =head2 catch
522              
523             Returns the set of exception types that were associated
524             with the function via C<%catch>. (array reference)
525              
526             =head2 aliases
527              
528             Returns a hashref of C position>
529             function name aliases (see %alias and L ALIAS keyword).
530             Does not include the main function name.
531              
532             =cut
533              
534 129     129 1 427 sub cpp_name { $_[0]->{CPP_NAME} }
535 0     0 0 0 sub set_cpp_name { $_[0]->{CPP_NAME} = $_[1] }
536 114     114 1 291 sub perl_name { $_[0]->{PERL_NAME} }
537 10     10 1 43 sub set_perl_name { $_[0]->{PERL_NAME} = $_[1] }
538 317     317 1 570 sub arguments { $_[0]->{ARGUMENTS} }
539 305     305 1 600 sub ret_type { $_[0]->{RET_TYPE} }
540 120     120 1 291 sub code { $_[0]->{CODE} }
541 0     0 1 0 sub set_code { $_[0]->{CODE} = $_[1] }
542 112     112 1 212 sub cleanup { $_[0]->{CLEANUP} }
543 112     112 1 218 sub postcall { $_[0]->{POSTCALL} }
544 261 100   261 1 882 sub catch { $_[0]->{CATCH} ? $_[0]->{CATCH} : [] }
545 0 0   0 1 0 sub aliases { $_[0]->{ALIAS} ? $_[0]->{ALIAS} : {} }
546 95     95 0 261 sub tags { $_[0]->{TAGS} }
547              
548             =head2 set_static
549              
550             Sets the C-ness attribute of the function.
551             Can be either undef (i.e. not static), C<"package_static">,
552             or C<"class_static">.
553              
554             =head2 package_static
555              
556             Returns whether the function is package static. A package static
557             function can be invoked as:
558              
559             My::Package::Function( ... );
560              
561             =head2 class_static
562              
563             Returns whether the function is class static. A class static function
564             can be invoked as:
565              
566             My::Package->Function( ... );
567              
568             =cut
569              
570 1     1 1 3 sub set_static { $_[0]->{STATIC} = $_[1] }
571 125   100 125 1 427 sub package_static { ( $_[0]->{STATIC} || '' ) eq 'package_static' }
572 0   0 0 1 0 sub class_static { ( $_[0]->{STATIC} || '' ) eq 'class_static' }
573              
574             =head2 ret_typemap
575              
576             Returns the typemap for the return value of the function.
577              
578             =head2 set_ret_typemap( typemap )
579              
580             Sets the typemap for the return value of the function.
581              
582             =head2 arg_typemap( index )
583              
584             Returns the typemap for one function arguments.
585              
586             =head2 set_arg_typemap( index, typemap )
587              
588             Sets the typemap for one function argument.
589              
590             =cut
591              
592             sub ret_typemap {
593 1     1 1 9 my ($this) = @_;
594              
595 1 50       3 die "Typemap not available yet" unless $this->{TYPEMAPS}{RET_TYPE};
596 1         3 return $this->{TYPEMAPS}{RET_TYPE};
597             }
598              
599             sub set_ret_typemap {
600 10     10 1 20 my ($this, $typemap) = @_;
601              
602 10         18 $this->{TYPEMAPS}{RET_TYPE} = $typemap;
603             }
604              
605             sub arg_typemap {
606 1     1 1 2 my ($this, $index) = @_;
607              
608 1 50       2 die "Invalid index" unless $index < @{$this->{ARGUMENTS}};
  1         3  
609 1 50       3 die "Typemap not available yet" unless $this->{TYPEMAPS}{ARGUMENTS};
610 1         3 return $this->{TYPEMAPS}{ARGUMENTS}[$index];
611             }
612              
613             sub set_arg_typemap {
614 10     10 1 18 my ($this, $index, $typemap) = @_;
615              
616 10 50       13 die "Invalid index" unless $index < @{$this->{ARGUMENTS}};
  10         16  
617 10         28 $this->{TYPEMAPS}{ARGUMENTS}[$index] = $typemap;
618             }
619              
620             1;