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 20 21 95.2
total 554 649 85.3


line stmt bran cond sub pod time code
1             package ExtUtils::Typemaps;
2 15     15   645294 use 5.006001;
  15         138  
3 15     15   89 use strict;
  15         28  
  15         446  
4 15     15   115 use warnings;
  15         61  
  15         73451  
5             our $VERSION = '3.51';
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 9563 my $class = shift;
75 77         231 my %args = @_;
76              
77 77 50 66     405 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         911 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         316 $self->_init();
93              
94 77         350 return $self;
95             }
96              
97             sub _init {
98 77     77   158 my $self = shift;
99 77 100 66     1063 if (defined $self->{string}) {
    100          
100 9         57 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
101 9         36 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       1686 . $self->{file} . "' for reading: $!";
107 39         283 local $/ = undef;
108 39         1397 my $string = <$fh>;
109 39         379 $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 9210 my $self = shift;
149 2162         2986 my $type;
150             my %args;
151              
152 2162 100       4345 if ((@_ % 2) == 1) {
153 2145         2823 my $orig = shift;
154 2145         4114 $type = $orig->new();
155 2145         5067 %args = @_;
156             }
157             else {
158 17         60 %args = @_;
159 17         37 my $ctype = $args{ctype};
160 17 50       44 die("Need ctype argument") if not defined $ctype;
161 17         31 my $xstype = $args{xstype};
162 17 50       38 die("Need xstype argument") if not defined $xstype;
163              
164             $type = ExtUtils::Typemaps::Type->new(
165             xstype => $xstype,
166 17         89 'prototype' => $args{'prototype'},
167             ctype => $ctype,
168             );
169             }
170              
171 2162 50 66     4191 if ($args{skip} and $args{replace}) {
172 0         0 die("Cannot use both 'skip' and 'replace'");
173             }
174              
175 2162 100       3721 if ($args{replace}) {
    100          
176 2104         4466 $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         174 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
183             }
184              
185             # store
186 2160         3112 push @{$self->{typemap_section}}, $type;
  2160         4233  
187             # remember type for lookup, too.
188 2160         2925 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
  2160         5487  
189              
190 2160         8979 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 2320 my $self = shift;
217 1576         2133 my $input;
218             my %args;
219              
220 1576 100       2930 if ((@_ % 2) == 1) {
221 1566         2051 my $orig = shift;
222 1566         2897 $input = $orig->new();
223 1566         3320 %args = @_;
224             }
225             else {
226 10         33 %args = @_;
227 10         21 my $xstype = $args{xstype};
228 10 50       27 die("Need xstype argument") if not defined $xstype;
229 10         17 my $code = $args{code};
230 10 50       24 die("Need code argument") if not defined $code;
231              
232 10         50 $input = ExtUtils::Typemaps::InputMap->new(
233             xstype => $xstype,
234             code => $code,
235             );
236             }
237              
238 1576 50 66     2871 if ($args{skip} and $args{replace}) {
239 0         0 die("Cannot use both 'skip' and 'replace'");
240             }
241              
242 1576 100       2666 if ($args{replace}) {
    100          
243 1535         3113 $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         117 $self->validate(inputmap_xstype => $input->xstype);
250             }
251              
252             # store
253 1576         2204 push @{$self->{input_section}}, $input;
  1576         2878  
254             # remember type for lookup, too.
255 1576         1992 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
  1576         3793  
256              
257 1576         3810 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 2229 my $self = shift;
269 1573         2106 my $output;
270             my %args;
271              
272 1573 100       2889 if ((@_ % 2) == 1) {
273 1565         2128 my $orig = shift;
274 1565         2976 $output = $orig->new();
275 1565         3298 %args = @_;
276             }
277             else {
278 8         36 %args = @_;
279 8         31 my $xstype = $args{xstype};
280 8 50       23 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         42 $output = ExtUtils::Typemaps::OutputMap->new(
285             xstype => $xstype,
286             code => $code,
287             );
288             }
289              
290 1573 0 33     2944 if ($args{skip} and $args{replace}) {
291 0         0 die("Cannot use both 'skip' and 'replace'");
292             }
293              
294 1573 100       2652 if ($args{replace}) {
    50          
295 1537         3144 $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         109 $self->validate(outputmap_xstype => $output->xstype);
302             }
303              
304             # store
305 1573         2388 push @{$self->{output_section}}, $output;
  1573         3224  
306             # remember type for lookup, too.
307 1573         2084 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
  1573         3956  
308              
309 1573         3750 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 477 my $self = shift;
322 2         10 my %args = @_;
323 2 50       11 die("Need 'string' argument") if not defined $args{string};
324              
325             # no, this is not elegant.
326 2         8 my $other = ExtUtils::Typemaps->new(string => $args{string});
327 2         19 $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 3169 my $self = shift;
342 2104         2805 my $ctype;
343 2104 50       3953 if (@_ > 1) {
344 2104         4138 my %args = @_;
345 2104         3477 $ctype = $args{ctype};
346 2104 50       3852 die("Need ctype argument") if not defined $ctype;
347 2104         3274 $ctype = tidy_type($ctype);
348             }
349             else {
350 0         0 $ctype = $_[0]->tidy_ctype;
351             }
352              
353 2104         4951 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 2185 my $self = shift;
368 1535         1909 my $xstype;
369 1535 50       2581 if (@_ > 1) {
370 1535         2992 my %args = @_;
371 1535         2217 $xstype = $args{xstype};
372 1535 50       3264 die("Need xstype argument") if not defined $xstype;
373             }
374             else {
375 0         0 $xstype = $_[0]->xstype;
376             }
377            
378 1535         2988 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup});
379             }
380              
381             =head2 remove_outputmap
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 1 2108 my $self = shift;
393 1537         1976 my $xstype;
394 1537 50       2455 if (@_ > 1) {
395 1537         3067 my %args = @_;
396 1537         2171 $xstype = $args{xstype};
397 1537 50       3266 die("Need xstype argument") if not defined $xstype;
398             }
399             else {
400 0         0 $xstype = $_[0]->xstype;
401             }
402            
403 1537         3064 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
404             }
405              
406             sub _remove {
407 5176     5176   7228 my $self = shift;
408 5176         7047 my $rm = shift;
409 5176         6558 my $array = shift;
410 5176         6735 my $lookup = shift;
411              
412             # Just fetch the index of the item from the lookup table
413 5176         7665 my $index = $lookup->{$rm};
414 5176 100       10797 return() if not defined $index;
415              
416             # Nuke the item from storage
417 1076         1736 splice(@$array, $index, 1);
418              
419             # Decrement the storage position of all items thereafter
420 1076         6693 foreach my $key (keys %$lookup) {
421 50875 100       80981 if ($lookup->{$key} > $index) {
422 47418         65542 $lookup->{$key}--;
423             }
424             }
425 1076         3488 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 477 my $self = shift;
441 265 100       570 die("Need named parameters, got uneven number") if @_ % 2;
442              
443 264         651 my %args = @_;
444 264         452 my $ctype = $args{ctype};
445 264 50       495 die("Need ctype argument") if not defined $ctype;
446 264         471 $ctype = tidy_type($ctype);
447              
448 264         546 my $index = $self->{typemap_lookup}{$ctype};
449 264 100       542 return() if not defined $index;
450 259         786 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 398 my $self = shift;
475 59 100       141 die("Need named parameters, got uneven number") if @_ % 2;
476              
477 58         171 my %args = @_;
478 58         96 my $xstype = $args{xstype};
479 58         97 my $ctype = $args{ctype};
480 58 50 66     127 die("Need xstype or ctype argument")
481             if not defined $xstype
482             and not defined $ctype;
483 58 50 66     230 die("Need xstype OR ctype arguments, not both")
484             if defined $xstype and defined $ctype;
485              
486 58 100       118 if (defined $ctype) {
487 2         11 my $tm = $self->get_typemap(ctype => $ctype);
488 2   66     13 $xstype = $tm && $tm->xstype;
489 2 100       36 return() if not defined $xstype;
490             }
491              
492 57         130 my $index = $self->{input_lookup}{$xstype};
493 57 100       131 return() if not defined $index;
494 55         166 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 447 my $self = shift;
513 91 100       251 die("Need named parameters, got uneven number") if @_ % 2;
514              
515 90         243 my %args = @_;
516 90         173 my $xstype = $args{xstype};
517 90         141 my $ctype = $args{ctype};
518 90 50 66     338 die("Need xstype or ctype argument")
519             if not defined $xstype
520             and not defined $ctype;
521 90 50 66     248 die("Need xstype OR ctype arguments, not both")
522             if defined $xstype and defined $ctype;
523              
524 90 100       190 if (defined $ctype) {
525 69         153 my $tm = $self->get_typemap(ctype => $ctype);
526 69   66     318 $xstype = $tm && $tm->xstype;
527 69 100       166 return() if not defined $xstype;
528             }
529              
530 89         196 my $index = $self->{output_lookup}{$xstype};
531 89 100       187 return() if not defined $index;
532 88         257 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 708 my $self = shift;
546 1         6 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       88 open my $fh, '>', $file
552             or die "Cannot open typemap file '$file' for writing: $!";
553 1         8 print $fh $self->as_string();
554 1         69 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 3638 my $self = shift;
565 24         43 my $typemap = $self->{typemap_section};
566 24         38 my @code;
567 24         50 push @code, "TYPEMAP\n";
568 24         56 foreach my $entry (@$typemap) {
569             # type kind proto
570             # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
571 38 50       99 push @code, $entry->ctype . "\t" . $entry->xstype
572             . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
573             }
574              
575 24         55 my $input = $self->{input_section};
576 24 100       56 if (@$input) {
577 17         31 push @code, "\nINPUT\n";
578 17         32 foreach my $entry (@$input) {
579 33         77 push @code, $entry->xstype, "\n", $entry->code, "\n";
580             }
581             }
582              
583 24         57 my $output = $self->{output_section};
584 24 100       65 if (@$output) {
585 15         28 push @code, "\nOUTPUT\n";
586 15         28 foreach my $entry (@$output) {
587 29         70 push @code, $entry->xstype, "\n", $entry->code, "\n";
588             }
589             }
590 24         194 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 16 my $self = shift;
610 6         19 my $string = $self->as_string;
611              
612 6         17 my @ident_cand = qw(END_TYPEMAP END_OF_TYPEMAP END);
613 6         8 my $icand = 0;
614 6         12 my $cand_suffix = "";
615 6         61 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         17 my $marker = "$ident_cand[$icand]$cand_suffix";
624 6         75 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 1903 my $self = shift;
643 39         170 my %args = @_;
644              
645 39 50 66     394 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         74 my @params;
653 39 100       160 push @params, 'replace' => $args{replace} if exists $args{replace};
654 39 100       95 push @params, 'skip' => $args{skip} if exists $args{skip};
655              
656 39         69 my $typemap = $args{typemap};
657 39 100       123 if (not defined $typemap) {
658 25         208 $typemap = ref($self)->new(file => $args{file}, @params);
659             }
660              
661             # FIXME breaking encapsulation. Add accessor code.
662 39         73 foreach my $entry (@{$typemap->{typemap_section}}) {
  39         125  
663 1064         1958 $self->add_typemap( $entry, @params );
664             }
665              
666 38         80 foreach my $entry (@{$typemap->{input_section}}) {
  38         89  
667 773         1427 $self->add_inputmap( $entry, @params );
668             }
669              
670 38         82 foreach my $entry (@{$typemap->{output_section}}) {
  38         124  
671 773         1358 $self->add_outputmap( $entry, @params );
672             }
673              
674 38         1287 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 25 my $self = shift;
685              
686             return @{ $self->{typemap_section} } == 0
687             && @{ $self->{input_section} } == 0
688 5   100     6 && @{ $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   39 my $self = shift;
724 4         11 my $lookup = $self->{typemap_lookup};
725 4         6 my $storage = $self->{typemap_section};
726              
727 4         8 my %rv;
728 4         32 foreach my $ctype (keys %$lookup) {
729 197         391 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
730             }
731              
732 4         37 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   9 my $self = shift;
757 4         8 my $lookup = $self->{input_lookup};
758 4         8 my $storage = $self->{input_section};
759              
760 4         5 my %rv;
761 4         22 foreach my $xstype (keys %$lookup) {
762 97         241 $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         1247 $rv{$xstype} =~ s/\s*\z/\n/;
768             }
769              
770 4         22 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   8 my $self = shift;
807 4         15 my $lookup = $self->{output_lookup};
808 4         13 my $storage = $self->{output_section};
809              
810 4         6 my %rv;
811 4         20 foreach my $xstype (keys %$lookup) {
812 92         274 $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         749 $rv{$xstype} =~ s/\s*\z/\n/;
818             }
819              
820 4         24 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   10 my $self = shift;
845 4         7 my $lookup = $self->{typemap_lookup};
846 4         7 my $storage = $self->{typemap_section};
847              
848 4         5 my %rv;
849 4         31 foreach my $ctype (keys %$lookup) {
850 197   50     393 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
851             }
852              
853 4         25 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 211 my $self = shift;
862 133         353 my %args = @_;
863              
864 133 100 100     395 if ( exists $args{ctype}
865             and exists $self->{typemap_lookup}{tidy_type($args{ctype})} )
866             {
867 1         15 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
868             }
869              
870 132 50 66     460 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     381 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         263 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 10 my $proto = shift;
898 2         8 my %args = @_;
899              
900 2         2 my $self;
901 2 100       9 if ($args{shallow}) {
902             $self = bless( {
903             %$proto,
904 1         3 typemap_section => [@{$proto->{typemap_section}}],
905 1         3 typemap_lookup => {%{$proto->{typemap_lookup}}},
906 1         3 input_section => [@{$proto->{input_section}}],
907 1         10 input_lookup => {%{$proto->{input_lookup}}},
908 1         4 output_section => [@{$proto->{output_section}}],
909 1         4 output_lookup => {%{$proto->{output_lookup}}},
  1         6  
910             } => ref($proto) );
911             }
912             else {
913             $self = bless( {
914             %$proto,
915 1         6 typemap_section => [map $_->new, @{$proto->{typemap_section}}],
916 1         6 typemap_lookup => {%{$proto->{typemap_lookup}}},
917 1         4 input_section => [map $_->new, @{$proto->{input_section}}],
918 1         4 input_lookup => {%{$proto->{input_lookup}}},
919 1         3 output_section => [map $_->new, @{$proto->{output_section}}],
920 1         5 output_lookup => {%{$proto->{output_lookup}}},
  1         25  
921             } => ref($proto) );
922             }
923              
924 2         10 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 18489 local $_ = shift;
940              
941             # for templated C++ types, do some bit of flawed canonicalization
942             # wrt. templates at least
943 6038 100       13457 if (/[<>]/) {
944 4         30 s/\s*([<>])\s*/$1/g;
945 4         11 s/>>/> >/g;
946             }
947              
948             # rationalise any '*' by joining them into bunches and removing whitespace
949 6038         14223 s#\s*(\*+)\s*#$1#g;
950 6038         12258 s#(\*+)# $1 #g ;
951              
952             # trim leading & trailing whitespace
953 6038         11015 s/^\s+//; s/\s+$//;
  6038         11020  
954              
955             # change multiple whitespace into a single space
956 6038         11312 s/\s+/ /g;
957              
958 6038         15295 $_;
959             }
960              
961              
962              
963             sub _parse {
964 48     48   120 my $self = shift;
965 48         78 my $stringref = shift;
966 48         128 my $lineno_offset = shift;
967 48 100       165 $lineno_offset = 0 if not defined $lineno_offset;
968 48         105 my $filename = shift;
969 48 100       119 $filename = '' if not defined $filename;
970              
971 48         85 my $replace = $self->{replace};
972 48         92 my $skip = $self->{skip};
973 48 50 66     204 die "Can only replace OR skip" if $replace and $skip;
974 48         72 my @add_params;
975 48 100       156 push @add_params, replace => 1 if $replace;
976 48 50       112 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         107 my $section = 'typemap';
982 48         95 my $lineno = $lineno_offset;
983 48         94 my $junk = "";
984 48         86 my $current = \$junk;
985 48         99 my @input_expr;
986             my @output_expr;
987 48         443 while ($$stringref =~ /^(.*)$/gcm) {
988 7579         17155 local $_ = $1;
989 7579         9560 ++$lineno;
990 7579         9582 chomp;
991 7579 100       14479 next if /^\s*#/;
992 7467 100       17345 if (/^INPUT\s*$/) {
    100          
    100          
993 43         80 $section = 'input';
994 43         75 $current = \$junk;
995 43         137 next;
996             }
997             elsif (/^OUTPUT\s*$/) {
998 40         65 $section = 'output';
999 40         82 $current = \$junk;
1000 40         126 next;
1001             }
1002             elsif (/^TYPEMAP\s*$/) {
1003 23         56 $section = 'typemap';
1004 23         41 $current = \$junk;
1005 23         93 next;
1006             }
1007            
1008 7361 100       16957 if ($section eq 'typemap') {
    100          
    100          
    100          
1009 1168         1688 my $line = $_;
1010 1168         2223 s/^\s+//; s/\s+$//;
  1168         2365  
1011 1168 100 66     4081 next if $_ eq '' or /^#/;
1012 1081 50       6507 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       2412 $proto = '$' unless $proto;
1017 1081 50       2006 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
1018             unless _valid_proto_string($proto);
1019 1081         3150 $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         9646 s/\s+$//;
1027 4563 100       17502 $$current .= $$current eq '' ? $_ : "\n".$_;
1028             } elsif ($_ eq '') {
1029 45         151 next;
1030             } elsif ($section eq 'input') {
1031 793         1296 s/\s+$//;
1032 793         2355 push @input_expr, {xstype => $_, code => ''};
1033 793         3002 $current = \$input_expr[-1]{code};
1034             } else { # output section
1035 792         1412 s/\s+$//;
1036 792         2410 push @output_expr, {xstype => $_, code => ''};
1037 792         3010 $current = \$output_expr[-1]{code};
1038             }
1039              
1040             } # end while lines
1041              
1042 48         149 foreach my $inexpr (@input_expr) {
1043 793         2628 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params );
1044             }
1045 48         110 foreach my $outexpr (@output_expr) {
1046 792         2540 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params );
1047             }
1048              
1049 48         1240 return 1;
1050             }
1051              
1052             # taken from ExtUtils::ParseXS
1053             sub _valid_proto_string {
1054 1081     1081   1715 my $string = shift;
1055 1081 50       3187 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
1056 1081         2700 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