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   11519 use strict;
  21         42  
  21         778  
3 21     21   110 use warnings;
  21         33  
  21         473  
4 21     21   113 use Carp ();
  21         37  
  21         369  
5 21     21   96 use base 'ExtUtils::XSpp::Node';
  21         37  
  21         11180  
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 210 my $this = shift;
43 113         1156 my %args = @_;
44              
45 113         615 $this->{CPP_NAME} = $args{cpp_name};
46 113   66     719 $this->{PERL_NAME} = $args{perl_name} || $args{cpp_name};
47 113   100     546 $this->{ARGUMENTS} = $args{arguments} || [];
48 113         290 $this->{RET_TYPE} = $args{ret_type};
49 113         292 $this->{CODE} = $args{code};
50 113         251 $this->{CALL_CODE} = $args{call_code};
51 113         247 $this->{CLEANUP} = $args{cleanup};
52 113         1224 $this->{POSTCALL} = $args{postcall};
53 113         293 $this->{CLASS} = $args{class};
54 113         283 $this->{CATCH} = $args{catch};
55 113         267 $this->{CONDITION} = $args{condition};
56 113         297 $this->{ALIAS} = $args{alias};
57 113         229 $this->{TAGS} = $args{tags};
58 113         258 $this->{EMIT_CONDITION} = $args{emit_condition};
59              
60 113         171 my $index = 0;
61 113         168 foreach my $arg ( @{$this->{ARGUMENTS}} ) {
  113         340  
62 123         249 $arg->{FUNCTION} = $this;
63 123         223 $arg->{INDEX} = $index;
64 123         268 ++$index;
65             }
66              
67 113 50 66     208 if (@{$this->catch} > 1
  113         622  
  7         20  
68 3         22 and grep {$_ eq 'nothing'} @{$this->{CATCH}})
69             {
70 0         0 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}}) . ")" );
74             }
75 113         618 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 184 my $this = shift;
88 113         190 my $index = 0;
89              
90 113 100       400 if( $this->ret_type ) {
91 110   66     644 $this->{TYPEMAPS}{RET_TYPE} ||=
92             ExtUtils::XSpp::Typemap::get_typemap_for_type( $this->ret_type );
93             }
94 113         314 foreach my $a ( @{$this->arguments} ) {
  113         451  
95 123   66     1221 $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 183 my $this = shift;
110              
111 95         166 my @catch = @{$this->catch};
  95         257  
112              
113 95         157 my @exceptions;
114              
115             # If this method is not hard-wired to catch nothing...
116 95 100       328 if (not grep {$_ eq 'nothing'} @catch) {
  21         72  
117 92         152 my %seen;
118 92         242 foreach my $catch (@catch) {
119 18 100       56 next if $seen{$catch}++;
120 17         280 push @exceptions,
121             ExtUtils::XSpp::Exception->get_exception_for_name($catch);
122             }
123              
124             # If nothing else, catch std::exceptions nicely
125 92 100       266 if (not @exceptions) {
126 81         393 my $typenode = ExtUtils::XSpp::Node::Type->new(base => 'std::exception');
127 81         950 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         885 push @exceptions,
137             ExtUtils::XSpp::Exception::unknown->new( name => '', type => '' );
138              
139 95         501 $this->{EXCEPTIONS} = \@exceptions;
140             }
141              
142             sub disable_exceptions {
143 18     18 0 22 my $this = shift;
144              
145 18         53 $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 113 my $this = shift;
160              
161             # ignore class %catch'es if overridden with "nothing" in the method
162 51 100 100     134 if (@{$this->catch} == 1 and $this->{CATCH}[0] eq 'nothing') {
  51         153  
163 2         6 return();
164             }
165              
166             # ignore class %catch{nothing} if overridden in the method
167 49 100 66     220 if (@_ == 1 and $_[0] eq 'nothing' and @{$this->catch}) {
  2   100     6  
168 1         3 return();
169             }
170              
171 48   50     192 $this->{CATCH} ||= [];
172 48         74 push @{$this->{CATCH}}, @_;
  48         101  
173              
174 48         105 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 202 my $this = shift;
205 109         271 my $state = shift;
206              
207 109         180 my $out = '';
208 109         397 my $fname = $this->perl_function_name;
209 109         544 my $args = $this->arguments;
210 109         275 my $ret_type = $this->ret_type;
211 109         280 my $ret_typemap = $this->{TYPEMAPS}{RET_TYPE};
212 109   100     420 my $aliases = $this->{ALIAS} || {};
213              
214 109         275 my $has_aliases = scalar(keys %$aliases);
215              
216 109 50       499 $out .= '#if ' . $this->emit_condition . "\n" if $this->emit_condition;
217              
218 109         440 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     653 if( $args && @$args ) {
224 85 100       328 my $has_self = $this->is_method ? 1 : 0;
225 85         309 my( @arg_list, @call_arg_list );
226 85         290 foreach my $i ( 0 .. $#$args ) {
227 123         170 my $arg = ${$args}[$i];
  123         429  
228 123         502 my $t = $this->{TYPEMAPS}{ARGUMENTS}[$i];
229 123         827 my $pc = $t->precall_code( sprintf( 'ST(%d)', $i + $has_self ),
230             $arg->name );
231              
232 123 100       572 push @arg_list, $t->cpp_type . ' ' . $arg->name .
233             ( $arg->has_default ? ' = ' . $arg->default : '' );
234              
235 123         498 my $call_code = $t->call_parameter_code( $arg->name );
236 123 100       572 push @call_arg_list, defined( $call_code ) ? $call_code : $arg->name;
237 123 100       573 $precall .= $pc . ";\n" if $pc
238             }
239              
240 85         281 $arg_list = ' ' . join( ', ', @arg_list ) . ' ';
241 85         298 $call_arg_list = ' ' . join( ', ', @call_arg_list ) . ' ';
242             }
243              
244             # If there's %alias{foo = 123} definitions, generate ALIAS section
245 109 100       279 if ($has_aliases) {
246             # order by ordinal for consistent hash-order-independent output
247 1         9 my @alias_list = map " $_ = $aliases->{$_}\n",
248 4         29 sort {$aliases->{$a} <=> $aliases->{$b}}
249             keys %$aliases;
250 4         12 $alias = " ALIAS:\n" . join("", @alias_list);
251             }
252              
253 109 100       582 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     1123 if( $this->isa( 'ExtUtils::XSpp::Node::Constructor' ) &&
258             $this->perl_name ne $this->cpp_name ) {
259 3         68 $retstr = "static $retstr";
260             }
261              
262 109   100     1286 my $has_ret = $ret_typemap && !$ret_typemap->type->is_void;
263              
264 109 100 100     881 my $ppcode = $has_ret && $ret_typemap->output_list( '' ) ? 1 : 0;
265 109 100       407 my $code_type = $ppcode ? "PPCODE" : "CODE";
266 109         402 my $ccode = $this->_call_code( $call_arg_list );
267 109 100 100     1309 if ($this->{CALL_CODE}) {
    100          
    100          
    100          
    50          
268 18         20 $ccode = join( "\n", @{$this->{CALL_CODE}} );
  18         49  
269             } elsif ($this->isa('ExtUtils::XSpp::Node::Destructor')) {
270 3         10 $ccode = 'delete THIS';
271 3         6 $has_ret = 0;
272             } elsif( $has_ret && defined $ret_typemap->call_function_code( '', '' ) ) {
273 2         9 $ccode = $ret_typemap->call_function_code( $ccode, 'RETVAL' );
274             } elsif( $has_ret ) {
275 65 100       145 if ($has_aliases) {
276 4         23 $ccode = $this->_generate_alias_conditionals($call_arg_list, 1); # 1 == use RETVAL
277             } else {
278 61         180 $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         180 my @catchers = @{$this->{EXCEPTIONS}};
  109         339  
285 109         273 $code .= " $code_type:\n";
286 109 100       302 $code .= " try {\n" if @catchers;
287 109 100       281 if ($precall) {
288 2         8 $code .= ' ' . $precall;
289             }
290 109 100       421 $code .= (@catchers ? ' ' : '') . ' ' . $ccode . ";\n";
291 109 100 100     527 if( $has_ret && defined $ret_typemap->output_code( '', '' ) ) {
292 1         7 my $retcode = $ret_typemap->output_code( 'ST(0)', 'RETVAL' );
293 1         4 $code .= ' ' . $retcode . ";\n";
294             }
295 109 100 100     484 if( $has_ret && defined $ret_typemap->output_list( '' ) ) {
296 1         4 my $retcode = $ret_typemap->output_list( 'RETVAL' );
297 1         3 $code .= ' ' . $retcode . ";\n";
298             }
299 109 100       342 $code .= " }\n" if @catchers;
300 109         220 foreach my $exception_handler (@catchers) {
301 185         678 my $handler_code = $exception_handler->handler_code;
302 185         526 $code .= $handler_code;
303             }
304              
305 109 100       332 $output = " OUTPUT: RETVAL\n" if $has_ret;
306              
307 109 100 100     617 if( $has_ret && defined $ret_typemap->cleanup_code( '', '' ) ) {
308 2         20 $cleanup .= " CLEANUP:\n";
309 2         10 my $cleanupcode = $ret_typemap->cleanup_code( 'ST(0)', 'RETVAL' );
310 2         12 $cleanup .= ' ' . $cleanupcode . ";\n";
311             }
312              
313 109 100       432 if( $this->code ) {
314 11         28 $code = " $code_type:\n " . join( "\n", @{$this->code} ) . "\n";
  11         24  
315 11 100       75 $output = " OUTPUT: RETVAL\n" if $code =~ m/\bRETVAL\b/;
316             }
317 109 100       414 if( $this->postcall ) {
318 3         7 $postcall = " POSTCALL:\n " . join( "\n", @{$this->postcall} ) . "\n";
  3         11  
319 3 100 50     17 $output ||= " OUTPUT: RETVAL\n" if $has_ret;
320             }
321 109 100       393 if( $this->cleanup ) {
322 3   50     22 $cleanup ||= " CLEANUP:\n";
323 3         5 my $clcode = join( "\n", @{$this->cleanup} );
  3         7  
324 3         14 $cleanup .= " $clcode\n";
325             }
326 109 100       306 if( $ppcode ) {
327 1         2 $output = '';
328             }
329              
330 109 100 100     327 if( !$this->is_method && $fname =~ /^(.*)::(\w+)$/ ) {
331 1         4 my $pcname = $1;
332 1         3 $fname = $2;
333 1         5 my $cur_module = $state->{current_module}->to_string;
334 1         5 $out .= <
335             $cur_module PACKAGE=$pcname
336              
337             EOT
338             }
339              
340 109         485 my $head = "$retstr\n"
341             . "$fname($arg_list)\n";
342 109         467 my $body = $alias . $init . $code . $postcall . $output . $cleanup;
343              
344             # cleanup potential multiple newlines because they break XSUBs
345 109         1055 $body =~ s/^\s*\n//mg;
346 109         172 $body .= "\n";
347              
348 109 100       407 $this->_munge_code(\$body) if $this->has_argument_with_length;
349              
350 109         512 $out .= $head . $body;
351 109 50       327 $out .= '#endif // ' . $this->emit_condition . "\n" if $this->emit_condition;
352              
353 109         811 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   7 my $this = shift;
364 3         6 my $code = shift;
365            
366 3         6 foreach my $arg (@{$this->{ARGUMENTS}}) {
  3         11  
367 6         22 $$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 0         0 return $this->ret_type->print . ' ' . $this->cpp_name . '( ' .
381 0 0       0 join( ', ', map $_->print, @{$this->arguments} ) . ')' .
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 201 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 418 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 181 my $this = shift;
412 109         360 foreach my $arg (@{$this->{ARGUMENTS}}) {
  109         360  
413 123 100       412 return 1 if $arg->uses_length;
414             }
415 106         340 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   169 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   18 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   7 my ($this, $call_arg_list, $use_retval) = @_;
458 4         7 my $aliases = $this->{ALIAS};
459              
460 4 50       9 my $retval_code = $use_retval ? "RETVAL = " : "";
461 4         12 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         4  
466             {
467 5         19 $buf .= "else if (ix == $aliases->{$alias}) {\n "
468             . $retval_code . $this->_call_code_aliased($alias, $call_arg_list)
469             . ";\n}\n";
470             }
471 4         8 $buf .= "else\n croak(\"Panic: Invalid invocation of function alias number %i!\", (int)ix))";
472              
473             # indent
474 4         38 $buf =~ s/^/ /gm;
475 4         16 $buf =~ s/^\s+//; # first line will get special treatment...
476              
477 4         9 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 811 sub cpp_name { $_[0]->{CPP_NAME} }
535 0     0 0 0 sub set_cpp_name { $_[0]->{CPP_NAME} = $_[1] }
536 114     114 1 554 sub perl_name { $_[0]->{PERL_NAME} }
537 10     10 1 68 sub set_perl_name { $_[0]->{PERL_NAME} = $_[1] }
538 317     317 1 943 sub arguments { $_[0]->{ARGUMENTS} }
539 305     305 1 1506 sub ret_type { $_[0]->{RET_TYPE} }
540 120     120 1 457 sub code { $_[0]->{CODE} }
541 0     0 1 0 sub set_code { $_[0]->{CODE} = $_[1] }
542 112     112 1 435 sub cleanup { $_[0]->{CLEANUP} }
543 112     112 1 388 sub postcall { $_[0]->{POSTCALL} }
544 261 100   261 1 1531 sub catch { $_[0]->{CATCH} ? $_[0]->{CATCH} : [] }
545 0 0   0 1 0 sub aliases { $_[0]->{ALIAS} ? $_[0]->{ALIAS} : {} }
546 95     95 0 550 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 4 sub set_static { $_[0]->{STATIC} = $_[1] }
571 125   100 125 1 848 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 15 my ($this) = @_;
594              
595 1 50       6 die "Typemap not available yet" unless $this->{TYPEMAPS}{RET_TYPE};
596 1         6 return $this->{TYPEMAPS}{RET_TYPE};
597             }
598              
599             sub set_ret_typemap {
600 10     10 1 20 my ($this, $typemap) = @_;
601              
602 10         37 $this->{TYPEMAPS}{RET_TYPE} = $typemap;
603             }
604              
605             sub arg_typemap {
606 1     1 1 2 my ($this, $index) = @_;
607              
608 1 50       3 die "Invalid index" unless $index < @{$this->{ARGUMENTS}};
  1         6  
609 1 50       7 die "Typemap not available yet" unless $this->{TYPEMAPS}{ARGUMENTS};
610 1         6 return $this->{TYPEMAPS}{ARGUMENTS}[$index];
611             }
612              
613             sub set_arg_typemap {
614 10     10 1 23 my ($this, $index, $typemap) = @_;
615              
616 10 50       24 die "Invalid index" unless $index < @{$this->{ARGUMENTS}};
  10         33  
617 10         50 $this->{TYPEMAPS}{ARGUMENTS}[$index] = $typemap;
618             }
619              
620             1;