File Coverage

blib/lib/XS/Install/FrozenShit/ParseXS/Node.pm
Criterion Covered Total %
statement 149 277 53.7
branch 57 162 35.1
condition 10 66 15.1
subroutine 13 15 86.6
pod 0 1 0.0
total 229 521 43.9


line stmt bran cond sub pod time code
1             package
2             XS::Install::FrozenShit::ParseXS::Node;
3 1     1   7 use strict;
  1         2  
  1         41  
4 1     1   6 use warnings;
  1         1  
  1         158  
5              
6             our $VERSION = '3.57';
7              
8             =head1 NAME
9              
10             XS::Install::FrozenShit::ParseXS::Node - Classes for nodes of an XS::Install::FrozenShit::ParseXS AST
11              
12             =head1 SYNOPSIS
13              
14             XXX TBC
15              
16             =head1 DESCRIPTION
17              
18             XXX Sept 2024: this is Work In Progress. This API is currently private and
19             subject to change. Most of ParseXS doesn't use an AST, and instead
20             maintains just enough state to emit code as it parses. This module
21             represents the start of an effort to make it use an AST instead.
22              
23             An C class, and its various subclasses, hold the
24             state for the nodes of an Abstract Syntax Tree (AST), which represents the
25             parsed state of an XS file.
26              
27             Each node is basically a hash of fields. Which field names are legal
28             varies by the node type. The hash keys and values can be accessed
29             directly: there are no getter/setter methods.
30              
31             =cut
32              
33              
34             # ======================================================================
35              
36             package
37             XS::Install::FrozenShit::ParseXS::Node;
38              
39             # Base class for all the other node types.
40             #
41             # The 'use fields' enables compile-time or run-time errors if code
42             # attempts to use a key which isn't listed here.
43              
44             my $USING_FIELDS;
45              
46             BEGIN {
47 1     1   5 our @FIELDS = (
48             # Currently there are no node fields common to all node types
49             );
50              
51             # do 'use fields', except: fields needs Hash::Util which is XS, which
52             # needs us. So only 'use fields' on systems where Hash::Util has already
53             # been built.
54 1 50       91 if (eval 'require Hash::Util; 1;') {
55 1         5 require fields;
56 1         2 $USING_FIELDS = 1;
57 1         6 fields->import(@FIELDS);
58             }
59             }
60              
61              
62             # new(): takes one optional arg, $args, which is a hash ref of key/value
63             # pairs to initialise the object with.
64              
65             sub new {
66 20     20 0 55 my ($class, $args) = @_;
67 20 100       105 $args = {} unless defined $args;
68              
69 20         39 my XS::Install::FrozenShit::ParseXS::Node $self;
70 20 50       47 if ($USING_FIELDS) {
71 20         93 $self = fields::new($class);
72 20         3974 %$self = %$args;
73             }
74             else {
75 0         0 $self = bless { %$args } => $class;
76             }
77 20         84 return $self;
78             }
79              
80              
81             # ======================================================================
82              
83             package
84             XS::Install::FrozenShit::ParseXS::Node::Param;
85              
86             # Node subclass which holds the state of one XSUB parameter, based on the
87             # XSUB's signature and/or an INPUT line.
88              
89             BEGIN {
90 1     1   363 our @ISA = qw(XS::Install::FrozenShit::ParseXS::Node);
91              
92 1         6 our @FIELDS = (
93             @XS::Install::FrozenShit::ParseXS::Node::FIELDS,
94              
95             # values derived from the XSUB's signature
96             'in_out', # The IN/OUT/OUTLIST etc value (if any)
97             'var', # the name of the parameter
98             'arg_num', # The arg number (starting at 1) mapped to this param
99             'default', # default value (if any)
100             'default_usage', # how to report default value in "usage:..." error
101             'is_ansi', # param's type was specified in signature
102             'is_length', # param is declared as 'length(foo)' in signature
103             'len_name' , # the 'foo' in 'length(foo)' in signature
104             'is_synthetic',# var like 'THIS' - we pretend it was in the sig
105              
106             # values derived from both the XSUB's signature and/or INPUT line
107             'type', # The C type of the parameter
108             'no_init', # don't initialise the parameter
109              
110             # values derived from the XSUB's INPUT line
111             'init_op', # initialisation type: one of =/+/;
112             'init', # initialisation template code
113             'is_addr', # INPUT var declared as '&foo'
114             'is_alien', # var declared in INPUT line, but not in signature
115             'in_input', # the parameter has appeared in an INPUT statement
116              
117             # values derived from the XSUB's OUTPUT line
118             'in_output', # the parameter has appeared in an OUTPUT statement
119             'do_setmagic', # 'SETMAGIC: ENABLE' was active for this parameter
120             'output_code', # the optional setting-code for this parameter
121              
122             # derived values calculated later
123             'defer', # deferred initialisation template code
124             'proto', # overridden prototype char(s) (if any) from typemap
125             );
126              
127 1 50       28 fields->import(@FIELDS) if $USING_FIELDS;
128             }
129              
130              
131              
132             # check(): for a parsed INPUT line and/or typed parameter in a signature,
133             # update some global state and do some checks
134             #
135             # Return true if checks pass.
136              
137             sub check {
138 8     8   17 my XS::Install::FrozenShit::ParseXS::Node::Param $self = shift;
139 8         16 my XS::Install::FrozenShit::ParseXS $pxs = shift;
140            
141 8         22 my $type = $self->{type};
142              
143             # Get the overridden prototype character, if any, associated with the
144             # typemap entry for this var's type.
145             # Note that something with a provisional type such as THIS can get
146             # the type changed later. It is important to update each time.
147             # It also can't be looked up only at BOOT code emitting time, because
148             # potentiall, the typmap may been bee updated last in the XS file
149             # after the XSUB was parsed.
150 8 100       23 if ($self->{arg_num}) {
151 6         25 my $typemap = $pxs->{typemaps_object}->get_typemap(ctype => $type);
152 6   33     41 my $p = $typemap && $typemap->proto;
153 6 50 33     30 $self->{proto} = $p if defined $p && length $p;
154             }
155            
156 8         29 return 1;
157             }
158              
159              
160             # $self->as_code():
161             #
162             # Emit the param object as C code which declares and initialise the variable.
163             # See also the as_output_code() method, which emits code to return the value
164             # of that local var.
165              
166             sub as_code {
167             my XS::Install::FrozenShit::ParseXS::Node::Param $self = shift;
168             my XS::Install::FrozenShit::ParseXS $pxs = shift;
169            
170             my ($type, $arg_num, $var, $init, $no_init, $defer, $default)
171             = @{$self}{qw(type arg_num var init no_init defer default)};
172            
173             my $arg = $pxs->ST($arg_num);
174            
175             if ($self->{is_length}) {
176             # Process length(foo) parameter.
177             # Basically for something like foo(char *s, int length(s)),
178             # create *two* local C vars: one with STRLEN type, and one with the
179             # type specified in the signature. Eventually, generate code looking
180             # something like:
181             # STRLEN STRLEN_length_of_s;
182             # int XSauto_length_of_s;
183             # char *s = (char *)SvPV(ST(0), STRLEN_length_of_s);
184             # XSauto_length_of_s = STRLEN_length_of_s;
185             # RETVAL = foo(s, XSauto_length_of_s);
186             #
187             # Note that the SvPV() code line is generated via a separate call to
188             # this sub with s as the var (as opposed to *this* call, which is
189             # handling length(s)), by overriding the normal T_PV typemap (which
190             # uses PV_nolen()).
191            
192             my $name = $self->{len_name};
193            
194             print "\tSTRLEN\tSTRLEN_length_of_$name;\n";
195             # defer this line until after all the other declarations
196             $pxs->{xsub_deferred_code_lines} .=
197             "\n\tXSauto_length_of_$name = STRLEN_length_of_$name;\n";
198            
199             # this var will be declared using the normal typemap mechanism below
200             $var = "XSauto_length_of_$name";
201             }
202            
203             # Emit the variable's type and name.
204             #
205             # Includes special handling for function pointer types. An INPUT line
206             # always has the C type followed by the variable name. The C code
207             # which is emitted normally follows the same pattern. However for
208             # function pointers, the code is different: the variable name has to
209             # be embedded *within* the type. For example, these two INPUT lines:
210             #
211             # char * s
212             # int (*)(int) fn_ptr
213             #
214             # cause the following lines of C to be emitted;
215             #
216             # char * s = [something from a typemap]
217             # int (* fn_ptr)(int) = [something from a typemap]
218             #
219             # So handle specially the specific case of a type containing '(*)' by
220             # embedding the variable name *within* rather than *after* the type.
221            
222            
223             if ($type =~ / \( \s* \* \s* \) /x) {
224             # for a fn ptr type, embed the var name in the type declaration
225             print "\t" . $pxs->map_type($type, $var);
226             }
227             else {
228             print "\t",
229             ((defined($pxs->{xsub_class}) && $var eq 'CLASS')
230             ? $type
231             : $pxs->map_type($type, undef)),
232             "\t$var";
233             }
234            
235             # whitespace-tidy the type
236             $type = XS::Install::FrozenShit::Typemaps::tidy_type($type);
237            
238             # Specify the environment for when the initialiser template is evaled.
239             # Only the common ones are specified here. Other fields may be added
240             # later.
241             my $eval_vars = {
242             type => $type,
243             var => $var,
244             num => $arg_num,
245             arg => $arg,
246             };
247            
248             # The type looked up in the eval is Foo__Bar rather than Foo::Bar
249             $eval_vars->{type} =~ tr/:/_/
250             unless $pxs->{config_RetainCplusplusHierarchicalTypes};
251            
252             my $init_template;
253            
254             if (defined $init) {
255             # Use the supplied code template rather than getting it from the
256             # typemap
257            
258             $pxs->death(
259             "Internal error: XS::Install::FrozenShit::ParseXS::Node::Param::as_code(): "
260             . "both init and no_init supplied")
261             if $no_init;
262            
263             $eval_vars->{init} = $init;
264             $init_template = "\$var = $init";
265             }
266             elsif ($no_init) {
267             # don't add initialiser
268             $init_template = "";
269             }
270             else {
271             # Get the initialiser template from the typemap
272            
273             my $typemaps = $pxs->{typemaps_object};
274            
275             # Normalised type ('Foo *' becomes 'FooPtr): one of the valid vars
276             # which can appear within a typemap template.
277             (my $ntype = $type) =~ s/\s*\*/Ptr/g;
278            
279             # $subtype is really just for the T_ARRAY / DO_ARRAY_ELEM code below,
280             # where it's the type of each array element. But it's also passed to
281             # the typemap template (although undocumented and virtually unused).
282             (my $subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;
283            
284             # look up the TYPEMAP entry for this C type and grab the corresponding
285             # XS type name (e.g. $type of 'char *' gives $xstype of 'T_PV'
286             my $typemap = $typemaps->get_typemap(ctype => $type);
287             if (not $typemap) {
288             $pxs->report_typemap_failure($typemaps, $type);
289             return;
290             }
291             my $xstype = $typemap->xstype;
292            
293             # An optimisation: for the typemaps which check that the dereferenced
294             # item is blessed into the right class, skip the test for DESTROY()
295             # methods, as more or less by definition, DESTROY() will be called
296             # on an object of the right class. Basically, for T_foo_OBJ, use
297             # T_foo_REF instead. T_REF_IV_PTR was added in v5.22.0.
298             $xstype =~ s/OBJ$/REF/ || $xstype =~ s/^T_REF_IV_PTR$/T_PTRREF/
299             if $pxs->{xsub_func_name} =~ /DESTROY$/;
300            
301             # For a string-ish parameter foo, if length(foo) was also declared
302             # as a pseudo-parameter, then override the normal typedef - which
303             # would emit SvPV_nolen(...) - and instead, emit SvPV(...,
304             # STRLEN_length_of_foo)
305             if ( $xstype eq 'T_PV'
306             and exists $pxs->{xsub_sig}{names}{"length($var)"})
307             {
308             print " = ($type)SvPV($arg, STRLEN_length_of_$var);\n";
309             die "default value not supported with length(NAME) supplied"
310             if defined $default;
311             return;
312             }
313            
314             # Get the XS::Install::FrozenShit::Typemaps::InputMap object associated with the
315             # xstype. This contains the template of the code to be embedded,
316             # e.g. 'SvPV_nolen($arg)'
317             my $inputmap = $typemaps->get_inputmap(xstype => $xstype);
318             if (not defined $inputmap) {
319             $pxs->blurt("Error: No INPUT definition for type '$type', typekind '$xstype' found");
320             return;
321             }
322            
323             # Get the text of the template, with a few transformations to make it
324             # work better with fussy C compilers. In particular, strip trailing
325             # semicolons and remove any leading white space before a '#'.
326             my $expr = $inputmap->cleaned_code;
327            
328             my $argoff = $arg_num - 1;
329            
330             # Process DO_ARRAY_ELEM. This is an undocumented hack that makes the
331             # horrible T_ARRAY typemap work. "DO_ARRAY_ELEM" appears as a token
332             # in the INPUT and OUTPUT code for for T_ARRAY, within a "for each
333             # element" loop, and the purpose of this branch is to substitute the
334             # token for some real code which will process each element, based
335             # on the type of the array elements (the $subtype).
336             #
337             # Note: This gruesome bit either needs heavy rethinking or
338             # documentation. I vote for the former. --Steffen, 2011
339             # Seconded, DAPM 2024.
340             if ($expr =~ /\bDO_ARRAY_ELEM\b/) {
341             my $subtypemap = $typemaps->get_typemap(ctype => $subtype);
342             if (not $subtypemap) {
343             $pxs->report_typemap_failure($typemaps, $subtype);
344             return;
345             }
346            
347             my $subinputmap =
348             $typemaps->get_inputmap(xstype => $subtypemap->xstype);
349             if (not $subinputmap) {
350             $pxs->blurt("Error: No INPUT definition for type '$subtype',
351             typekind '" . $subtypemap->xstype . "' found");
352             return;
353             }
354            
355             my $subexpr = $subinputmap->cleaned_code;
356             $subexpr =~ s/\$type/\$subtype/g;
357             $subexpr =~ s/ntype/subtype/g;
358             $subexpr =~ s/\$arg/ST(ix_$var)/g;
359             $subexpr =~ s/\n\t/\n\t\t/g;
360             $subexpr =~ s/is not of (.*\")/[arg %d] is not of $1, ix_$var + 1/g;
361             $subexpr =~ s/\$var/${var}\[ix_$var - $argoff]/;
362             $expr =~ s/\bDO_ARRAY_ELEM\b/$subexpr/;
363             }
364            
365             if ($expr =~ m#/\*.*scope.*\*/#i) { # "scope" in C comments
366             $pxs->{xsub_SCOPE_enabled} = 1;
367             }
368            
369             # Specify additional environment for when a template derived from a
370             # *typemap* is evalled.
371             @$eval_vars{qw(ntype subtype argoff)} = ($ntype, $subtype, $argoff);
372             $init_template = $expr;
373             }
374            
375             # Now finally, emit the actual variable declaration and initialisation
376             # line(s). The variable type and name will already have been emitted.
377            
378             my $init_code =
379             length $init_template
380             ? $pxs->eval_input_typemap_code("qq\a$init_template\a", $eval_vars)
381             : "";
382            
383            
384             if (defined $default
385             # XXX for now, for backcompat, ignore default if the
386             # param has a typemap override
387             && !(defined $init)
388             # XXX for now, for backcompat, ignore default if the
389             # param wouldn't otherwise get initialised
390             && !$no_init
391             ) {
392             # Has a default value. Just terminate the variable declaration, and
393             # defer the initialisation.
394            
395             print ";\n";
396            
397             # indent the code 1 step further
398             $init_code =~ s/(\t+)/$1 /g;
399             $init_code =~ s/ /\t/g;
400            
401             if ($default eq 'NO_INIT') {
402             # for foo(a, b = NO_INIT), add code to initialise later only if
403             # an arg was supplied.
404             $pxs->{xsub_deferred_code_lines}
405             .= sprintf "\n\tif (items >= %d) {\n%s;\n\t}\n",
406             $arg_num, $init_code;
407             }
408             else {
409             # for foo(a, b = default), add code to initialise later to either
410             # the arg or default value
411             my $else = ($init_code =~ /\S/) ? "\telse {\n$init_code;\n\t}\n" : "";
412            
413             $default =~ s/"/\\"/g; # escape double quotes
414             $pxs->{xsub_deferred_code_lines}
415             .= sprintf "\n\tif (items < %d)\n\t %s = %s;\n%s",
416             $arg_num,
417             $var,
418             $pxs->eval_input_typemap_code("qq\a$default\a",
419             $eval_vars),
420             $else;
421             }
422             }
423             elsif ($pxs->{xsub_SCOPE_enabled} or $init_code !~ /^\s*\Q$var\E =/) {
424             # The template is likely a full block rather than a '$var = ...'
425             # expression. Just terminate the variable declaration, and defer the
426             # initialisation.
427             # Note that /\Q$var\E/ matches the string containing whatever $var
428             # was expanded to in the eval.
429            
430             print ";\n";
431            
432             $pxs->{xsub_deferred_code_lines} .= sprintf "\n%s;\n", $init_code
433             if $init_code =~ /\S/;
434             }
435             else {
436             # The template starts with '$var = ...'. The variable name has already
437             # been emitted, so remove it from the typemap before evalling it,
438            
439             $init_code =~ s/^\s*\Q$var\E(\s*=\s*)/$1/
440             or $pxs->death("panic: typemap doesn't start with '\$var='\n");
441            
442             printf "%s;\n", $init_code;
443             }
444            
445             if (defined $defer) {
446             $pxs->{xsub_deferred_code_lines}
447             .= $pxs->eval_input_typemap_code("qq\a$defer\a", $eval_vars) . "\n";
448             }
449             }
450              
451              
452              
453             # $param->as_output_code($ParseXS_object, $out_num])
454             #
455             # Emit code to: possibly create, then set the value of, and possibly
456             # push, an output SV, based on the values in the $param object.
457             #
458             # $out_num is optional and its presence indicates that an OUTLIST var is
459             # being pushed: it indicates the position on the stack of that SV.
460             #
461             # This function emits code such as "sv_setiv(ST(0), (IV)foo)", based on
462             # the typemap OUTPUT entry associated with $type. It passes the typemap
463             # code through a double-quotish context eval first to expand variables
464             # such as $arg and $var. It also tries to optimise the emitted code in
465             # various ways, such as using TARG where available rather than calling
466             # sv_newmortal() to obtain an SV to set to the return value.
467             #
468             # It expects to handle three categories of variable, with these general
469             # actions:
470             #
471             # RETVAL, i.e. the return value
472             #
473             # Create a new SV; use the typemap to set its value to RETVAL; then
474             # store it at ST(0).
475             #
476             # OUTLIST foo
477             #
478             # Create a new SV; use the typemap to set its value to foo; then store
479             # it at ST($out_num-1).
480             #
481             # OUTPUT: foo / OUT foo
482             #
483             # Update the value of the passed arg ST($num-1), using the typemap to
484             # set its value
485             #
486             # Note that it's possible for this function to be called *twice* for the
487             # same variable: once for OUTLIST, and once for an 'OUTPUT:' entry.
488             #
489             # It treats output typemaps as falling into two basic categories,
490             # exemplified by:
491             #
492             # sv_setFoo($arg, (Foo)$var));
493             #
494             # $arg = newFoo($var);
495             #
496             # The first form is the most general and can be used to set the SV value
497             # for all of the three variable categories above. For the first two
498             # categories it typically uses a new mortal, while for the last, it just
499             # uses the passed arg SV.
500             #
501             # The assign form of the typemap can be considered an optimisation of
502             # sv_setsv($arg, newFoo($var)), and is applicable when newFOO() is known
503             # to return a new SV. So rather than copying it to yet another new SV,
504             # just return as-is, possibly after mortalising it,
505             #
506             # Some typemaps evaluate to different code depending on whether the var is
507             # RETVAL, e.g T_BOOL is currently defined as:
508             #
509             # ${"$var" eq "RETVAL" ? \"$arg = boolSV($var);"
510             # : \"sv_setsv($arg, boolSV($var));"}
511             #
512             # So we examine the typemap *after* evaluation to determine whether it's
513             # of the form '$arg = ' or not.
514             #
515             # Note that *currently* we generally end up with the pessimised option for
516             # OUTLIST vars, since the typmaps onlt check for RETVAL.
517             #
518             # Currently RETVAL and 'OUTLIST var' mostly share the same code paths
519             # below, so they both benefit from optimisations such as using TARG
520             # instead of creating a new mortal, and using the RETVALSV C var to keep
521             # track of the temp SV, rather than repeatedly retrieving it from ST(0)
522             # etc. Note that RETVALSV is private and shouldn't be referenced within XS
523             # code or typemaps.
524              
525             sub as_output_code {
526 2     2   5 my XS::Install::FrozenShit::ParseXS::Node::Param $self = shift;
527 2         5 my XS::Install::FrozenShit::ParseXS $pxs = shift;
528 2         3 my $out_num = shift;
529              
530             my ($type, $num, $var, $do_setmagic, $output_code)
531 2         5 = @{$self}{qw(type arg_num var do_setmagic output_code)};
  2         12  
532              
533 2 50       7 if ($var eq 'RETVAL') {
534             # Do some preliminary RETVAL-specific checks and settings.
535              
536             # Only OUT/OUTPUT vars (which update one of the passed args) should be
537             # calling set magic; RETVAL and OUTLIST should be setting the value of
538             # a fresh mortal or TARG. Note that a param can be both OUTPUT and
539             # OUTLIST - the value of $do_setmagic only applies to its use as an
540             # OUTPUT (updating) value.
541              
542 2 50       7 $pxs->death("Internal error: do set magic requested on RETVAL")
543             if $do_setmagic;
544              
545             # RETVAL normally has an undefined arg_num, although it can be
546             # set to a real index if RETVAL is also declared as a parameter.
547             # But when returning its value, it's always stored at ST(0).
548 2         4 $num = 1;
549              
550             # It is possible for RETVAL to have multiple types, e.g.
551             # int
552             # foo(long RETVAL)
553             #
554             # In the above, 'long' is used for the RETVAL C var's declaration,
555             # while 'int' is used to generate the return code (for backwards
556             # compatibility).
557 2         7 $type = $pxs->{xsub_return_type};
558             }
559              
560              
561             # ------------------------------------------------------------------
562             # Do initial processing of $type, including creating various derived
563             # values
564              
565 2 50       8 unless (defined $type) {
566 0         0 $pxs->blurt("Can't determine output type for '$var'");
567 0         0 return;
568             }
569              
570             # $ntype: normalised type ('Foo *' becomes 'FooPtr' etc): one of the
571             # valid vars which can appear within a typemap template.
572 2         25 (my $ntype = $type) =~ s/\s*\*/Ptr/g;
573 2         8 $ntype =~ s/\(\)//g;
574              
575             # $subtype is really just for the T_ARRAY / DO_ARRAY_ELEM code below,
576             # where it's the type of each array element. But it's also passed to
577             # the typemap template (although undocumented and virtually unused).
578             # Basically for a type like FooArray or FooArrayPtr, the subtype is Foo.
579 2         13 (my $subtype = $ntype) =~ s/(?:Array)?(?:Ptr)?$//;
580              
581             # whitespace-tidy the type
582 2         10 $type = XS::Install::FrozenShit::Typemaps::tidy_type($type);
583              
584             # The type as supplied to the eval is Foo__Bar rather than Foo::Bar
585 2         6 my $eval_type = $type;
586 2 50       9 $eval_type =~ tr/:/_/ unless $pxs->{config_RetainCplusplusHierarchicalTypes};
587              
588             # We can be called twice for the same variable: once to update the
589             # original arg (via an entry in OUTPUT) and once to push the param's
590             # value (via OUTLIST). When doing the latter, any override code on an
591             # OUTPUT line should not be used.
592 2 50       7 undef $output_code if defined $out_num;
593              
594              
595             # ------------------------------------------------------------------
596             # Find the template code (pre any eval) and store it in $expr.
597             # This is typically obtained via a typemap lookup, but can be
598             # overridden. Also set vars ready for evalling the typemap template.
599              
600 2         5 my $expr;
601             my $outputmap;
602 2         4 my $typemaps = $pxs->{typemaps_object};
603              
604 2 50       10 if (defined $output_code) {
    50          
605             # An override on an OUTPUT line: use that instead of the typemap.
606             # Note that we don't set $expr here, because $expr holds a template
607             # string pre-eval, while OUTPUT override code is *not*
608             # template-expanded, so $output_code is effectively post-eval code.
609             }
610             elsif ($type =~ /^array\(([^,]*),(.*)\)/) {
611             # Specially handle the implicit array return type, "array(type, nlelem)"
612             # rather than using a typemap entry. It returns a string SV whose
613             # buffer is a copy of $var, which it assumes is a C array of
614             # type 'type' with 'nelem' elements.
615              
616 0         0 my ($atype, $nitems) = ($1, $2);
617              
618 0 0       0 if ($var ne 'RETVAL') {
619             # This special type is intended for use only as the return type of
620             # an XSUB
621 0 0       0 $pxs->blurt("Can't use array(type,nitems) type for "
622             . (defined $out_num ? "OUTLIST" : "OUT")
623             . " parameter");
624 0         0 return;
625             }
626              
627 0         0 $expr = "\tsv_setpvn(\$arg, (char *)\$var, $nitems * sizeof($atype));\n";
628             }
629             else {
630             # Handle a normal return type via a typemap.
631              
632             # Get the output map entry for this type; complain if not found.
633 2         10 my $typemap = $typemaps->get_typemap(ctype => $type);
634 2 50       8 if (not $typemap) {
635 0         0 $pxs->report_typemap_failure($typemaps, $type);
636 0         0 return;
637             }
638              
639 2         10 $outputmap = $typemaps->get_outputmap(xstype => $typemap->xstype);
640 2 50       7 if (not $outputmap) {
641 0         0 $pxs->blurt("Error: No OUTPUT definition for type '$type', typekind '"
642             . $typemap->xstype . "' found");
643 0         0 return;
644             }
645              
646             # Get the text of the typemap template, with a few transformations to
647             # make it work better with fussy C compilers. In particular, strip
648             # trailing semicolons and remove any leading white space before a '#'.
649              
650 2         10 $expr = $outputmap->cleaned_code;
651             }
652              
653 2 50       15 my $arg = $pxs->ST(defined $out_num ? $out_num + 1 : $num);
654              
655             # Specify the environment for if/when the code template is evalled.
656 2         22 my $eval_vars = {
657             num => $num,
658             var => $var,
659             do_setmagic => $do_setmagic,
660             subtype => $subtype,
661             ntype => $ntype,
662             arg => $arg,
663             type => $eval_type,
664             };
665              
666              
667             # ------------------------------------------------------------------
668             # Handle DO_ARRAY_ELEM token as a very special case
669              
670 2 50 33     25 if (!defined $output_code and $expr =~ /\bDO_ARRAY_ELEM\b/) {
671             # See the comments in XS::Install::FrozenShit::ParseXS::Node::Param::as_code() that
672             # explain the similar code for the DO_ARRAY_ELEM hack there.
673              
674 0 0       0 if ($var ne 'RETVAL') {
675             # Typemap templates containing DO_ARRAY_ELEM are assumed to contain
676             # a loop which explicitly stores a new mortal SV at each of the
677             # locations ST(0) .. ST(n-1), and which then uses the code from the
678             # typemap for the underlying array element to set each SV's value.
679             #
680             # This is a horrible hack for RETVAL, which would probably fail with
681             # OUTLIST due to stack offsets being wrong, and definitely would
682             # fail with OUT, which is supposed to be updating parameter SVs, not
683             # pushing anything on the stack. So forbid all except RETVAL.
684 0 0       0 $pxs->blurt("Can't use typemap containing DO_ARRAY_ELEM for "
685             . (defined $out_num ? "OUTLIST" : "OUT")
686             . " parameter");
687 0         0 return;
688             }
689              
690 0         0 my $subtypemap = $typemaps->get_typemap(ctype => $subtype);
691 0 0       0 if (not $subtypemap) {
692 0         0 $pxs->report_typemap_failure($typemaps, $subtype);
693 0         0 return;
694             }
695              
696 0         0 my $suboutputmap = $typemaps->get_outputmap(xstype => $subtypemap->xstype);
697 0 0       0 if (not $suboutputmap) {
698 0         0 $pxs->blurt("Error: No OUTPUT definition for type '$subtype', typekind '" . $subtypemap->xstype . "' found");
699 0         0 return;
700             }
701              
702 0         0 my $subexpr = $suboutputmap->cleaned_code;
703 0         0 $subexpr =~ s/ntype/subtype/g;
704 0         0 $subexpr =~ s/\$arg/ST(ix_$var)/g;
705 0         0 $subexpr =~ s/\$var/${var}\[ix_$var]/g;
706 0         0 $subexpr =~ s/\n\t/\n\t\t/g;
707 0         0 $expr =~ s/\bDO_ARRAY_ELEM\b/$subexpr/;
708              
709             # We do our own code emitting and return here (rather than control
710             # passing on to normal RETVAL processing) since that processing is
711             # expecting to push a single temp onto the stack, while our code
712             # pushes several temps.
713 0         0 print $pxs->eval_output_typemap_code("qq\a$expr\a", $eval_vars);
714 0         0 return;
715             }
716              
717              
718             # ------------------------------------------------------------------
719             # Now emit code for the three types of return value:
720             #
721             # RETVAL - The usual case: store an SV at ST(0) which is set
722             # to the value of RETVAL. This is typically a new
723             # mortal, but may be optimised to use TARG.
724             #
725             # OUTLIST param - if $out_num is defined (and will be >= 0) Push
726             # after any RETVAL, new mortal(s) containing the
727             # current values of the local var set from that
728             # parameter. (May also use TARG if not already used
729             # by RETVAL).
730             #
731             # OUT/OUTPUT param - update passed arg SV at ST($num-1) (which
732             # corresponds to param) with the current value of
733             # the local var set from that parameter.
734              
735 2 50 33     8 if ($var ne 'RETVAL' and not defined $out_num) {
736             # This is a normal OUTPUT var: i.e. a named parameter whose
737             # corresponding arg on the stack should be updated with the
738             # parameter's current value by using the code contained in the
739             # output typemap.
740             #
741             # Note that for args being *updated* (as opposed to replaced), this
742             # branch relies on the typemap to Do The Right Thing. For example,
743             # T_BOOL currently has this typemap entry:
744             #
745             # ${"$var" eq "RETVAL" ? \"$arg = boolSV($var);"
746             # : \"sv_setsv($arg, boolSV($var));"}
747             #
748             # which means that if we hit this branch, $evalexpr will have been
749             # expanded to something like "sv_setsv(ST(2), boolSV(foo))".
750              
751 0 0       0 unless (defined $num) {
752 0         0 $pxs->blurt("Internal error: OUT parameter has undefined argument number");
753 0         0 return;
754             }
755              
756             # Use the code on the OUTPUT line if specified, otherwise use the
757             # typemap
758 0 0       0 my $code = defined $output_code
759             ? "\t$output_code\n"
760             : $pxs->eval_output_typemap_code("qq\a$expr\a", $eval_vars);
761 0         0 print $code;
762              
763             # For parameters in the OUTPUT section, honour the SETMAGIC in force
764             # at the time. For parameters instead being output because of an OUT
765             # keyword in the signature, assume set magic always.
766 0 0 0     0 print "\tSvSETMAGIC($arg);\n" if !$self->{in_output} || $do_setmagic;
767 0         0 return;
768             }
769              
770              
771             # ------------------------------------------------------------------
772             # The rest of this main body handles RETVAL or "OUTLIST foo".
773              
774 2 50 33     7 if (defined $output_code and !defined $out_num) {
775             # Handle this (just emit overridden code as-is):
776             # OUTPUT:
777             # RETVAL output_code
778 0         0 print "\t$output_code\n";
779 0 0       0 print "\t++SP;\n" if $pxs->{xsub_stack_was_reset};
780 0         0 return;
781             }
782              
783             # Emit a standard RETVAL/OUTLIST return
784              
785              
786             # ------------------------------------------------------------------
787             # First, evaluate the typemap, expanding any vars like $var and $arg,
788             # for example,
789             #
790             # $arg = newFoo($var);
791             # or
792             # sv_setFoo($arg, $var);
793             #
794             # However, rather than using the actual destination (such as ST(0))
795             # for the value of $arg, we instead set it initially to RETVALSV. This
796             # is because often the SV will be used in more than one statement,
797             # and so it is more efficient to temporarily store it in a C auto var.
798             # So we normally emit code such as:
799             #
800             # {
801             # SV *RETVALSV;
802             # RETVALSV = newFoo(RETVAL);
803             # RETVALSV = sv_2mortal(RETVALSV);
804             # ST(0) = RETVALSV;
805             # }
806             #
807             # Rather than
808             #
809             # ST(0) = newFoo(RETVAL);
810             # sv_2mortal(ST(0));
811             #
812             # Later we sometimes modify the evalled typemap to change 'RETVALSV'
813             # to some other value:
814             # - back to e.g. 'ST(0)' if there is no other use of the SV;
815             # - to TARG when we are using the OP_ENTERSUB's targ;
816             # - to $var when then return type is SV* (and thus ntype is SVPtr)
817             # and so the variable will already have been declared as type 'SV*'
818             # and thus there is no need for a RETVALSV too.
819             #
820             # Note that we evaluate the typemap early here so that the various
821             # regexes below such as /^\s*\Q$arg\E\s*=/ can be matched against
822             # the *evalled* result of typemap entries such as
823             #
824             # ${ "$var" eq "RETVAL" ? \"$arg = $var;" : \"sv_setsv_mg($arg, $var);" }
825             #
826             # which may eval to something like "RETVALSV = RETVAL" and
827             # subsequently match /^\s*\Q$arg\E =/ (where $arg is "RETVAL"), but
828             # couldn't have matched against the original typemap.
829             # This is why we *always* set $arg to 'RETVALSV' first and then modify
830             # the typemap later - we don't know what final value we want for $arg
831             # until after we've examined the evalled result.
832              
833 2         7 my $orig_arg = $arg;
834 2         16 $eval_vars->{arg} = $arg = 'RETVALSV';
835 2         14 my $evalexpr = $pxs->eval_output_typemap_code("qq\a$expr\a", $eval_vars);
836              
837              
838             # ------------------------------------------------------------------
839             # Examine the just-evalled typemap code to determine what optimisations
840             # etc can be performed and what sort of code needs emitting. The two
841             # halves of this following if/else examine the two forms of evalled
842             # typemap:
843             #
844             # RETVALSV = newFoo((Foo)RETVAL);
845             # and
846             # sv_setFoo(RETVALSV, (Foo)RETVAL);
847             #
848             # In particular, the first form is assumed to be returning an SV which
849             # the function has generated itself (e.g. newSVREF()) and which may
850             # just need mortalising; while the second form generally needs a call
851             # to sv_newmortal() first to create an SV which the function can then
852             # set the value of.
853              
854 2         6 my $do_mortalize = 0; # Emit an sv_2mortal()
855 2         4 my $want_newmortal = 0; # Emit an sv_newmortal()
856 2         6 my $retvar = 'RETVALSV'; # The name of the C var which holds the SV
857             # (likely tmp) to set to the value of the var
858              
859 2 50       86 if ($evalexpr =~ /^\s*\Q$arg\E\s*=/) {
860             # Handle this form: RETVALSV = newFoo((Foo)RETVAL);
861             # newFoo creates its own SV: we just need to mortalise and return it
862              
863             # Is the SV one of the immortal SVs?
864 2 50       138 if ($evalexpr =~
865             /^\s*
866             \Q$arg\E
867             \s*=\s*
868             ( boolSV\(.*\)
869             | &PL_sv_yes
870             | &PL_sv_no
871             | &PL_sv_undef
872             | &PL_sv_zero
873             )
874             \s*;\s*$
875             /x)
876             {
877             # If so, we can skip mortalising it to stop it leaking.
878 0         0 $retvar = $orig_arg; # just assign to ST(N) directly
879             }
880             else {
881             # general '$arg = newFOO()' typemap
882 2         5 $do_mortalize = 1;
883              
884             # If $var is already of type SV*, then use that instead of
885             # declaring 'SV* RETVALSV' as an intermediate var.
886 2 50       8 $retvar = $var if $ntype eq "SVPtr";
887             }
888             }
889             else {
890             # Handle this (eval-expanded) form of typemap:
891             # sv_setFoo(RETVALSV, (Foo)var);
892             # We generally need to supply a mortal SV for the typemap code to
893             # set, and then return it on the stack,
894              
895             # First, see if we can use the targ (if any) attached to the current
896             # OP_ENTERSUB, to avoid having to create a new mortal.
897             #
898             # The targetable() OutputMap class method looks at whether the code
899             # snippet is of a form suitable for using TARG as the destination.
900             # It looks for one of a known list of well-behaved setting function
901             # calls, like sv_setiv() which will set the TARG to a value that
902             # doesn't include magic, tieing, being a reference (which would leak
903             # as the TARG is never freed), etc. If so, emit dXSTARG and replace
904             # RETVALSV with TARG.
905             #
906             # For backwards-compatibility, dXSTARG may have already been emitted
907             # early in the XSUB body, when a more restrictive set of targ-
908             # compatible typemap entries were checked for. Note that dXSTARG is
909             # defined as something like:
910             #
911             # SV * targ = (PL_op->op_private & OPpENTERSUB_HASTARG)
912             # ? PAD_SV(PL_op->op_targ) : sv_newmortal()
913              
914 0 0 0     0 if ( $pxs->{config_optimize}
      0        
915             && XS::Install::FrozenShit::Typemaps::OutputMap->targetable($evalexpr)
916             && !$pxs->{xsub_targ_used})
917             {
918             # So TARG is available for use.
919 0         0 $retvar = 'TARG';
920 0         0 $pxs->{xsub_targ_used} = 1; # can only use TARG to return one value
921              
922             # Since we're using TARG for the return SV, see if we can use the
923             # TARG[iun] macros as appropriate to speed up setting it.
924             # If so, convert "sv_setiv(RETVALSV, val)" to "TARGi(val,1)" and
925             # similarly for uv and nv. These macros skip a function call for the
926             # common case where TARG is already a simple IV/UV/NV. Convert the
927             # _mg forms too: since we're setting the TARG, there shouldn't be
928             # set magic on it, so the _mg action can be safely ignored.
929              
930 0         0 $evalexpr =~ s{
931             ^
932             (\s*)
933             sv_set([iun])v(?:_mg)?
934             \(
935             \s* RETVALSV \s* ,
936             \s* (.*)
937             \)
938             ( \s* ; \s*)
939             $
940             }
941             {$1TARG$2($3, 1)$4}x;
942             }
943             else {
944             # general typemap: give it a fresh SV to set the value of.
945 0         0 $want_newmortal = 1;
946             }
947             }
948              
949              
950             # ------------------------------------------------------------------
951             # Now emit the return C code, based on the various flags and values
952             # determined above.
953              
954 2         8 my $do_scope; # wrap code in a {} block
955             my @lines; # Lines of code to eventually emit
956              
957             # Do any declarations first
958              
959 2 50 33     12 if ($retvar eq 'TARG' && !$pxs->{xsub_targ_declared_early}) {
    50          
960 0         0 push @lines, "\tdXSTARG;\n";
961 0         0 $do_scope = 1;
962             }
963             elsif ($retvar eq 'RETVALSV') {
964 0         0 push @lines, "\tSV * $retvar;\n";
965 0         0 $do_scope = 1;
966             }
967              
968 2 50       7 push @lines, "\tRETVALSV = sv_newmortal();\n" if $want_newmortal;
969              
970             # Emit the typemap, while changing the name of the destination SV back
971             # from RETVALSV to one of the other forms (varname/TARG/ST(N)) if was
972             # determined earlier to be necessary.
973             # Skip emitting it if it's of the trivial form "var = var", which is
974             # generated when the typemap is of the form '$arg = $var' and the SVPtr
975             # optimisation is using $var for the destination.
976              
977 2 50       24 $evalexpr =~ s/\bRETVALSV\b/$retvar/g if $retvar ne 'RETVALSV';
978              
979 2 50       111 unless ($evalexpr =~ /^\s*\Q$var\E\s*=\s*\Q$var\E\s*;\s*$/) {
980 0         0 push @lines, split /^/, $evalexpr
981             }
982              
983             # Emit mortalisation on the result SV if needed
984 2 50       13 push @lines, "\t$retvar = sv_2mortal($retvar);\n" if $do_mortalize;
985              
986             # Emit the final 'ST(n) = RETVALSV' or similar, unless ST(n)
987             # was already assigned to earlier directly by the typemap.
988 2 50       10 push @lines, "\t$orig_arg = $retvar;\n" unless $retvar eq $orig_arg;
989              
990 2 50       8 if ($do_scope) {
991             # Add an extra 4-indent, then wrap the output code in a new block
992 0         0 for (@lines) {
993 0         0 s/\t/ /g; # break down all tabs into spaces
994 0         0 s/^/ /; # add 4-space extra indent
995 0         0 s/ /\t/g; # convert 8 spaces back to tabs
996             }
997 0         0 unshift @lines, "\t{\n";
998 0         0 push @lines, "\t}\n";
999             }
1000              
1001 2         14 print @lines;
1002 2 50       23 print "\t++SP;\n" if $pxs->{xsub_stack_was_reset};
1003             }
1004              
1005             # ======================================================================
1006              
1007             package
1008             XS::Install::FrozenShit::ParseXS::Node::Sig;
1009              
1010             # Node subclass which holds the state of an XSUB's signature, based on the
1011             # XSUB's actual signature plus any INPUT lines. It is a mainly a list of
1012             # Node::Param children.
1013              
1014             BEGIN {
1015 1     1   4049 our @ISA = qw(XS::Install::FrozenShit::ParseXS::Node);
1016              
1017 1         5 our @FIELDS = (
1018             @XS::Install::FrozenShit::ParseXS::Node::FIELDS,
1019             'orig_params', # Array ref of Node::Param objects representing
1020             # the original (as parsed) parameters of this XSUB
1021              
1022             'params', # Array ref of Node::Param objects representing
1023             # the current parameters of this XSUB - this
1024             # is orig_params plus any updated fields from
1025             # processing INPUT and OUTPUT lines. Note that
1026             # with multiple CASE: blocks, there can be
1027             # multiple sets of INPUT and OUTPUT etc blocks.
1028             # params is reset to the contents of orig_params
1029             # after the start of each new CASE: block.
1030              
1031             'names', # Hash ref mapping variable names to Node::Param
1032             # objects
1033              
1034             'sig_text', # The original text of the sig, e.g.
1035             # 'param1, int param2 = 0'
1036              
1037             'seen_ellipsis', # Bool: XSUB signature has ( ,...)
1038              
1039             'nargs', # The number of args expected from caller
1040             'min_args', # The minimum number of args allowed from caller
1041              
1042             'auto_function_sig_override', # the C_ARGS value, if any
1043              
1044             );
1045              
1046 1 50       8 fields->import(@FIELDS) if $USING_FIELDS;
1047             }
1048              
1049              
1050             # ----------------------------------------------------------------
1051             # Parse the XSUB's signature: $sig->{sig_text}
1052             #
1053             # Split the signature on commas into parameters, while allowing for
1054             # things like '(a = ",", b)'. Then for each parameter, parse its
1055             # various fields and store in a XS::Install::FrozenShit::ParseXS::Node::Param object.
1056             # Store those Param objects within the Sig object, plus any other state
1057             # deduced from the signature, such as min/max permitted number of args.
1058             #
1059             # A typical signature might look like:
1060             #
1061             # OUT char *s, \
1062             # int length(s), \
1063             # OUTLIST int size = 10)
1064             #
1065             # ----------------------------------------------------------------
1066              
1067             my ($C_group_rex, $C_arg);
1068              
1069             # Group in C (no support for comments or literals)
1070             #
1071             # DAPM 2024: I'm not entirely clear what this is supposed to match.
1072             # It appears to match balanced and possibly nested [], {} etc, with
1073             # similar but possibly unbalanced punctuation within. But the balancing
1074             # brackets don't have to correspond: so [} is just as valid as [] or {},
1075             # as is [{{{{] or even [}}}}}
1076              
1077             $C_group_rex = qr/ [({\[]
1078             (?: (?> [^()\[\]{}]+ ) | (??{ $C_group_rex }) )*
1079             [)}\]] /x;
1080              
1081             # $C_arg: match a chunk in C without comma at toplevel (no comments),
1082             # i.e. a single arg within an XS signature, such as
1083             # foo = ','
1084             #
1085             # DAPM 2024. This appears to match zero, one or more of:
1086             # a random collection of non-bracket/quote/comma chars (e.g, a word or
1087             # number or 'int *foo' etc), or
1088             # a balanced(ish) nested brackets, or
1089             # a "string literal", or
1090             # a 'c' char literal
1091             # So (I guess), it captures the next item in a function signature
1092              
1093             $C_arg = qr/ (?: (?> [^()\[\]{},"']+ )
1094             | (??{ $C_group_rex })
1095             | " (?: (?> [^\\"]+ )
1096             | \\.
1097             )* " # String literal
1098             | ' (?: (?> [^\\']+ ) | \\. )* ' # Char literal
1099             )* /xs;
1100              
1101              
1102             sub parse {
1103 4     4   14 my XS::Install::FrozenShit::ParseXS::Node::Sig $self = shift;
1104 4         17 my XS::Install::FrozenShit::ParseXS $pxs = shift;
1105              
1106             # remove line continuation chars (\)
1107 4         18 $self->{sig_text} =~ s/\\\s*/ /g;
1108 4         12 my $sig_text = $self->{sig_text};
1109              
1110 4         11 my @param_texts;
1111 4         7 my $opt_args = 0; # how many params with default values seen
1112 4         8 my $nargs = 0; # how many args are expected
1113              
1114             # First, split signature into separate parameters
1115              
1116 4 50       24 if ($sig_text =~ /\S/) {
1117 4         12 my $sig_c = "$sig_text ,";
1118 1     1   459 use re 'eval'; # needed for 5.16.0 and earlier
  1         2  
  1         100  
1119 4         27 my $can_use_regex = ($sig_c =~ /^( (??{ $C_arg }) , )* $ /x);
1120 1     1   7 no re 'eval';
  1         2  
  1         55  
1121              
1122 4 50       13 if ($can_use_regex) {
1123             # If the parameters are capable of being split by using the
1124             # fancy regex, do so. This splits the params on commas, but
1125             # can handle things like foo(a = ",", b)
1126 1     1   5 use re 'eval';
  1         2  
  1         1992  
1127 4         21 @param_texts = ($sig_c =~ /\G ( (??{ $C_arg }) ) , /xg);
1128             }
1129             else {
1130             # This is the fallback parameter-splitting path for when the
1131             # $C_arg regex doesn't work. This code path should ideally
1132             # never be reached, and indicates a design weakness in $C_arg.
1133 0         0 @param_texts = split(/\s*,\s*/, $sig_text);
1134 0         0 Warn($pxs, "Warning: cannot parse parameter list '$sig_text', fallback to split");
1135             }
1136             }
1137             else {
1138 0         0 @param_texts = ();
1139             }
1140              
1141             # C++ methods get a fake object/class param at the start.
1142             # This affects arg numbering.
1143 4 50       21 if (defined($pxs->{xsub_class})) {
1144             my ($var, $type) =
1145 0 0 0     0 ($pxs->{xsub_seen_static} or $pxs->{xsub_func_name} eq 'new')
1146             ? ('CLASS', "char *")
1147             : ('THIS', "$pxs->{xsub_class} *");
1148              
1149 0         0 my XS::Install::FrozenShit::ParseXS::Node::Param $param
1150             = XS::Install::FrozenShit::ParseXS::Node::Param->new( {
1151             var => $var,
1152             type => $type,
1153             is_synthetic => 1,
1154             arg_num => ++$nargs,
1155             });
1156 0         0 push @{$self->{params}}, $param;
  0         0  
1157 0         0 $self->{names}{$var} = $param;
1158 0         0 $param->check($pxs)
1159             }
1160              
1161             # For non-void return types, add a fake RETVAL parameter. This triggers
1162             # the emitting of an 'int RETVAL;' declaration or similar, and (e.g. if
1163             # later flagged as in_output), triggers the emitting of code to return
1164             # RETVAL's value.
1165             #
1166             # Note that a RETVAL param can be in three main states:
1167             #
1168             # fully-synthetic What is being created here. RETVAL hasn't appeared
1169             # in a signature or INPUT.
1170             #
1171             # semi-real Same as fully-synthetic, but with a defined
1172             # arg_num, and with an updated position within
1173             # @{$self->{params}}.
1174             # A RETVAL has appeared in the signature, but
1175             # without a type yet specified, so it continues to
1176             # use {xsub_return_type}.
1177             #
1178             # real is_synthetic, no_init flags turned off. Its
1179             # type comes from the sig or INPUT line. This is
1180             # just a normal parameter now.
1181              
1182 4 100       27 if ($pxs->{xsub_return_type} ne 'void') {
1183             my XS::Install::FrozenShit::ParseXS::Node::Param $param =
1184             XS::Install::FrozenShit::ParseXS::Node::Param->new( {
1185             var => 'RETVAL',
1186             type => $pxs->{xsub_return_type},
1187 2         48 no_init => 1, # just declare the var, don't initialise it
1188             is_synthetic => 1,
1189             } );
1190              
1191 2         9 push @{$self->{params}}, $param;
  2         8  
1192 2         14 $self->{names}{RETVAL} = $param;
1193 2         9 $param->check($pxs)
1194             }
1195              
1196 4         10 for (@param_texts) {
1197             # Process each parameter. A parameter is of the general form:
1198             #
1199             # OUT char* foo = expression
1200             #
1201             # where:
1202             # IN/OUT/OUTLIST etc are only allowed under
1203             # $pxs->{config_allow_inout}
1204             #
1205             # a C type is only allowed under
1206             # $pxs->{config_allow_argtypes}
1207             #
1208             # foo can be a plain C variable name, or can be
1209             # length(foo) but only under $pxs->{config_allow_argtypes}
1210             #
1211             # = default default value - only allowed under
1212             # $pxs->{config_allow_argtypes}
1213              
1214 8         38 s/^\s+//;
1215 8         42 s/\s+$//;
1216              
1217             # Process ellipsis (...)
1218              
1219             $pxs->blurt("further XSUB parameter seen after ellipsis (...)")
1220 8 50       25 if $self->{seen_ellipsis};
1221              
1222 8 100       23 if ($_ eq '...') {
1223 2         5 $self->{seen_ellipsis} = 1;
1224 2         7 next;
1225             }
1226              
1227             # Decompose parameter into its components.
1228             # Note that $name can be either 'foo' or 'length(foo)'
1229              
1230 6         75 my ($out_type, $type, $name, $sp1, $sp2, $default) =
1231             /^
1232             (?:
1233             (IN|IN_OUT|IN_OUTLIST|OUT|OUTLIST)
1234             \b\s*
1235             )?
1236             (.*?) # optional type
1237             \s*
1238             \b
1239             ( \w+ # var
1240             | length\( \s*\w+\s* \) # length(var)
1241             )
1242             (?:
1243             (\s*) = (\s*) ( .*?) # default expr
1244             )?
1245             \s*
1246             $
1247             /x;
1248              
1249 6 50       23 unless (defined $name) {
1250 0 0       0 if (/^ SV \s* \* $/x) {
1251             # special-case SV* as a placeholder for backwards
1252             # compatibility.
1253 0         0 push @{$self->{params}},
  0         0  
1254             XS::Install::FrozenShit::ParseXS::Node::Param->new( {
1255             var => 'SV *',
1256             arg_num => ++$nargs,
1257             });
1258             }
1259             else {
1260 0         0 $pxs->blurt("Unparseable XSUB parameter: '$_'");
1261             }
1262 0         0 next;
1263             }
1264              
1265 6 50 33     41 undef $type unless length($type) && $type =~ /\S/;
1266              
1267 6         35 my XS::Install::FrozenShit::ParseXS::Node::Param $param
1268             = XS::Install::FrozenShit::ParseXS::Node::Param->new( {
1269             var => $name,
1270             });
1271              
1272             # Check for duplicates
1273              
1274 6         27 my $old_param = $self->{names}{$name};
1275 6 50       18 if ($old_param) {
1276 0 0 0     0 if ( $name eq 'RETVAL'
      0        
1277             and $old_param->{is_synthetic}
1278             and !defined $old_param->{arg_num})
1279             {
1280             # RETVAL is currently fully synthetic. Now that it has
1281             # been declared as a parameter too, override any implicit
1282             # RETVAL declaration. Delete the original param from the
1283             # param list.
1284 0         0 @{$self->{params}} = grep $_ != $old_param, @{$self->{params}};
  0         0  
  0         0  
1285             # If the param declaration includes a type, it becomes a
1286             # real parameter. Otherwise the param is kept as
1287             # 'semi-real' (synthetic, but with an arg_num) until such
1288             # time as it gets a type set in INPUT, which would remove
1289             # the synthetic/no_init.
1290 0 0       0 $param = $old_param if !defined $type;
1291             }
1292             else {
1293 0         0 $pxs->blurt(
1294             "Error: duplicate definition of parameter '$name' ignored");
1295 0         0 next;
1296             }
1297             }
1298              
1299 6         22 push @{$self->{params}}, $param;
  6         21  
1300 6         38 $self->{names}{$name} = $param;
1301              
1302             # Process optional IN/OUT etc modifier
1303              
1304 6 50       16 if (defined $out_type) {
1305 0 0       0 if ($pxs->{config_allow_inout}) {
1306 0 0       0 $out_type = $out_type eq 'IN' ? '' : $out_type;
1307             }
1308             else {
1309 0         0 $pxs->blurt("parameter IN/OUT modifier not allowed under -noinout");
1310             }
1311             }
1312             else {
1313 6         19 $out_type = '';
1314             }
1315              
1316             # Process optional type
1317              
1318 6 50 33     42 if (defined($type) && !$pxs->{config_allow_argtypes}) {
1319 0         0 $pxs->blurt("parameter type not allowed under -noargtypes");
1320 0         0 undef $type;
1321             }
1322              
1323             # Process 'length(foo)' pseudo-parameter
1324              
1325 6         13 my $is_length;
1326             my $len_name;
1327              
1328 6 50       18 if ($name =~ /^length\( \s* (\w+) \s* \)\z/x) {
1329 0 0       0 if ($pxs->{config_allow_argtypes}) {
1330 0         0 $len_name = $1;
1331 0         0 $is_length = 1;
1332 0 0       0 if (defined $default) {
1333 0         0 $pxs->blurt("Default value not allowed on length() parameter '$len_name'");
1334 0         0 undef $default;
1335             }
1336             }
1337             else {
1338 0         0 $pxs->blurt("length() pseudo-parameter not allowed under -noargtypes");
1339             }
1340             }
1341              
1342             # Handle ANSI params: those which have a type or 'length(s)',
1343             # and which thus don't need a matching INPUT line.
1344              
1345 6 50 33     22 if (defined $type or $is_length) { # 'int foo' or 'length(foo)'
1346 6         47 @$param{qw(type is_ansi)} = ($type, 1);
1347              
1348 6 50       20 if ($is_length) {
1349 0         0 $param->{no_init} = 1;
1350 0         0 $param->{is_length} = 1;
1351 0         0 $param->{len_name} = $len_name;
1352             }
1353             }
1354              
1355 6 50       14 $param->{in_out} = $out_type if length $out_type;
1356 6 50       26 $param->{no_init} = 1 if $out_type =~ /^OUT/;
1357              
1358             # Process the default expression, including making the text
1359             # to be used in "usage: ..." error messages.
1360 6         11 my $report_def = '';
1361 6 50       17 if (defined $default) {
1362 0         0 $opt_args++;
1363             # The default expression for reporting usage. For backcompat,
1364             # sometimes preserve the spaces either side of the '='
1365 0 0 0     0 $report_def = ((defined $type or $is_length) ? '' : $sp1)
1366             . "=$sp2$default";
1367 0         0 $param->{default_usage} = $report_def;
1368 0         0 $param->{default} = $default;
1369             }
1370              
1371 6 50 33     29 if ($out_type eq "OUTLIST" or $is_length) {
1372 0         0 $param->{arg_num} = undef;
1373             }
1374             else {
1375 6         24 $param->{arg_num} = ++$nargs;
1376             }
1377             } # for (@param_texts)
1378              
1379 4         11 $self->{nargs} = $nargs;
1380 4         21 $self->{min_args} = $nargs - $opt_args;
1381             }
1382              
1383              
1384             # Return a string to be used in "usage: .." error messages.
1385              
1386             sub usage_string {
1387 4     4   10 my XS::Install::FrozenShit::ParseXS::Node::Sig $self = shift;
1388              
1389             my @args = map {
1390             $_->{var}
1391             . (defined $_->{default_usage}
1392             ?$_->{default_usage}
1393 6 50       36 : ''
1394             )
1395             }
1396             grep {
1397             defined $_->{arg_num},
1398 8         23 }
1399 4         9 @{$self->{params}};
  4         15  
1400              
1401 4 100       17 push @args, '...' if $self->{seen_ellipsis};
1402 4         24 return join ', ', @args;
1403             }
1404              
1405              
1406             # $self->C_func_signature():
1407             #
1408             # return a string containing the arguments to pass to an autocall C
1409             # function, e.g. 'a, &b, c'.
1410              
1411             sub C_func_signature {
1412 0     0     my XS::Install::FrozenShit::ParseXS::Node::Sig $self = shift;
1413 0           my XS::Install::FrozenShit::ParseXS $pxs = shift;
1414              
1415 0           my @args;
1416 0           for my $param (@{$self->{params}}) {
  0            
1417             next if $param->{is_synthetic} # THIS/CLASS/RETVAL
1418             # if a synthetic RETVAL has acquired an arg_num, then
1419             # it's appeared in the signature (although without a
1420             # type) and has become semi-real.
1421 0 0 0       && !($param->{var} eq 'RETVAL' && defined($param->{arg_num}));
      0        
1422              
1423 0 0         if ($param->{is_length}) {
1424 0           push @args, "XSauto_length_of_$param->{len_name}";
1425 0           next;
1426             }
1427              
1428 0 0         if ($param->{var} eq 'SV *') {
1429             #backcompat placeholder
1430 0           $pxs->blurt("Error: parameter 'SV *' not valid as a C argument");
1431 0           next;
1432             }
1433              
1434 0           my $io = $param->{in_out};
1435 0 0         $io = '' unless defined $io;
1436              
1437             # Ignore fake/alien stuff, except an OUTLIST arg, which
1438             # isn't passed from perl (so no arg_num), but *is* passed to
1439             # the C function and then back to perl.
1440 0 0 0       next unless defined $param->{arg_num} or $io eq 'OUTLIST';
1441            
1442 0           my $a = $param->{var};
1443 0 0 0       $a = "&$a" if $param->{is_addr} or $io =~ /OUT/;
1444 0           push @args, $a;
1445             }
1446              
1447 0           return join(", ", @args);
1448             }
1449              
1450              
1451             # $self->proto_string():
1452             #
1453             # return a string containing the perl prototype string for this XSUB,
1454             # e.g. '$$;$$@'.
1455              
1456             sub proto_string {
1457 0     0     my XS::Install::FrozenShit::ParseXS::Node::Sig $self = shift;
1458              
1459             # Generate a prototype entry for each param that's bound to a real
1460             # arg. Use '$' unless the typemap for that param has specified an
1461             # overridden entry.
1462             my @p = map defined $_->{proto} ? $_->{proto} : '$',
1463             grep defined $_->{arg_num} && $_->{arg_num} > 0,
1464 0 0 0       @{$self->{params}};
  0            
1465              
1466 0           my @sep = (';'); # separator between required and optional args
1467 0           my $min = $self->{min_args};
1468 0 0         if ($min < $self->{nargs}) {
1469             # has some default vals
1470 0           splice (@p, $min, 0, ';');
1471 0           @sep = (); # separator already added
1472             }
1473 0 0         push @p, @sep, '@' if $self->{seen_ellipsis}; # '...'
1474 0           return join '', @p;
1475             }
1476              
1477             1;
1478              
1479             # vim: ts=4 sts=4 sw=4: et: