1 package ExtUtils::Typemaps;
8 require ExtUtils::ParseXS;
9 require ExtUtils::ParseXS::Constants;
10 require ExtUtils::Typemaps::InputMap;
11 require ExtUtils::Typemaps::OutputMap;
12 require ExtUtils::Typemaps::Type;
16 ExtUtils::Typemaps - Read/Write/Modify Perl/XS typemap files
21 my $typemap = ExtUtils::Typemaps->new(file => 'typemap');
22 # alternatively create an in-memory typemap
23 # $typemap = ExtUtils::Typemaps->new();
24 # alternatively create an in-memory typemap by parsing a string
25 # $typemap = ExtUtils::Typemaps->new(string => $sometypemap);
28 $typemap->add_typemap(ctype => 'NV', xstype => 'T_NV');
29 $typemap->add_inputmap (xstype => 'T_NV', code => '$var = ($type)SvNV($arg);');
30 $typemap->add_outputmap(xstype => 'T_NV', code => 'sv_setnv($arg, (NV)$var);');
31 $typemap->add_string(string => $typemapstring); # will be parsed and merged
33 # remove a mapping (same for remove_typemap and remove_outputmap...)
34 $typemap->remove_inputmap(xstype => 'SomeType');
36 # save a typemap to a file
37 $typemap->write(file => 'anotherfile.map');
39 # merge the other typemap into this one
40 $typemap->merge(typemap => $another_typemap);
44 This module can read, modify, create and write Perl XS typemap files. If you don't know
45 what a typemap is, please confer the L<perlxstut> and L<perlxs> manuals.
47 The module is not entirely round-trip safe: For example it currently simply strips all comments.
48 The order of entries in the maps is, however, preserved.
50 We check for duplicate entries in the typemap, but do not check for missing
51 C<TYPEMAP> entries for C<INPUTMAP> or C<OUTPUTMAP> entries since these might be hidden
52 in a different typemap.
60 Returns a new typemap object. Takes an optional C<file> parameter.
61 If set, the given file will be read. If the file doesn't exist, an empty typemap
64 Alternatively, if the C<string> parameter is given, the supplied
65 string will be parsed instead of a file.
73 if (defined $args{file} and defined $args{string}) {
74 die("Cannot handle both 'file' and 'string' arguments to constructor");
80 typemap_section => [],
95 if (defined $self->{string}) {
96 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
97 delete $self->{string};
99 elsif (defined $self->{file} and -e $self->{file}) {
100 open my $fh, '<', $self->{file}
101 or die "Cannot open typemap file '"
102 . $self->{file} . "' for reading: $!";
105 $self->_parse(\$string, $self->{lineno_offset}, $self->{file});
111 Get/set the file that the typemap is written to when the
112 C<write> method is called.
117 $_[0]->{file} = $_[1] if @_ > 1;
123 Add a C<TYPEMAP> entry to the typemap.
125 Required named arguments: The C<ctype> (e.g. C<ctype =E<gt> 'double'>)
126 and the C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>).
128 Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of
129 existing C<TYPEMAP> entries of the same C<ctype>. C<skip =E<gt> 1>
130 triggers a I<"first come first serve"> logic by which new entries that conflict
131 with existing entries are silently ignored.
133 As an alternative to the named parameters usage, you may pass in
134 an C<ExtUtils::Typemaps::Type> object as first argument, a copy of which will be
135 added to the typemap. In that case, only the C<replace> or C<skip> named parameters
136 may be used after the object. Example:
138 $map->add_typemap($type_obj, replace => 1);
149 $type = $orig->new();
154 my $ctype = $args{ctype};
155 die("Need ctype argument") if not defined $ctype;
156 my $xstype = $args{xstype};
157 die("Need xstype argument") if not defined $xstype;
159 $type = ExtUtils::Typemaps::Type->new(
161 'prototype' => $args{'prototype'},
166 if ($args{skip} and $args{replace}) {
167 die("Cannot use both 'skip' and 'replace'");
170 if ($args{replace}) {
171 $self->remove_typemap(ctype => $type->ctype);
173 elsif ($args{skip}) {
174 return() if exists $self->{typemap_lookup}{$type->ctype};
177 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
181 push @{$self->{typemap_section}}, $type;
182 # remember type for lookup, too.
183 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
190 Add an C<INPUT> entry to the typemap.
192 Required named arguments:
193 The C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>)
194 and the C<code> to associate with it for input.
196 Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of
197 existing C<INPUT> entries of the same C<xstype>. C<skip =E<gt> 1>
198 triggers a I<"first come first serve"> logic by which new entries that conflict
199 with existing entries are silently ignored.
201 As an alternative to the named parameters usage, you may pass in
202 an C<ExtUtils::Typemaps::InputMap> object as first argument, a copy of which will be
203 added to the typemap. In that case, only the C<replace> or C<skip> named parameters
204 may be used after the object. Example:
206 $map->add_inputmap($type_obj, replace => 1);
217 $input = $orig->new();
222 my $xstype = $args{xstype};
223 die("Need xstype argument") if not defined $xstype;
224 my $code = $args{code};
225 die("Need code argument") if not defined $code;
227 $input = ExtUtils::Typemaps::InputMap->new(
233 if ($args{skip} and $args{replace}) {
234 die("Cannot use both 'skip' and 'replace'");
237 if ($args{replace}) {
238 $self->remove_inputmap(xstype => $input->xstype);
240 elsif ($args{skip}) {
241 return() if exists $self->{input_lookup}{$input->xstype};
244 $self->validate(inputmap_xstype => $input->xstype);
248 push @{$self->{input_section}}, $input;
249 # remember type for lookup, too.
250 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
257 Add an C<OUTPUT> entry to the typemap.
258 Works exactly the same as C<add_inputmap>.
269 $output = $orig->new();
274 my $xstype = $args{xstype};
275 die("Need xstype argument") if not defined $xstype;
276 my $code = $args{code};
277 die("Need code argument") if not defined $code;
279 $output = ExtUtils::Typemaps::OutputMap->new(
285 if ($args{skip} and $args{replace}) {
286 die("Cannot use both 'skip' and 'replace'");
289 if ($args{replace}) {
290 $self->remove_outputmap(xstype => $output->xstype);
292 elsif ($args{skip}) {
293 return() if exists $self->{output_lookup}{$output->xstype};
296 $self->validate(outputmap_xstype => $output->xstype);
300 push @{$self->{output_section}}, $output;
301 # remember type for lookup, too.
302 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
309 Parses a string as a typemap and merge it into the typemap object.
311 Required named argument: C<string> to specify the string to parse.
318 die("Need 'string' argument") if not defined $args{string};
320 # no, this is not elegant.
321 my $other = ExtUtils::Typemaps->new(string => $args{string});
322 $self->merge(typemap => $other);
325 =head2 remove_typemap
327 Removes a C<TYPEMAP> entry from the typemap.
329 Required named argument: C<ctype> to specify the entry to remove from the typemap.
331 Alternatively, you may pass a single C<ExtUtils::Typemaps::Type> object.
340 $ctype = $args{ctype};
341 die("Need ctype argument") if not defined $ctype;
342 $ctype = _tidy_type($ctype);
345 $ctype = $_[0]->tidy_ctype;
348 return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup});
351 =head2 remove_inputmap
353 Removes an C<INPUT> entry from the typemap.
355 Required named argument: C<xstype> to specify the entry to remove from the typemap.
357 Alternatively, you may pass a single C<ExtUtils::Typemaps::InputMap> object.
361 sub remove_inputmap {
366 $xstype = $args{xstype};
367 die("Need xstype argument") if not defined $xstype;
370 $xstype = $_[0]->xstype;
373 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup});
376 =head2 remove_inputmap
378 Removes an C<OUTPUT> entry from the typemap.
380 Required named argument: C<xstype> to specify the entry to remove from the typemap.
382 Alternatively, you may pass a single C<ExtUtils::Typemaps::OutputMap> object.
386 sub remove_outputmap {
391 $xstype = $args{xstype};
392 die("Need xstype argument") if not defined $xstype;
395 $xstype = $_[0]->xstype;
398 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
407 # Just fetch the index of the item from the lookup table
408 my $index = $lookup->{$rm};
409 return() if not defined $index;
411 # Nuke the item from storage
412 splice(@$array, $index, 1);
414 # Decrement the storage position of all items thereafter
415 foreach my $key (keys %$lookup) {
416 if ($lookup->{$key} > $index) {
425 Fetches an entry of the TYPEMAP section of the typemap.
427 Mandatory named arguments: The C<ctype> of the entry.
429 Returns the C<ExtUtils::Typemaps::Type>
430 object for the entry if found.
436 die("Need named parameters, got uneven number") if @_ % 2;
439 my $ctype = $args{ctype};
440 die("Need ctype argument") if not defined $ctype;
441 $ctype = _tidy_type($ctype);
443 my $index = $self->{typemap_lookup}{$ctype};
444 return() if not defined $index;
445 return $self->{typemap_section}[$index];
450 Fetches an entry of the INPUT section of the
453 Mandatory named arguments: The C<xstype> of the
454 entry or the C<ctype> of the typemap that can be used to find
455 the C<xstype>. To wit, the following pieces of code
458 my $type = $typemap->get_typemap(ctype => $ctype)
459 my $input_map = $typemap->get_inputmap(xstype => $type->xstype);
461 my $input_map = $typemap->get_inputmap(ctype => $ctype);
463 Returns the C<ExtUtils::Typemaps::InputMap>
464 object for the entry if found.
470 die("Need named parameters, got uneven number") if @_ % 2;
473 my $xstype = $args{xstype};
474 my $ctype = $args{ctype};
475 die("Need xstype or ctype argument")
476 if not defined $xstype
477 and not defined $ctype;
478 die("Need xstype OR ctype arguments, not both")
479 if defined $xstype and defined $ctype;
481 if (defined $ctype) {
482 my $tm = $self->get_typemap(ctype => $ctype);
483 $xstype = $tm && $tm->xstype;
484 return() if not defined $xstype;
487 my $index = $self->{input_lookup}{$xstype};
488 return() if not defined $index;
489 return $self->{input_section}[$index];
494 Fetches an entry of the OUTPUT section of the
497 Mandatory named arguments: The C<xstype> of the
498 entry or the C<ctype> of the typemap that can be used to
499 resolve the C<xstype>. (See above for an example.)
501 Returns the C<ExtUtils::Typemaps::InputMap>
502 object for the entry if found.
508 die("Need named parameters, got uneven number") if @_ % 2;
511 my $xstype = $args{xstype};
512 my $ctype = $args{ctype};
513 die("Need xstype or ctype argument")
514 if not defined $xstype
515 and not defined $ctype;
516 die("Need xstype OR ctype arguments, not both")
517 if defined $xstype and defined $ctype;
519 if (defined $ctype) {
520 my $tm = $self->get_typemap(ctype => $ctype);
521 $xstype = $tm && $tm->xstype;
522 return() if not defined $xstype;
525 my $index = $self->{output_lookup}{$xstype};
526 return() if not defined $index;
527 return $self->{output_section}[$index];
532 Write the typemap to a file. Optionally takes a C<file> argument. If given, the
533 typemap will be written to the specified file. If not, the typemap is written
534 to the currently stored file name (see C<-E<gt>file> above, this defaults to the file
535 it was read from if any).
542 my $file = defined $args{file} ? $args{file} : $self->file();
543 die("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
544 if not defined $file;
546 open my $fh, '>', $file
547 or die "Cannot open typemap file '$file' for writing: $!";
548 print $fh $self->as_string();
554 Generates and returns the string form of the typemap.
560 my $typemap = $self->{typemap_section};
562 push @code, "TYPEMAP\n";
563 foreach my $entry (@$typemap) {
565 # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
566 push @code, $entry->ctype . "\t" . $entry->xstype
567 . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
570 my $input = $self->{input_section};
572 push @code, "\nINPUT\n";
573 foreach my $entry (@$input) {
574 push @code, $entry->xstype, "\n", $entry->code, "\n";
578 my $output = $self->{output_section};
580 push @code, "\nOUTPUT\n";
581 foreach my $entry (@$output) {
582 push @code, $entry->xstype, "\n", $entry->code, "\n";
585 return join '', @code;
590 Merges a given typemap into the object. Note that a failed merge
591 operation leaves the object in an inconsistent state so clone it if necessary.
593 Mandatory named arguments: Either C<typemap =E<gt> $another_typemap_obj>
594 or C<file =E<gt> $path_to_typemap_file> but not both.
596 Optional arguments: C<replace =E<gt> 1> to force replacement
597 of existing typemap entries without warning or C<skip =E<gt> 1>
598 to skip entries that exist already in the typemap.
606 if (exists $args{typemap} and exists $args{file}) {
607 die("Need {file} OR {typemap} argument. Not both!");
609 elsif (not exists $args{typemap} and not exists $args{file}) {
610 die("Need {file} or {typemap} argument!");
614 push @params, 'replace' => $args{replace} if exists $args{replace};
615 push @params, 'skip' => $args{skip} if exists $args{skip};
617 my $typemap = $args{typemap};
618 if (not defined $typemap) {
619 $typemap = ref($self)->new(file => $args{file}, @params);
622 # FIXME breaking encapsulation. Add accessor code.
623 foreach my $entry (@{$typemap->{typemap_section}}) {
624 $self->add_typemap( $entry, @params );
627 foreach my $entry (@{$typemap->{input_section}}) {
628 $self->add_inputmap( $entry, @params );
631 foreach my $entry (@{$typemap->{output_section}}) {
632 $self->add_outputmap( $entry, @params );
640 Returns a bool indicating whether this typemap is entirely empty.
647 return @{ $self->{typemap_section} } == 0
648 && @{ $self->{input_section} } == 0
649 && @{ $self->{output_section} } == 0;
652 =head2 _get_typemap_hash
654 Returns a hash mapping the C types to the XS types:
657 'char **' => 'T_PACKEDARRAY',
660 'InputStream' => 'T_IN',
661 'double' => 'T_DOUBLE',
665 This is documented because it is used by C<ExtUtils::ParseXS>,
666 but it's not intended for general consumption. May be removed
671 sub _get_typemap_hash {
673 my $lookup = $self->{typemap_lookup};
674 my $storage = $self->{typemap_section};
677 foreach my $ctype (keys %$lookup) {
678 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
684 =head2 _get_inputmap_hash
686 Returns a hash mapping the XS types (identifiers) to the
687 corresponding INPUT code:
690 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
692 'T_OUT' => ' $var = IoOFP(sv_2io($arg))
694 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
698 This is documented because it is used by C<ExtUtils::ParseXS>,
699 but it's not intended for general consumption. May be removed
704 sub _get_inputmap_hash {
706 my $lookup = $self->{input_lookup};
707 my $storage = $self->{input_section};
710 foreach my $xstype (keys %$lookup) {
711 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
713 # Squash trailing whitespace to one line break
714 # This isn't strictly necessary, but makes the output more similar
715 # to the original ExtUtils::ParseXS.
716 $rv{$xstype} =~ s/\s*\z/\n/;
723 =head2 _get_outputmap_hash
725 Returns a hash mapping the XS types (identifiers) to the
726 corresponding OUTPUT code:
729 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
730 $var.context.value().size());
733 GV *gv = newGVgen("$Package");
734 if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
735 sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1)));
743 This is documented because it is used by C<ExtUtils::ParseXS>,
744 but it's not intended for general consumption. May be removed
749 sub _get_outputmap_hash {
751 my $lookup = $self->{output_lookup};
752 my $storage = $self->{output_section};
755 foreach my $xstype (keys %$lookup) {
756 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
758 # Squash trailing whitespace to one line break
759 # This isn't strictly necessary, but makes the output more similar
760 # to the original ExtUtils::ParseXS.
761 $rv{$xstype} =~ s/\s*\z/\n/;
767 =head2 _get_prototype_hash
769 Returns a hash mapping the C types of the typemap to their
770 corresponding prototypes.
776 'InputStream' => '$',
781 This is documented because it is used by C<ExtUtils::ParseXS>,
782 but it's not intended for general consumption. May be removed
787 sub _get_prototype_hash {
789 my $lookup = $self->{typemap_lookup};
790 my $storage = $self->{typemap_section};
793 foreach my $ctype (keys %$lookup) {
794 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
802 # make sure that the provided types wouldn't collide with what's
803 # in the object already.
808 if ( exists $args{ctype}
809 and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} )
811 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
814 if ( exists $args{inputmap_xstype}
815 and exists $self->{input_lookup}{$args{inputmap_xstype}} )
817 die("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
820 if ( exists $args{outputmap_xstype}
821 and exists $self->{output_lookup}{$args{outputmap_xstype}} )
823 die("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
831 my $stringref = shift;
832 my $lineno_offset = shift;
833 $lineno_offset = 0 if not defined $lineno_offset;
834 my $filename = shift;
835 $filename = '<string>' if not defined $filename;
837 my $replace = $self->{replace};
838 my $skip = $self->{skip};
839 die "Can only replace OR skip" if $replace and $skip;
841 push @add_params, replace => 1 if $replace;
842 push @add_params, skip => 1 if $skip;
844 # TODO comments should round-trip, currently ignoring
845 # TODO order of sections, multiple sections of same type
846 # Heavily influenced by ExtUtils::ParseXS
847 my $section = 'typemap';
848 my $lineno = $lineno_offset;
850 my $current = \$junk;
853 while ($$stringref =~ /^(.*)$/gcm) {
863 elsif (/^OUTPUT\s*$/) {
868 elsif (/^TYPEMAP\s*$/) {
869 $section = 'typemap';
874 if ($section eq 'typemap') {
877 next if $_ eq '' or /^#/;
878 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
879 or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
881 # prototype defaults to '$'
882 $proto = '$' unless $proto;
883 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
884 unless _valid_proto_string($proto);
886 ExtUtils::Typemaps::Type->new(
887 xstype => $kind, proto => $proto, ctype => $type
893 $$current .= $$current eq '' ? $_ : "\n".$_;
896 } elsif ($section eq 'input') {
898 push @input_expr, {xstype => $_, code => ''};
899 $current = \$input_expr[-1]{code};
900 } else { # output section
902 push @output_expr, {xstype => $_, code => ''};
903 $current = \$output_expr[-1]{code};
908 foreach my $inexpr (@input_expr) {
909 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params );
911 foreach my $outexpr (@output_expr) {
912 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params );
918 # taken from ExtUtils::ParseXS
922 # rationalise any '*' by joining them into bunches and removing whitespace
926 # trim leading & trailing whitespace
929 # change multiple whitespace into a single space
936 # taken from ExtUtils::ParseXS
937 sub _valid_proto_string {
939 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
946 # taken from ExtUtils::ParseXS (C_string)
947 sub _escape_backslashes {
949 $string =~ s[\\][\\\\]g;
955 Inherits some evil code from C<ExtUtils::ParseXS>.
959 The parser is heavily inspired from the one in L<ExtUtils::ParseXS>.
961 For details on typemaps: L<perlxstut>, L<perlxs>.
965 Steffen Mueller C<<smueller@cpan.org>>
967 =head1 COPYRIGHT & LICENSE
969 Copyright 2009-2011 Steffen Mueller
971 This program is free software; you can redistribute it and/or
972 modify it under the same terms as Perl itself.