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}));
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->{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.
437 my $ctype = $args{ctype};
438 die("Need ctype argument") if not defined $ctype;
439 $ctype = _tidy_type($ctype);
441 my $index = $self->{typemap_lookup}{$ctype};
442 return() if not defined $index;
443 return $self->{typemap_section}[$index];
448 Fetches an entry of the INPUT section of the
451 Mandatory named arguments: The C<xstype> of the
452 entry or the C<ctype> of the typemap that can be used to find
453 the C<xstype>. To wit, the following pieces of code
456 my $type = $typemap->get_typemap(ctype => $ctype)
457 my $input_map = $typemap->get_inputmap(xstype => $type->xstype);
459 my $input_map = $typemap->get_inputmap(ctype => $ctype);
461 Returns the C<ExtUtils::Typemaps::InputMap>
462 object for the entry if found.
469 my $xstype = $args{xstype};
470 my $ctype = $args{ctype};
471 die("Need xstype or ctype argument")
472 if not defined $xstype
473 and not defined $ctype;
474 die("Need xstype OR ctype arguments, not both")
475 if defined $xstype and defined $ctype;
477 if (defined $ctype) {
478 $xstype = $self->get_typemap(ctype => $ctype)->xstype;
481 my $index = $self->{input_lookup}{$xstype};
482 return() if not defined $index;
483 return $self->{input_section}[$index];
488 Fetches an entry of the OUTPUT section of the
491 Mandatory named arguments: The C<xstype> of the
492 entry or the C<ctype> of the typemap that can be used to
493 resolve the C<xstype>. (See above for an example.)
495 Returns the C<ExtUtils::Typemaps::InputMap>
496 object for the entry if found.
503 my $xstype = $args{xstype};
504 my $ctype = $args{ctype};
505 die("Need xstype or ctype argument")
506 if not defined $xstype
507 and not defined $ctype;
508 die("Need xstype OR ctype arguments, not both")
509 if defined $xstype and defined $ctype;
511 if (defined $ctype) {
512 $xstype = $self->get_typemap(ctype => $ctype)->xstype;
515 my $index = $self->{output_lookup}{$xstype};
516 return() if not defined $index;
517 return $self->{output_section}[$index];
522 Write the typemap to a file. Optionally takes a C<file> argument. If given, the
523 typemap will be written to the specified file. If not, the typemap is written
524 to the currently stored file name (see C<-E<gt>file> above, this defaults to the file
525 it was read from if any).
532 my $file = defined $args{file} ? $args{file} : $self->file();
533 die("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
534 if not defined $file;
536 open my $fh, '>', $file
537 or die "Cannot open typemap file '$file' for writing: $!";
538 print $fh $self->as_string();
544 Generates and returns the string form of the typemap.
550 my $typemap = $self->{typemap_section};
552 push @code, "TYPEMAP\n";
553 foreach my $entry (@$typemap) {
555 # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
556 push @code, $entry->ctype . "\t" . $entry->xstype
557 . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
560 my $input = $self->{input_section};
562 push @code, "\nINPUT\n";
563 foreach my $entry (@$input) {
564 push @code, $entry->xstype, "\n", $entry->code, "\n";
568 my $output = $self->{output_section};
570 push @code, "\nOUTPUT\n";
571 foreach my $entry (@$output) {
572 push @code, $entry->xstype, "\n", $entry->code, "\n";
575 return join '', @code;
580 Merges a given typemap into the object. Note that a failed merge
581 operation leaves the object in an inconsistent state so clone it if necessary.
583 Mandatory named arguments: Either C<typemap =E<gt> $another_typemap_obj>
584 or C<file =E<gt> $path_to_typemap_file> but not both.
586 Optional arguments: C<replace =E<gt> 1> to force replacement
587 of existing typemap entries without warning or C<skip =E<gt> 1>
588 to skip entries that exist already in the typemap.
596 if (exists $args{typemap} and exists $args{file}) {
597 die("Need {file} OR {typemap} argument. Not both!");
599 elsif (not exists $args{typemap} and not exists $args{file}) {
600 die("Need {file} or {typemap} argument!");
604 push @params, 'replace' => $args{replace} if exists $args{replace};
605 push @params, 'skip' => $args{skip} if exists $args{skip};
607 my $typemap = $args{typemap};
608 if (not defined $typemap) {
609 $typemap = ref($self)->new(file => $args{file}, @params);
612 # FIXME breaking encapsulation. Add accessor code.
613 foreach my $entry (@{$typemap->{typemap_section}}) {
614 $self->add_typemap( $entry, @params );
617 foreach my $entry (@{$typemap->{input_section}}) {
618 $self->add_inputmap( $entry, @params );
621 foreach my $entry (@{$typemap->{output_section}}) {
622 $self->add_outputmap( $entry, @params );
629 =head2 _get_typemap_hash
631 Returns a hash mapping the C types to the XS types:
634 'char **' => 'T_PACKEDARRAY',
637 'InputStream' => 'T_IN',
638 'double' => 'T_DOUBLE',
642 This is documented because it is used by C<ExtUtils::ParseXS>,
643 but it's not intended for general consumption. May be removed
648 sub _get_typemap_hash {
650 my $lookup = $self->{typemap_lookup};
651 my $storage = $self->{typemap_section};
654 foreach my $ctype (keys %$lookup) {
655 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
661 =head2 _get_inputmap_hash
663 Returns a hash mapping the XS types (identifiers) to the
664 corresponding INPUT code:
667 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
669 'T_OUT' => ' $var = IoOFP(sv_2io($arg))
671 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
675 This is documented because it is used by C<ExtUtils::ParseXS>,
676 but it's not intended for general consumption. May be removed
681 sub _get_inputmap_hash {
683 my $lookup = $self->{input_lookup};
684 my $storage = $self->{input_section};
687 foreach my $xstype (keys %$lookup) {
688 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
690 # Squash trailing whitespace to one line break
691 # This isn't strictly necessary, but makes the output more similar
692 # to the original ExtUtils::ParseXS.
693 $rv{$xstype} =~ s/\s*\z/\n/;
700 =head2 _get_outputmap_hash
702 Returns a hash mapping the XS types (identifiers) to the
703 corresponding OUTPUT code:
706 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
707 $var.context.value().size());
710 GV *gv = newGVgen("$Package");
711 if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
712 sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1)));
720 This is documented because it is used by C<ExtUtils::ParseXS>,
721 but it's not intended for general consumption. May be removed
726 sub _get_outputmap_hash {
728 my $lookup = $self->{output_lookup};
729 my $storage = $self->{output_section};
732 foreach my $xstype (keys %$lookup) {
733 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
735 # Squash trailing whitespace to one line break
736 # This isn't strictly necessary, but makes the output more similar
737 # to the original ExtUtils::ParseXS.
738 $rv{$xstype} =~ s/\s*\z/\n/;
744 =head2 _get_prototype_hash
746 Returns a hash mapping the C types of the typemap to their
747 corresponding prototypes.
753 'InputStream' => '$',
758 This is documented because it is used by C<ExtUtils::ParseXS>,
759 but it's not intended for general consumption. May be removed
764 sub _get_prototype_hash {
766 my $lookup = $self->{typemap_lookup};
767 my $storage = $self->{typemap_section};
770 foreach my $ctype (keys %$lookup) {
771 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
779 # make sure that the provided types wouldn't collide with what's
780 # in the object already.
785 if ( exists $args{ctype}
786 and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} )
788 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
791 if ( exists $args{inputmap_xstype}
792 and exists $self->{input_lookup}{$args{inputmap_xstype}} )
794 die("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
797 if ( exists $args{outputmap_xstype}
798 and exists $self->{output_lookup}{$args{outputmap_xstype}} )
800 die("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
808 my $stringref = shift;
809 my $filename = shift;
810 $filename = '<string>' if not defined $filename;
812 my $replace = $self->{replace};
813 my $skip = $self->{skip};
814 die "Can only replace OR skip" if $replace and $skip;
816 push @add_params, replace => 1 if $replace;
817 push @add_params, skip => 1 if $skip;
819 # TODO comments should round-trip, currently ignoring
820 # TODO order of sections, multiple sections of same type
821 # Heavily influenced by ExtUtils::ParseXS
822 my $section = 'typemap';
825 my $current = \$junk;
828 while ($$stringref =~ /^(.*)$/gcm) {
838 elsif (/^OUTPUT\s*$/) {
843 elsif (/^TYPEMAP\s*$/) {
844 $section = 'typemap';
849 if ($section eq 'typemap') {
852 next if $_ eq '' or /^#/;
853 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
854 or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
856 # prototype defaults to '$'
857 $proto = '$' unless $proto;
858 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
859 unless _valid_proto_string($proto);
861 ExtUtils::Typemaps::Type->new(
862 xstype => $kind, proto => $proto, ctype => $type
868 $$current .= $$current eq '' ? $_ : "\n".$_;
871 } elsif ($section eq 'input') {
873 push @input_expr, {xstype => $_, code => ''};
874 $current = \$input_expr[-1]{code};
875 } else { # output section
877 push @output_expr, {xstype => $_, code => ''};
878 $current = \$output_expr[-1]{code};
883 foreach my $inexpr (@input_expr) {
884 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params );
886 foreach my $outexpr (@output_expr) {
887 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params );
893 # taken from ExtUtils::ParseXS
897 # rationalise any '*' by joining them into bunches and removing whitespace
901 # trim leading & trailing whitespace
904 # change multiple whitespace into a single space
911 # taken from ExtUtils::ParseXS
912 sub _valid_proto_string {
914 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
921 # taken from ExtUtils::ParseXS (C_string)
922 sub _escape_backslashes {
924 $string =~ s[\\][\\\\]g;
930 Inherits some evil code from C<ExtUtils::ParseXS>.
934 The parser is heavily inspired from the one in L<ExtUtils::ParseXS>.
936 For details on typemaps: L<perlxstut>, L<perlxs>.
940 Steffen Mueller C<<smueller@cpan.org>>
942 =head1 COPYRIGHT & LICENSE
944 Copyright 2009-2011 Steffen Mueller
946 This program is free software; you can redistribute it and/or
947 modify it under the same terms as Perl itself.