File Coverage

blib/lib/ExtUtils/Typemaps.pm
Criterion Covered Total %
statement 339 364 93.1
branch 125 172 72.6
condition 40 59 67.8
subroutine 30 33 90.9
pod 19 21 90.4
total 553 649 85.2


line stmt bran cond sub pod time code
1             package ExtUtils::Typemaps;
2 15     15   537984 use 5.006001;
  15         130  
3 15     15   86 use strict;
  15         34  
  15         428  
4 15     15   98 use warnings;
  15         61  
  15         62593  
5             our $VERSION = '3.44';
6              
7             require ExtUtils::ParseXS;
8             require ExtUtils::ParseXS::Constants;
9             require ExtUtils::Typemaps::InputMap;
10             require ExtUtils::Typemaps::OutputMap;
11             require ExtUtils::Typemaps::Type;
12              
13             =head1 NAME
14              
15             ExtUtils::Typemaps - Read/Write/Modify Perl/XS typemap files
16              
17             =head1 SYNOPSIS
18              
19             # read/create file
20             my $typemap = ExtUtils::Typemaps->new(file => 'typemap');
21             # alternatively create an in-memory typemap
22             # $typemap = ExtUtils::Typemaps->new();
23             # alternatively create an in-memory typemap by parsing a string
24             # $typemap = ExtUtils::Typemaps->new(string => $sometypemap);
25              
26             # add a mapping
27             $typemap->add_typemap(ctype => 'NV', xstype => 'T_NV');
28             $typemap->add_inputmap(
29             xstype => 'T_NV', code => '$var = ($type)SvNV($arg);'
30             );
31             $typemap->add_outputmap(
32             xstype => 'T_NV', code => 'sv_setnv($arg, (NV)$var);'
33             );
34             $typemap->add_string(string => $typemapstring);
35             # will be parsed and merged
36              
37             # remove a mapping (same for remove_typemap and remove_outputmap...)
38             $typemap->remove_inputmap(xstype => 'SomeType');
39              
40             # save a typemap to a file
41             $typemap->write(file => 'anotherfile.map');
42              
43             # merge the other typemap into this one
44             $typemap->merge(typemap => $another_typemap);
45              
46             =head1 DESCRIPTION
47              
48             This module can read, modify, create and write Perl XS typemap files. If you don't know
49             what a typemap is, please confer the L and L manuals.
50              
51             The module is not entirely round-trip safe: For example it currently simply strips all comments.
52             The order of entries in the maps is, however, preserved.
53              
54             We check for duplicate entries in the typemap, but do not check for missing
55             C entries for C or C entries since these might be hidden
56             in a different typemap.
57              
58             =head1 METHODS
59              
60             =cut
61              
62             =head2 new
63              
64             Returns a new typemap object. Takes an optional C parameter.
65             If set, the given file will be read. If the file doesn't exist, an empty typemap
66             is returned.
67              
68             Alternatively, if the C parameter is given, the supplied
69             string will be parsed instead of a file.
70              
71             =cut
72              
73             sub new {
74 77     77 1 8111 my $class = shift;
75 77         222 my %args = @_;
76              
77 77 50 66     549 if (defined $args{file} and defined $args{string}) {
78 0         0 die("Cannot handle both 'file' and 'string' arguments to constructor");
79             }
80              
81 77         902 my $self = bless {
82             file => undef,
83             %args,
84             typemap_section => [],
85             typemap_lookup => {},
86             input_section => [],
87             input_lookup => {},
88             output_section => [],
89             output_lookup => {},
90             } => $class;
91              
92 77         302 $self->_init();
93              
94 77         370 return $self;
95             }
96              
97             sub _init {
98 77     77   140 my $self = shift;
99 77 100 66     1011 if (defined $self->{string}) {
    100          
100 9         51 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
101 9         26 delete $self->{string};
102             }
103             elsif (defined $self->{file} and -e $self->{file}) {
104             open my $fh, '<', $self->{file}
105             or die "Cannot open typemap file '"
106 39 50       1542 . $self->{file} . "' for reading: $!";
107 39         274 local $/ = undef;
108 39         1231 my $string = <$fh>;
109 39         359 $self->_parse(\$string, $self->{lineno_offset}, $self->{file});
110             }
111             }
112              
113              
114             =head2 file
115              
116             Get/set the file that the typemap is written to when the
117             C method is called.
118              
119             =cut
120              
121             sub file {
122 0 0   0 1 0 $_[0]->{file} = $_[1] if @_ > 1;
123             $_[0]->{file}
124 0         0 }
125              
126             =head2 add_typemap
127              
128             Add a C entry to the typemap.
129              
130             Required named arguments: The C (e.g. C 'double'>)
131             and the C (e.g. C 'T_NV'>).
132              
133             Optional named arguments: C 1> forces removal/replacement of
134             existing C entries of the same C. C 1>
135             triggers a I<"first come first serve"> logic by which new entries that conflict
136             with existing entries are silently ignored.
137              
138             As an alternative to the named parameters usage, you may pass in
139             an C object as first argument, a copy of which will be
140             added to the typemap. In that case, only the C or C named parameters
141             may be used after the object. Example:
142              
143             $map->add_typemap($type_obj, replace => 1);
144              
145             =cut
146              
147             sub add_typemap {
148 2162     2162 1 7350 my $self = shift;
149 2162         2951 my $type;
150             my %args;
151              
152 2162 100       4213 if ((@_ % 2) == 1) {
153 2145         2710 my $orig = shift;
154 2145         3893 $type = $orig->new();
155 2145         4662 %args = @_;
156             }
157             else {
158 17         53 %args = @_;
159 17         27 my $ctype = $args{ctype};
160 17 50       54 die("Need ctype argument") if not defined $ctype;
161 17         28 my $xstype = $args{xstype};
162 17 50       50 die("Need xstype argument") if not defined $xstype;
163              
164             $type = ExtUtils::Typemaps::Type->new(
165             xstype => $xstype,
166 17         93 'prototype' => $args{'prototype'},
167             ctype => $ctype,
168             );
169             }
170              
171 2162 50 66     4181 if ($args{skip} and $args{replace}) {
172 0         0 die("Cannot use both 'skip' and 'replace'");
173             }
174              
175 2162 100       3628 if ($args{replace}) {
    100          
176 2104         4354 $self->remove_typemap(ctype => $type->ctype);
177             }
178             elsif ($args{skip}) {
179 1 50       3 return() if exists $self->{typemap_lookup}{$type->ctype};
180             }
181             else {
182 57         152 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
183             }
184              
185             # store
186 2160         2999 push @{$self->{typemap_section}}, $type;
  2160         3801  
187             # remember type for lookup, too.
188 2160         2796 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
  2160         5288  
189              
190 2160         8808 return 1;
191             }
192              
193             =head2 add_inputmap
194              
195             Add an C entry to the typemap.
196              
197             Required named arguments:
198             The C (e.g. C 'T_NV'>)
199             and the C to associate with it for input.
200              
201             Optional named arguments: C 1> forces removal/replacement of
202             existing C entries of the same C. C 1>
203             triggers a I<"first come first serve"> logic by which new entries that conflict
204             with existing entries are silently ignored.
205              
206             As an alternative to the named parameters usage, you may pass in
207             an C object as first argument, a copy of which will be
208             added to the typemap. In that case, only the C or C named parameters
209             may be used after the object. Example:
210              
211             $map->add_inputmap($type_obj, replace => 1);
212              
213             =cut
214              
215             sub add_inputmap {
216 1576     1576 1 2168 my $self = shift;
217 1576         2150 my $input;
218             my %args;
219              
220 1576 100       2893 if ((@_ % 2) == 1) {
221 1566         2067 my $orig = shift;
222 1566         3059 $input = $orig->new();
223 1566         3174 %args = @_;
224             }
225             else {
226 10         31 %args = @_;
227 10         27 my $xstype = $args{xstype};
228 10 50       41 die("Need xstype argument") if not defined $xstype;
229 10         17 my $code = $args{code};
230 10 50       22 die("Need code argument") if not defined $code;
231              
232 10         61 $input = ExtUtils::Typemaps::InputMap->new(
233             xstype => $xstype,
234             code => $code,
235             );
236             }
237              
238 1576 50 66     2942 if ($args{skip} and $args{replace}) {
239 0         0 die("Cannot use both 'skip' and 'replace'");
240             }
241              
242 1576 100       2550 if ($args{replace}) {
    100          
243 1535         2996 $self->remove_inputmap(xstype => $input->xstype);
244             }
245             elsif ($args{skip}) {
246 1 50       3 return() if exists $self->{input_lookup}{$input->xstype};
247             }
248             else {
249 40         103 $self->validate(inputmap_xstype => $input->xstype);
250             }
251              
252             # store
253 1576         2149 push @{$self->{input_section}}, $input;
  1576         2746  
254             # remember type for lookup, too.
255 1576         1975 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
  1576         3544  
256              
257 1576         3783 return 1;
258             }
259              
260             =head2 add_outputmap
261              
262             Add an C entry to the typemap.
263             Works exactly the same as C.
264              
265             =cut
266              
267             sub add_outputmap {
268 1573     1573 1 2163 my $self = shift;
269 1573         2116 my $output;
270             my %args;
271              
272 1573 100       2793 if ((@_ % 2) == 1) {
273 1565         2075 my $orig = shift;
274 1565         2871 $output = $orig->new();
275 1565         3191 %args = @_;
276             }
277             else {
278 8         22 %args = @_;
279 8         28 my $xstype = $args{xstype};
280 8 50       19 die("Need xstype argument") if not defined $xstype;
281 8         15 my $code = $args{code};
282 8 50       16 die("Need code argument") if not defined $code;
283              
284 8         39 $output = ExtUtils::Typemaps::OutputMap->new(
285             xstype => $xstype,
286             code => $code,
287             );
288             }
289              
290 1573 0 33     2835 if ($args{skip} and $args{replace}) {
291 0         0 die("Cannot use both 'skip' and 'replace'");
292             }
293              
294 1573 100       2587 if ($args{replace}) {
    50          
295 1537         3181 $self->remove_outputmap(xstype => $output->xstype);
296             }
297             elsif ($args{skip}) {
298 0 0       0 return() if exists $self->{output_lookup}{$output->xstype};
299             }
300             else {
301 36         83 $self->validate(outputmap_xstype => $output->xstype);
302             }
303              
304             # store
305 1573         2219 push @{$self->{output_section}}, $output;
  1573         2759  
306             # remember type for lookup, too.
307 1573         2026 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
  1573         3608  
308              
309 1573         3838 return 1;
310             }
311              
312             =head2 add_string
313              
314             Parses a string as a typemap and merge it into the typemap object.
315              
316             Required named argument: C to specify the string to parse.
317              
318             =cut
319              
320             sub add_string {
321 2     2 1 371 my $self = shift;
322 2         7 my %args = @_;
323 2 50       8 die("Need 'string' argument") if not defined $args{string};
324              
325             # no, this is not elegant.
326 2         7 my $other = ExtUtils::Typemaps->new(string => $args{string});
327 2         7 $self->merge(typemap => $other);
328             }
329              
330             =head2 remove_typemap
331              
332             Removes a C entry from the typemap.
333              
334             Required named argument: C to specify the entry to remove from the typemap.
335              
336             Alternatively, you may pass a single C object.
337              
338             =cut
339              
340             sub remove_typemap {
341 2104     2104 1 3009 my $self = shift;
342 2104         2637 my $ctype;
343 2104 50       3547 if (@_ > 1) {
344 2104         3827 my %args = @_;
345 2104         3120 $ctype = $args{ctype};
346 2104 50       3610 die("Need ctype argument") if not defined $ctype;
347 2104         3270 $ctype = tidy_type($ctype);
348             }
349             else {
350 0         0 $ctype = $_[0]->tidy_ctype;
351             }
352              
353 2104         4812 return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup});
354             }
355              
356             =head2 remove_inputmap
357              
358             Removes an C entry from the typemap.
359              
360             Required named argument: C to specify the entry to remove from the typemap.
361              
362             Alternatively, you may pass a single C object.
363              
364             =cut
365              
366             sub remove_inputmap {
367 1535     1535 1 2086 my $self = shift;
368 1535         1936 my $xstype;
369 1535 50       2408 if (@_ > 1) {
370 1535         2916 my %args = @_;
371 1535         2226 $xstype = $args{xstype};
372 1535 50       3141 die("Need xstype argument") if not defined $xstype;
373             }
374             else {
375 0         0 $xstype = $_[0]->xstype;
376             }
377            
378 1535         3113 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup});
379             }
380              
381             =head2 remove_inputmap
382              
383             Removes an C entry from the typemap.
384              
385             Required named argument: C to specify the entry to remove from the typemap.
386              
387             Alternatively, you may pass a single C object.
388              
389             =cut
390              
391             sub remove_outputmap {
392 1537     1537 0 2199 my $self = shift;
393 1537         1870 my $xstype;
394 1537 50       2428 if (@_ > 1) {
395 1537         2911 my %args = @_;
396 1537         2228 $xstype = $args{xstype};
397 1537 50       3075 die("Need xstype argument") if not defined $xstype;
398             }
399             else {
400 0         0 $xstype = $_[0]->xstype;
401             }
402            
403 1537         3007 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
404             }
405              
406             sub _remove {
407 5176     5176   6820 my $self = shift;
408 5176         6838 my $rm = shift;
409 5176         6543 my $array = shift;
410 5176         6470 my $lookup = shift;
411              
412             # Just fetch the index of the item from the lookup table
413 5176         7442 my $index = $lookup->{$rm};
414 5176 100       11002 return() if not defined $index;
415              
416             # Nuke the item from storage
417 1076         1776 splice(@$array, $index, 1);
418              
419             # Decrement the storage position of all items thereafter
420 1076         6957 foreach my $key (keys %$lookup) {
421 50875 100       80791 if ($lookup->{$key} > $index) {
422 47418         63901 $lookup->{$key}--;
423             }
424             }
425 1076         3424 return();
426             }
427              
428             =head2 get_typemap
429              
430             Fetches an entry of the TYPEMAP section of the typemap.
431              
432             Mandatory named arguments: The C of the entry.
433              
434             Returns the C
435             object for the entry if found.
436              
437             =cut
438              
439             sub get_typemap {
440 265     265 1 417 my $self = shift;
441 265 100       645 die("Need named parameters, got uneven number") if @_ % 2;
442              
443 264         616 my %args = @_;
444 264         433 my $ctype = $args{ctype};
445 264 50       495 die("Need ctype argument") if not defined $ctype;
446 264         484 $ctype = tidy_type($ctype);
447              
448 264         516 my $index = $self->{typemap_lookup}{$ctype};
449 264 100       528 return() if not defined $index;
450 259         716 return $self->{typemap_section}[$index];
451             }
452              
453             =head2 get_inputmap
454              
455             Fetches an entry of the INPUT section of the
456             typemap.
457              
458             Mandatory named arguments: The C of the
459             entry or the C of the typemap that can be used to find
460             the C. To wit, the following pieces of code
461             are equivalent:
462              
463             my $type = $typemap->get_typemap(ctype => $ctype)
464             my $input_map = $typemap->get_inputmap(xstype => $type->xstype);
465              
466             my $input_map = $typemap->get_inputmap(ctype => $ctype);
467              
468             Returns the C
469             object for the entry if found.
470              
471             =cut
472              
473             sub get_inputmap {
474 59     59 1 338 my $self = shift;
475 59 100       152 die("Need named parameters, got uneven number") if @_ % 2;
476              
477 58         156 my %args = @_;
478 58         111 my $xstype = $args{xstype};
479 58         104 my $ctype = $args{ctype};
480 58 50 66     124 die("Need xstype or ctype argument")
481             if not defined $xstype
482             and not defined $ctype;
483 58 50 66     239 die("Need xstype OR ctype arguments, not both")
484             if defined $xstype and defined $ctype;
485              
486 58 100       116 if (defined $ctype) {
487 2         5 my $tm = $self->get_typemap(ctype => $ctype);
488 2   66     11 $xstype = $tm && $tm->xstype;
489 2 100       10 return() if not defined $xstype;
490             }
491              
492 57         114 my $index = $self->{input_lookup}{$xstype};
493 57 100       468 return() if not defined $index;
494 55         162 return $self->{input_section}[$index];
495             }
496              
497             =head2 get_outputmap
498              
499             Fetches an entry of the OUTPUT section of the
500             typemap.
501              
502             Mandatory named arguments: The C of the
503             entry or the C of the typemap that can be used to
504             resolve the C. (See above for an example.)
505              
506             Returns the C
507             object for the entry if found.
508              
509             =cut
510              
511             sub get_outputmap {
512 91     91 1 457 my $self = shift;
513 91 100       250 die("Need named parameters, got uneven number") if @_ % 2;
514              
515 90         235 my %args = @_;
516 90         525 my $xstype = $args{xstype};
517 90         151 my $ctype = $args{ctype};
518 90 50 66     320 die("Need xstype or ctype argument")
519             if not defined $xstype
520             and not defined $ctype;
521 90 50 66     247 die("Need xstype OR ctype arguments, not both")
522             if defined $xstype and defined $ctype;
523              
524 90 100       191 if (defined $ctype) {
525 69         154 my $tm = $self->get_typemap(ctype => $ctype);
526 69   66     285 $xstype = $tm && $tm->xstype;
527 69 100       166 return() if not defined $xstype;
528             }
529              
530 89         170 my $index = $self->{output_lookup}{$xstype};
531 89 100       180 return() if not defined $index;
532 88         248 return $self->{output_section}[$index];
533             }
534              
535             =head2 write
536              
537             Write the typemap to a file. Optionally takes a C argument. If given, the
538             typemap will be written to the specified file. If not, the typemap is written
539             to the currently stored file name (see L above, this defaults to the file
540             it was read from if any).
541              
542             =cut
543              
544             sub write {
545 1     1 1 630 my $self = shift;
546 1         4 my %args = @_;
547 1 50       4 my $file = defined $args{file} ? $args{file} : $self->file();
548 1 50       5 die("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
549             if not defined $file;
550              
551 1 50       89 open my $fh, '>', $file
552             or die "Cannot open typemap file '$file' for writing: $!";
553 1         5 print $fh $self->as_string();
554 1         49 close $fh;
555             }
556              
557             =head2 as_string
558              
559             Generates and returns the string form of the typemap.
560              
561             =cut
562              
563             sub as_string {
564 24     24 1 3070 my $self = shift;
565 24         38 my $typemap = $self->{typemap_section};
566 24         35 my @code;
567 24         44 push @code, "TYPEMAP\n";
568 24         45 foreach my $entry (@$typemap) {
569             # type kind proto
570             # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
571 38 50       89 push @code, $entry->ctype . "\t" . $entry->xstype
572             . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
573             }
574              
575 24         45 my $input = $self->{input_section};
576 24 100       54 if (@$input) {
577 17         31 push @code, "\nINPUT\n";
578 17         25 foreach my $entry (@$input) {
579 33         65 push @code, $entry->xstype, "\n", $entry->code, "\n";
580             }
581             }
582              
583 24         40 my $output = $self->{output_section};
584 24 100       49 if (@$output) {
585 15         26 push @code, "\nOUTPUT\n";
586 15         21 foreach my $entry (@$output) {
587 29         103 push @code, $entry->xstype, "\n", $entry->code, "\n";
588             }
589             }
590 24         167 return join '', @code;
591             }
592              
593             =head2 as_embedded_typemap
594              
595             Generates and returns the string form of the typemap with the
596             appropriate prefix around it for verbatim inclusion into an
597             XS file as an embedded typemap. This will return a string like
598              
599             TYPEMAP: <
600             ... typemap here (see as_string) ...
601             END_OF_TYPEMAP
602              
603             The method takes care not to use a HERE-doc end marker that
604             appears in the typemap string itself.
605              
606             =cut
607              
608             sub as_embedded_typemap {
609 6     6 1 13 my $self = shift;
610 6         18 my $string = $self->as_string;
611              
612 6         14 my @ident_cand = qw(END_TYPEMAP END_OF_TYPEMAP END);
613 6         7 my $icand = 0;
614 6         10 my $cand_suffix = "";
615 6         50 while ($string =~ /^\Q$ident_cand[$icand]$cand_suffix\E\s*$/m) {
616 0         0 $icand++;
617 0 0       0 if ($icand == @ident_cand) {
618 0         0 $icand = 0;
619 0         0 ++$cand_suffix;
620             }
621             }
622              
623 6         15 my $marker = "$ident_cand[$icand]$cand_suffix";
624 6         58 return "TYPEMAP: <<$marker;\n$string\n$marker\n";
625             }
626              
627             =head2 merge
628              
629             Merges a given typemap into the object. Note that a failed merge
630             operation leaves the object in an inconsistent state so clone it if necessary.
631              
632             Mandatory named arguments: Either C $another_typemap_obj>
633             or C $path_to_typemap_file> but not both.
634              
635             Optional arguments: C 1> to force replacement
636             of existing typemap entries without warning or C 1>
637             to skip entries that exist already in the typemap.
638              
639             =cut
640              
641             sub merge {
642 39     39 1 1569 my $self = shift;
643 39         169 my %args = @_;
644              
645 39 50 66     366 if (exists $args{typemap} and exists $args{file}) {
    50 66        
646 0         0 die("Need {file} OR {typemap} argument. Not both!");
647             }
648             elsif (not exists $args{typemap} and not exists $args{file}) {
649 0         0 die("Need {file} or {typemap} argument!");
650             }
651              
652 39         71 my @params;
653 39 100       138 push @params, 'replace' => $args{replace} if exists $args{replace};
654 39 100       100 push @params, 'skip' => $args{skip} if exists $args{skip};
655              
656 39         82 my $typemap = $args{typemap};
657 39 100       94 if (not defined $typemap) {
658 25         189 $typemap = ref($self)->new(file => $args{file}, @params);
659             }
660              
661             # FIXME breaking encapsulation. Add accessor code.
662 39         75 foreach my $entry (@{$typemap->{typemap_section}}) {
  39         132  
663 1064         1889 $self->add_typemap( $entry, @params );
664             }
665              
666 38         64 foreach my $entry (@{$typemap->{input_section}}) {
  38         88  
667 773         1283 $self->add_inputmap( $entry, @params );
668             }
669              
670 38         64 foreach my $entry (@{$typemap->{output_section}}) {
  38         88  
671 773         1289 $self->add_outputmap( $entry, @params );
672             }
673              
674 38         1573 return 1;
675             }
676              
677             =head2 is_empty
678              
679             Returns a bool indicating whether this typemap is entirely empty.
680              
681             =cut
682              
683             sub is_empty {
684 5     5 1 21 my $self = shift;
685              
686             return @{ $self->{typemap_section} } == 0
687             && @{ $self->{input_section} } == 0
688 5   100     20 && @{ $self->{output_section} } == 0;
689             }
690              
691             =head2 list_mapped_ctypes
692              
693             Returns a list of the C types that are mappable by
694             this typemap object.
695              
696             =cut
697              
698             sub list_mapped_ctypes {
699 0     0 1 0 my $self = shift;
700 0         0 return sort keys %{ $self->{typemap_lookup} };
  0         0  
701             }
702              
703             =head2 _get_typemap_hash
704              
705             Returns a hash mapping the C types to the XS types:
706              
707             {
708             'char **' => 'T_PACKEDARRAY',
709             'bool_t' => 'T_IV',
710             'AV *' => 'T_AVREF',
711             'InputStream' => 'T_IN',
712             'double' => 'T_DOUBLE',
713             # ...
714             }
715              
716             This is documented because it is used by C,
717             but it's not intended for general consumption. May be removed
718             at any time.
719              
720             =cut
721              
722             sub _get_typemap_hash {
723 4     4   51 my $self = shift;
724 4         8 my $lookup = $self->{typemap_lookup};
725 4         9 my $storage = $self->{typemap_section};
726              
727 4         6 my %rv;
728 4         44 foreach my $ctype (keys %$lookup) {
729 197         345 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
730             }
731              
732 4         35 return \%rv;
733             }
734              
735             =head2 _get_inputmap_hash
736              
737             Returns a hash mapping the XS types (identifiers) to the
738             corresponding INPUT code:
739              
740             {
741             'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
742             ',
743             'T_OUT' => ' $var = IoOFP(sv_2io($arg))
744             ',
745             'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
746             # ...
747             }
748              
749             This is documented because it is used by C,
750             but it's not intended for general consumption. May be removed
751             at any time.
752              
753             =cut
754              
755             sub _get_inputmap_hash {
756 4     4   7 my $self = shift;
757 4         8 my $lookup = $self->{input_lookup};
758 4         9 my $storage = $self->{input_section};
759              
760 4         7 my %rv;
761 4         29 foreach my $xstype (keys %$lookup) {
762 97         211 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
763              
764             # Squash trailing whitespace to one line break
765             # This isn't strictly necessary, but makes the output more similar
766             # to the original ExtUtils::ParseXS.
767 97         1094 $rv{$xstype} =~ s/\s*\z/\n/;
768             }
769              
770 4         25 return \%rv;
771             }
772              
773              
774             =head2 _get_outputmap_hash
775              
776             Returns a hash mapping the XS types (identifiers) to the
777             corresponding OUTPUT code:
778              
779             {
780             'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
781             $var.context.value().size());
782             ',
783             'T_OUT' => ' {
784             GV *gv = (GV *)sv_newmortal();
785             gv_init_pvn(gv, gv_stashpvs("$Package",1),
786             "__ANONIO__",10,0);
787             if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
788             sv_setsv(
789             $arg,
790             sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))
791             );
792             else
793             $arg = &PL_sv_undef;
794             }
795             ',
796             # ...
797             }
798              
799             This is documented because it is used by C,
800             but it's not intended for general consumption. May be removed
801             at any time.
802              
803             =cut
804              
805             sub _get_outputmap_hash {
806 4     4   15 my $self = shift;
807 4         8 my $lookup = $self->{output_lookup};
808 4         8 my $storage = $self->{output_section};
809              
810 4         7 my %rv;
811 4         19 foreach my $xstype (keys %$lookup) {
812 92         196 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
813              
814             # Squash trailing whitespace to one line break
815             # This isn't strictly necessary, but makes the output more similar
816             # to the original ExtUtils::ParseXS.
817 92         664 $rv{$xstype} =~ s/\s*\z/\n/;
818             }
819              
820 4         31 return \%rv;
821             }
822              
823             =head2 _get_prototype_hash
824              
825             Returns a hash mapping the C types of the typemap to their
826             corresponding prototypes.
827              
828             {
829             'char **' => '$',
830             'bool_t' => '$',
831             'AV *' => '$',
832             'InputStream' => '$',
833             'double' => '$',
834             # ...
835             }
836              
837             This is documented because it is used by C,
838             but it's not intended for general consumption. May be removed
839             at any time.
840              
841             =cut
842              
843             sub _get_prototype_hash {
844 4     4   8 my $self = shift;
845 4         8 my $lookup = $self->{typemap_lookup};
846 4         9 my $storage = $self->{typemap_section};
847              
848 4         5 my %rv;
849 4         22 foreach my $ctype (keys %$lookup) {
850 197   50     345 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
851             }
852              
853 4         24 return \%rv;
854             }
855              
856              
857              
858             # make sure that the provided types wouldn't collide with what's
859             # in the object already.
860             sub validate {
861 133     133 0 182 my $self = shift;
862 133         292 my %args = @_;
863              
864 133 100 100     323 if ( exists $args{ctype}
865             and exists $self->{typemap_lookup}{tidy_type($args{ctype})} )
866             {
867 1         14 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
868             }
869              
870 132 50 66     347 if ( exists $args{inputmap_xstype}
871             and exists $self->{input_lookup}{$args{inputmap_xstype}} )
872             {
873 0         0 die("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
874             }
875              
876 132 50 66     314 if ( exists $args{outputmap_xstype}
877             and exists $self->{output_lookup}{$args{outputmap_xstype}} )
878             {
879 0         0 die("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
880             }
881              
882 132         242 return 1;
883             }
884              
885             =head2 clone
886              
887             Creates and returns a clone of a full typemaps object.
888              
889             Takes named parameters: If C is true,
890             the clone will share the actual individual type/input/outputmap objects,
891             but not share their storage. Use with caution. Without C,
892             the clone will be fully independent.
893              
894             =cut
895              
896             sub clone {
897 2     2 1 6 my $proto = shift;
898 2         5 my %args = @_;
899              
900 2         2 my $self;
901 2 100       6 if ($args{shallow}) {
902             $self = bless( {
903             %$proto,
904 1         2 typemap_section => [@{$proto->{typemap_section}}],
905 1         3 typemap_lookup => {%{$proto->{typemap_lookup}}},
906 1         3 input_section => [@{$proto->{input_section}}],
907 1         9 input_lookup => {%{$proto->{input_lookup}}},
908 1         4 output_section => [@{$proto->{output_section}}],
909 1         3 output_lookup => {%{$proto->{output_lookup}}},
  1         11  
910             } => ref($proto) );
911             }
912             else {
913             $self = bless( {
914             %$proto,
915 1         4 typemap_section => [map $_->new, @{$proto->{typemap_section}}],
916 1         4 typemap_lookup => {%{$proto->{typemap_lookup}}},
917 1         4 input_section => [map $_->new, @{$proto->{input_section}}],
918 1         3 input_lookup => {%{$proto->{input_lookup}}},
919 1         12 output_section => [map $_->new, @{$proto->{output_section}}],
920 1         5 output_lookup => {%{$proto->{output_lookup}}},
  1         7  
921             } => ref($proto) );
922             }
923              
924 2         7 return $self;
925             }
926              
927             =head2 tidy_type
928              
929             Function to (heuristically) canonicalize a C type. Works to some
930             degree with C++ types.
931              
932             $halfway_canonical_type = tidy_type($ctype);
933              
934             Moved from C.
935              
936             =cut
937              
938             sub tidy_type {
939 6038     6038 1 15962 local $_ = shift;
940              
941             # for templated C++ types, do some bit of flawed canonicalization
942             # wrt. templates at least
943 6038 100       12963 if (/[<>]/) {
944 4         36 s/\s*([<>])\s*/$1/g;
945 4         13 s/>>/> >/g;
946             }
947              
948             # rationalise any '*' by joining them into bunches and removing whitespace
949 6038         13851 s#\s*(\*+)\s*#$1#g;
950 6038         11904 s#(\*+)# $1 #g ;
951              
952             # trim leading & trailing whitespace
953 6038         10652 s/^\s+//; s/\s+$//;
  6038         10334  
954              
955             # change multiple whitespace into a single space
956 6038         11032 s/\s+/ /g;
957              
958 6038         14925 $_;
959             }
960              
961              
962              
963             sub _parse {
964 48     48   118 my $self = shift;
965 48         73 my $stringref = shift;
966 48         133 my $lineno_offset = shift;
967 48 100       168 $lineno_offset = 0 if not defined $lineno_offset;
968 48         82 my $filename = shift;
969 48 100       111 $filename = '' if not defined $filename;
970              
971 48         101 my $replace = $self->{replace};
972 48         83 my $skip = $self->{skip};
973 48 50 66     220 die "Can only replace OR skip" if $replace and $skip;
974 48         81 my @add_params;
975 48 100       150 push @add_params, replace => 1 if $replace;
976 48 50       98 push @add_params, skip => 1 if $skip;
977              
978             # TODO comments should round-trip, currently ignoring
979             # TODO order of sections, multiple sections of same type
980             # Heavily influenced by ExtUtils::ParseXS
981 48         92 my $section = 'typemap';
982 48         76 my $lineno = $lineno_offset;
983 48         83 my $junk = "";
984 48         82 my $current = \$junk;
985 48         98 my @input_expr;
986             my @output_expr;
987 48         424 while ($$stringref =~ /^(.*)$/gcm) {
988 7579         16538 local $_ = $1;
989 7579         9704 ++$lineno;
990 7579         9152 chomp;
991 7579 100       13683 next if /^\s*#/;
992 7467 100       16804 if (/^INPUT\s*$/) {
    100          
    100          
993 43         77 $section = 'input';
994 43         86 $current = \$junk;
995 43         132 next;
996             }
997             elsif (/^OUTPUT\s*$/) {
998 40         86 $section = 'output';
999 40         63 $current = \$junk;
1000 40         121 next;
1001             }
1002             elsif (/^TYPEMAP\s*$/) {
1003 23         38 $section = 'typemap';
1004 23         36 $current = \$junk;
1005 23         87 next;
1006             }
1007            
1008 7361 100       16272 if ($section eq 'typemap') {
    100          
    100          
    100          
1009 1168         1624 my $line = $_;
1010 1168         2177 s/^\s+//; s/\s+$//;
  1168         2293  
1011 1168 100 66     3862 next if $_ eq '' or /^#/;
1012 1081 50       6113 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
1013             or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
1014             next;
1015             # prototype defaults to '$'
1016 1081 50       2368 $proto = '$' unless $proto;
1017 1081 50       1957 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
1018             unless _valid_proto_string($proto);
1019 1081         3016 $self->add_typemap(
1020             ExtUtils::Typemaps::Type->new(
1021             xstype => $kind, proto => $proto, ctype => $type
1022             ),
1023             @add_params
1024             );
1025             } elsif (/^\s/) {
1026 4563         9408 s/\s+$//;
1027 4563 100       16990 $$current .= $$current eq '' ? $_ : "\n".$_;
1028             } elsif ($_ eq '') {
1029 45         123 next;
1030             } elsif ($section eq 'input') {
1031 793         1253 s/\s+$//;
1032 793         2373 push @input_expr, {xstype => $_, code => ''};
1033 793         3027 $current = \$input_expr[-1]{code};
1034             } else { # output section
1035 792         1300 s/\s+$//;
1036 792         2203 push @output_expr, {xstype => $_, code => ''};
1037 792         2968 $current = \$output_expr[-1]{code};
1038             }
1039              
1040             } # end while lines
1041              
1042 48         136 foreach my $inexpr (@input_expr) {
1043 793         2539 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params );
1044             }
1045 48         122 foreach my $outexpr (@output_expr) {
1046 792         2473 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params );
1047             }
1048              
1049 48         1633 return 1;
1050             }
1051              
1052             # taken from ExtUtils::ParseXS
1053             sub _valid_proto_string {
1054 1081     1081   1614 my $string = shift;
1055 1081 50       2937 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
1056 1081         2645 return $string;
1057             }
1058              
1059 0           return 0 ;
1060             }
1061              
1062             # taken from ExtUtils::ParseXS (C_string)
1063             sub _escape_backslashes {
1064 0     0     my $string = shift;
1065 0           $string =~ s[\\][\\\\]g;
1066 0           $string;
1067             }
1068              
1069             =head1 CAVEATS
1070              
1071             Inherits some evil code from C.
1072              
1073             =head1 SEE ALSO
1074              
1075             The parser is heavily inspired from the one in L.
1076              
1077             For details on typemaps: L, L.
1078              
1079             =head1 AUTHOR
1080              
1081             Steffen Mueller C<>
1082              
1083             =head1 COPYRIGHT & LICENSE
1084              
1085             Copyright 2009, 2010, 2011, 2012, 2013 Steffen Mueller
1086              
1087             This program is free software; you can redistribute it and/or
1088             modify it under the same terms as Perl itself.
1089              
1090             =cut
1091              
1092             1;
1093