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   646527 use 5.006001;
  15         134  
3 15     15   130 use strict;
  15         65  
  15         577  
4 15     15   83 use warnings;
  15         29  
  15         71593  
5             our $VERSION = '3.43_02';
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 9700 my $class = shift;
75 77         455 my %args = @_;
76              
77 77 50 66     440 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         1070 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         367 $self->_init();
93              
94 77         380 return $self;
95             }
96              
97             sub _init {
98 77     77   159 my $self = shift;
99 77 100 66     1208 if (defined $self->{string}) {
    100          
100 9         54 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
101 9         29 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       1877 . $self->{file} . "' for reading: $!";
107 39         337 local $/ = undef;
108 39         1317 my $string = <$fh>;
109 39         419 $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 8982 my $self = shift;
149 2162         3182 my $type;
150             my %args;
151              
152 2162 100       4524 if ((@_ % 2) == 1) {
153 2145         2944 my $orig = shift;
154 2145         4283 $type = $orig->new();
155 2145         4938 %args = @_;
156             }
157             else {
158 17         79 %args = @_;
159 17         40 my $ctype = $args{ctype};
160 17 50       51 die("Need ctype argument") if not defined $ctype;
161 17         34 my $xstype = $args{xstype};
162 17 50       48 die("Need xstype argument") if not defined $xstype;
163              
164             $type = ExtUtils::Typemaps::Type->new(
165             xstype => $xstype,
166 17         112 'prototype' => $args{'prototype'},
167             ctype => $ctype,
168             );
169             }
170              
171 2162 50 66     4251 if ($args{skip} and $args{replace}) {
172 0         0 die("Cannot use both 'skip' and 'replace'");
173             }
174              
175 2162 100       3634 if ($args{replace}) {
    100          
176 2104         4453 $self->remove_typemap(ctype => $type->ctype);
177             }
178             elsif ($args{skip}) {
179 1 50       4 return() if exists $self->{typemap_lookup}{$type->ctype};
180             }
181             else {
182 57         177 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
183             }
184              
185             # store
186 2160         3159 push @{$self->{typemap_section}}, $type;
  2160         4435  
187             # remember type for lookup, too.
188 2160         3001 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
  2160         5494  
189              
190 2160         9267 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 2255 my $self = shift;
217 1576         2185 my $input;
218             my %args;
219              
220 1576 100       3137 if ((@_ % 2) == 1) {
221 1566         2108 my $orig = shift;
222 1566         3019 $input = $orig->new();
223 1566         3418 %args = @_;
224             }
225             else {
226 10         51 %args = @_;
227 10         27 my $xstype = $args{xstype};
228 10 50       37 die("Need xstype argument") if not defined $xstype;
229 10         17 my $code = $args{code};
230 10 50       28 die("Need code argument") if not defined $code;
231              
232 10         65 $input = ExtUtils::Typemaps::InputMap->new(
233             xstype => $xstype,
234             code => $code,
235             );
236             }
237              
238 1576 50 66     3036 if ($args{skip} and $args{replace}) {
239 0         0 die("Cannot use both 'skip' and 'replace'");
240             }
241              
242 1576 100       2629 if ($args{replace}) {
    100          
243 1535         3175 $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         2201 push @{$self->{input_section}}, $input;
  1576         3023  
254             # remember type for lookup, too.
255 1576         2127 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
  1576         3716  
256              
257 1576         3861 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 2294 my $self = shift;
269 1573         2223 my $output;
270             my %args;
271              
272 1573 100       2950 if ((@_ % 2) == 1) {
273 1565         2075 my $orig = shift;
274 1565         2922 $output = $orig->new();
275 1565         3321 %args = @_;
276             }
277             else {
278 8         28 %args = @_;
279 8         18 my $xstype = $args{xstype};
280 8 50       31 die("Need xstype argument") if not defined $xstype;
281 8         18 my $code = $args{code};
282 8 50       31 die("Need code argument") if not defined $code;
283              
284 8         47 $output = ExtUtils::Typemaps::OutputMap->new(
285             xstype => $xstype,
286             code => $code,
287             );
288             }
289              
290 1573 0 33     3008 if ($args{skip} and $args{replace}) {
291 0         0 die("Cannot use both 'skip' and 'replace'");
292             }
293              
294 1573 100       2571 if ($args{replace}) {
    50          
295 1537         3180 $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         116 $self->validate(outputmap_xstype => $output->xstype);
302             }
303              
304             # store
305 1573         2225 push @{$self->{output_section}}, $output;
  1573         2894  
306             # remember type for lookup, too.
307 1573         2060 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
  1573         3716  
308              
309 1573         3870 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 457 my $self = shift;
322 2         8 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         9 $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 3153 my $self = shift;
342 2104         2833 my $ctype;
343 2104 50       3798 if (@_ > 1) {
344 2104         4096 my %args = @_;
345 2104         3313 $ctype = $args{ctype};
346 2104 50       3979 die("Need ctype argument") if not defined $ctype;
347 2104         3261 $ctype = tidy_type($ctype);
348             }
349             else {
350 0         0 $ctype = $_[0]->tidy_ctype;
351             }
352              
353 2104         5001 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 2229 my $self = shift;
368 1535         1932 my $xstype;
369 1535 50       2549 if (@_ > 1) {
370 1535         3117 my %args = @_;
371 1535         2209 $xstype = $args{xstype};
372 1535 50       3267 die("Need xstype argument") if not defined $xstype;
373             }
374             else {
375 0         0 $xstype = $_[0]->xstype;
376             }
377            
378 1535         3056 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 2172 my $self = shift;
393 1537         1875 my $xstype;
394 1537 50       2562 if (@_ > 1) {
395 1537         3086 my %args = @_;
396 1537         2273 $xstype = $args{xstype};
397 1537 50       3317 die("Need xstype argument") if not defined $xstype;
398             }
399             else {
400 0         0 $xstype = $_[0]->xstype;
401             }
402            
403 1537         3082 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
404             }
405              
406             sub _remove {
407 5176     5176   7150 my $self = shift;
408 5176         7296 my $rm = shift;
409 5176         6654 my $array = shift;
410 5176         6512 my $lookup = shift;
411              
412             # Just fetch the index of the item from the lookup table
413 5176         7813 my $index = $lookup->{$rm};
414 5176 100       10849 return() if not defined $index;
415              
416             # Nuke the item from storage
417 1076         1717 splice(@$array, $index, 1);
418              
419             # Decrement the storage position of all items thereafter
420 1076         6814 foreach my $key (keys %$lookup) {
421 50875 100       81655 if ($lookup->{$key} > $index) {
422 47418         65519 $lookup->{$key}--;
423             }
424             }
425 1076         3464 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 246     246 1 391 my $self = shift;
441 246 100       535 die("Need named parameters, got uneven number") if @_ % 2;
442              
443 245         604 my %args = @_;
444 245         420 my $ctype = $args{ctype};
445 245 50       459 die("Need ctype argument") if not defined $ctype;
446 245         670 $ctype = tidy_type($ctype);
447              
448 245         508 my $index = $self->{typemap_lookup}{$ctype};
449 245 100       479 return() if not defined $index;
450 240         743 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 55     55 1 355 my $self = shift;
475 55 100       160 die("Need named parameters, got uneven number") if @_ % 2;
476              
477 54         158 my %args = @_;
478 54         107 my $xstype = $args{xstype};
479 54         91 my $ctype = $args{ctype};
480 54 50 66     127 die("Need xstype or ctype argument")
481             if not defined $xstype
482             and not defined $ctype;
483 54 50 66     238 die("Need xstype OR ctype arguments, not both")
484             if defined $xstype and defined $ctype;
485              
486 54 100       130 if (defined $ctype) {
487 2         6 my $tm = $self->get_typemap(ctype => $ctype);
488 2   66     13 $xstype = $tm && $tm->xstype;
489 2 100       13 return() if not defined $xstype;
490             }
491              
492 53         124 my $index = $self->{input_lookup}{$xstype};
493 53 100       119 return() if not defined $index;
494 51         152 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 84     84 1 436 my $self = shift;
513 84 100       221 die("Need named parameters, got uneven number") if @_ % 2;
514              
515 83         247 my %args = @_;
516 83         174 my $xstype = $args{xstype};
517 83         141 my $ctype = $args{ctype};
518 83 50 66     337 die("Need xstype or ctype argument")
519             if not defined $xstype
520             and not defined $ctype;
521 83 50 66     229 die("Need xstype OR ctype arguments, not both")
522             if defined $xstype and defined $ctype;
523              
524 83 100       169 if (defined $ctype) {
525 65         149 my $tm = $self->get_typemap(ctype => $ctype);
526 65   66     295 $xstype = $tm && $tm->xstype;
527 65 100       165 return() if not defined $xstype;
528             }
529              
530 82         197 my $index = $self->{output_lookup}{$xstype};
531 82 100       171 return() if not defined $index;
532 81         214 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 756 my $self = shift;
546 1         4 my %args = @_;
547 1 50       5 my $file = defined $args{file} ? $args{file} : $self->file();
548 1 50       3 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       73 open my $fh, '>', $file
552             or die "Cannot open typemap file '$file' for writing: $!";
553 1         5 print $fh $self->as_string();
554 1         48 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 3691 my $self = shift;
565 24         49 my $typemap = $self->{typemap_section};
566 24         44 my @code;
567 24         54 push @code, "TYPEMAP\n";
568 24         55 foreach my $entry (@$typemap) {
569             # type kind proto
570             # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
571 38 50       105 push @code, $entry->ctype . "\t" . $entry->xstype
572             . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
573             }
574              
575 24         48 my $input = $self->{input_section};
576 24 100       67 if (@$input) {
577 17         39 push @code, "\nINPUT\n";
578 17         32 foreach my $entry (@$input) {
579 33         88 push @code, $entry->xstype, "\n", $entry->code, "\n";
580             }
581             }
582              
583 24         58 my $output = $self->{output_section};
584 24 100       59 if (@$output) {
585 15         29 push @code, "\nOUTPUT\n";
586 15         30 foreach my $entry (@$output) {
587 29         73 push @code, $entry->xstype, "\n", $entry->code, "\n";
588             }
589             }
590 24         180 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 15 my $self = shift;
610 6         21 my $string = $self->as_string;
611              
612 6         18 my @ident_cand = qw(END_TYPEMAP END_OF_TYPEMAP END);
613 6         9 my $icand = 0;
614 6         11 my $cand_suffix = "";
615 6         58 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         80 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 1943 my $self = shift;
643 39         202 my %args = @_;
644              
645 39 50 66     428 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         86 my @params;
653 39 100       156 push @params, 'replace' => $args{replace} if exists $args{replace};
654 39 100       124 push @params, 'skip' => $args{skip} if exists $args{skip};
655              
656 39         78 my $typemap = $args{typemap};
657 39 100       112 if (not defined $typemap) {
658 25         163 $typemap = ref($self)->new(file => $args{file}, @params);
659             }
660              
661             # FIXME breaking encapsulation. Add accessor code.
662 39         82 foreach my $entry (@{$typemap->{typemap_section}}) {
  39         273  
663 1064         1934 $self->add_typemap( $entry, @params );
664             }
665              
666 38         70 foreach my $entry (@{$typemap->{input_section}}) {
  38         104  
667 773         1344 $self->add_inputmap( $entry, @params );
668             }
669              
670 38         135 foreach my $entry (@{$typemap->{output_section}}) {
  38         114  
671 773         1341 $self->add_outputmap( $entry, @params );
672             }
673              
674 38         1709 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     10 && @{ $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   29 my $self = shift;
724 4         6 my $lookup = $self->{typemap_lookup};
725 4         8 my $storage = $self->{typemap_section};
726              
727 4         7 my %rv;
728 4         28 foreach my $ctype (keys %$lookup) {
729 197         359 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
730             }
731              
732 4         25 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   15 my $self = shift;
757 4         8 my $lookup = $self->{input_lookup};
758 4         6 my $storage = $self->{input_section};
759              
760 4         8 my %rv;
761 4         16 foreach my $xstype (keys %$lookup) {
762 97         224 $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         1187 $rv{$xstype} =~ s/\s*\z/\n/;
768             }
769              
770 4         24 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   12 my $self = shift;
807 4         7 my $lookup = $self->{output_lookup};
808 4         24 my $storage = $self->{output_section};
809              
810 4         7 my %rv;
811 4         18 foreach my $xstype (keys %$lookup) {
812 92         206 $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         766 $rv{$xstype} =~ s/\s*\z/\n/;
818             }
819              
820 4         27 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         7 my $lookup = $self->{typemap_lookup};
846 4         8 my $storage = $self->{typemap_section};
847              
848 4         5 my %rv;
849 4         23 foreach my $ctype (keys %$lookup) {
850 197   50     390 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
851             }
852              
853 4         21 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 273 my $self = shift;
862 133         354 my %args = @_;
863              
864 133 100 100     396 if ( exists $args{ctype}
865             and exists $self->{typemap_lookup}{tidy_type($args{ctype})} )
866             {
867 1         17 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
868             }
869              
870 132 50 66     420 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     366 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         275 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         4 my %args = @_;
899              
900 2         4 my $self;
901 2 100       8 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         3 input_lookup => {%{$proto->{input_lookup}}},
908 1         2 output_section => [@{$proto->{output_section}}],
909 1         4 output_lookup => {%{$proto->{output_lookup}}},
  1         21  
910             } => ref($proto) );
911             }
912             else {
913             $self = bless( {
914             %$proto,
915 1         7 typemap_section => [map $_->new, @{$proto->{typemap_section}}],
916 1         15 typemap_lookup => {%{$proto->{typemap_lookup}}},
917 1         7 input_section => [map $_->new, @{$proto->{input_section}}],
918 1         4 input_lookup => {%{$proto->{input_lookup}}},
919 1         5 output_section => [map $_->new, @{$proto->{output_section}}],
920 1         6 output_lookup => {%{$proto->{output_lookup}}},
  1         8  
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 6010     6010 1 17106 local $_ = shift;
940              
941             # for templated C++ types, do some bit of flawed canonicalization
942             # wrt. templates at least
943 6010 100       13480 if (/[<>]/) {
944 4         32 s/\s*([<>])\s*/$1/g;
945 4         13 s/>>/> >/g;
946             }
947              
948             # rationalise any '*' by joining them into bunches and removing whitespace
949 6010         14181 s#\s*(\*+)\s*#$1#g;
950 6010         12455 s#(\*+)# $1 #g ;
951              
952             # trim leading & trailing whitespace
953 6010         11184 s/^\s+//; s/\s+$//;
  6010         11046  
954              
955             # change multiple whitespace into a single space
956 6010         11376 s/\s+/ /g;
957              
958 6010         15567 $_;
959             }
960              
961              
962              
963             sub _parse {
964 48     48   107 my $self = shift;
965 48         87 my $stringref = shift;
966 48         130 my $lineno_offset = shift;
967 48 100       177 $lineno_offset = 0 if not defined $lineno_offset;
968 48         112 my $filename = shift;
969 48 100       136 $filename = '' if not defined $filename;
970              
971 48         116 my $replace = $self->{replace};
972 48         95 my $skip = $self->{skip};
973 48 50 66     283 die "Can only replace OR skip" if $replace and $skip;
974 48         93 my @add_params;
975 48 100       166 push @add_params, replace => 1 if $replace;
976 48 50       118 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         111 my $section = 'typemap';
982 48         84 my $lineno = $lineno_offset;
983 48         94 my $junk = "";
984 48         88 my $current = \$junk;
985 48         107 my @input_expr;
986             my @output_expr;
987 48         515 while ($$stringref =~ /^(.*)$/gcm) {
988 7579         17272 local $_ = $1;
989 7579         10158 ++$lineno;
990 7579         9651 chomp;
991 7579 100       14352 next if /^\s*#/;
992 7467 100       17435 if (/^INPUT\s*$/) {
    100          
    100          
993 43         86 $section = 'input';
994 43         82 $current = \$junk;
995 43         140 next;
996             }
997             elsif (/^OUTPUT\s*$/) {
998 40         86 $section = 'output';
999 40         74 $current = \$junk;
1000 40         130 next;
1001             }
1002             elsif (/^TYPEMAP\s*$/) {
1003 23         44 $section = 'typemap';
1004 23         48 $current = \$junk;
1005 23         103 next;
1006             }
1007            
1008 7361 100       17692 if ($section eq 'typemap') {
    100          
    100          
    100          
1009 1168         1725 my $line = $_;
1010 1168         2259 s/^\s+//; s/\s+$//;
  1168         2598  
1011 1168 100 66     4347 next if $_ eq '' or /^#/;
1012 1081 50       6762 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       2569 $proto = '$' unless $proto;
1017 1081 50       2040 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
1018             unless _valid_proto_string($proto);
1019 1081         3278 $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         10289 s/\s+$//;
1027 4563 100       17944 $$current .= $$current eq '' ? $_ : "\n".$_;
1028             } elsif ($_ eq '') {
1029 45         161 next;
1030             } elsif ($section eq 'input') {
1031 793         1343 s/\s+$//;
1032 793         2428 push @input_expr, {xstype => $_, code => ''};
1033 793         3115 $current = \$input_expr[-1]{code};
1034             } else { # output section
1035 792         1358 s/\s+$//;
1036 792         2331 push @output_expr, {xstype => $_, code => ''};
1037 792         3039 $current = \$output_expr[-1]{code};
1038             }
1039              
1040             } # end while lines
1041              
1042 48         132 foreach my $inexpr (@input_expr) {
1043 793         2749 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params );
1044             }
1045 48         109 foreach my $outexpr (@output_expr) {
1046 792         2558 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params );
1047             }
1048              
1049 48         1361 return 1;
1050             }
1051              
1052             # taken from ExtUtils::ParseXS
1053             sub _valid_proto_string {
1054 1081     1081   1781 my $string = shift;
1055 1081 50       3237 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
1056 1081         2777 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