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(
30 xstype => 'T_NV', code => '$var = ($type)SvNV($arg);'
32 $typemap->add_outputmap(
33 xstype => 'T_NV', code => 'sv_setnv($arg, (NV)$var);'
35 $typemap->add_string(string => $typemapstring);
36 # will be parsed and merged
38 # remove a mapping (same for remove_typemap and remove_outputmap...)
39 $typemap->remove_inputmap(xstype => 'SomeType');
41 # save a typemap to a file
42 $typemap->write(file => 'anotherfile.map');
44 # merge the other typemap into this one
45 $typemap->merge(typemap => $another_typemap);
49 This module can read, modify, create and write Perl XS typemap files. If you don't know
50 what a typemap is, please confer the L<perlxstut> and L<perlxs> manuals.
52 The module is not entirely round-trip safe: For example it currently simply strips all comments.
53 The order of entries in the maps is, however, preserved.
55 We check for duplicate entries in the typemap, but do not check for missing
56 C<TYPEMAP> entries for C<INPUTMAP> or C<OUTPUTMAP> entries since these might be hidden
57 in a different typemap.
65 Returns a new typemap object. Takes an optional C<file> parameter.
66 If set, the given file will be read. If the file doesn't exist, an empty typemap
69 Alternatively, if the C<string> parameter is given, the supplied
70 string will be parsed instead of a file.
78 if (defined $args{file} and defined $args{string}) {
79 die("Cannot handle both 'file' and 'string' arguments to constructor");
85 typemap_section => [],
100 if (defined $self->{string}) {
101 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
102 delete $self->{string};
104 elsif (defined $self->{file} and -e $self->{file}) {
105 open my $fh, '<', $self->{file}
106 or die "Cannot open typemap file '"
107 . $self->{file} . "' for reading: $!";
110 $self->_parse(\$string, $self->{lineno_offset}, $self->{file});
116 Get/set the file that the typemap is written to when the
117 C<write> method is called.
122 $_[0]->{file} = $_[1] if @_ > 1;
128 Add a C<TYPEMAP> entry to the typemap.
130 Required named arguments: The C<ctype> (e.g. C<ctype =E<gt> 'double'>)
131 and the C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>).
133 Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of
134 existing C<TYPEMAP> entries of the same C<ctype>. C<skip =E<gt> 1>
135 triggers a I<"first come first serve"> logic by which new entries that conflict
136 with existing entries are silently ignored.
138 As an alternative to the named parameters usage, you may pass in
139 an C<ExtUtils::Typemaps::Type> object as first argument, a copy of which will be
140 added to the typemap. In that case, only the C<replace> or C<skip> named parameters
141 may be used after the object. Example:
143 $map->add_typemap($type_obj, replace => 1);
154 $type = $orig->new();
159 my $ctype = $args{ctype};
160 die("Need ctype argument") if not defined $ctype;
161 my $xstype = $args{xstype};
162 die("Need xstype argument") if not defined $xstype;
164 $type = ExtUtils::Typemaps::Type->new(
166 'prototype' => $args{'prototype'},
171 if ($args{skip} and $args{replace}) {
172 die("Cannot use both 'skip' and 'replace'");
175 if ($args{replace}) {
176 $self->remove_typemap(ctype => $type->ctype);
178 elsif ($args{skip}) {
179 return() if exists $self->{typemap_lookup}{$type->ctype};
182 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
186 push @{$self->{typemap_section}}, $type;
187 # remember type for lookup, too.
188 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
195 Add an C<INPUT> entry to the typemap.
197 Required named arguments:
198 The C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>)
199 and the C<code> to associate with it for input.
201 Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of
202 existing C<INPUT> entries of the same C<xstype>. C<skip =E<gt> 1>
203 triggers a I<"first come first serve"> logic by which new entries that conflict
204 with existing entries are silently ignored.
206 As an alternative to the named parameters usage, you may pass in
207 an C<ExtUtils::Typemaps::InputMap> object as first argument, a copy of which will be
208 added to the typemap. In that case, only the C<replace> or C<skip> named parameters
209 may be used after the object. Example:
211 $map->add_inputmap($type_obj, replace => 1);
222 $input = $orig->new();
227 my $xstype = $args{xstype};
228 die("Need xstype argument") if not defined $xstype;
229 my $code = $args{code};
230 die("Need code argument") if not defined $code;
232 $input = ExtUtils::Typemaps::InputMap->new(
238 if ($args{skip} and $args{replace}) {
239 die("Cannot use both 'skip' and 'replace'");
242 if ($args{replace}) {
243 $self->remove_inputmap(xstype => $input->xstype);
245 elsif ($args{skip}) {
246 return() if exists $self->{input_lookup}{$input->xstype};
249 $self->validate(inputmap_xstype => $input->xstype);
253 push @{$self->{input_section}}, $input;
254 # remember type for lookup, too.
255 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
262 Add an C<OUTPUT> entry to the typemap.
263 Works exactly the same as C<add_inputmap>.
274 $output = $orig->new();
279 my $xstype = $args{xstype};
280 die("Need xstype argument") if not defined $xstype;
281 my $code = $args{code};
282 die("Need code argument") if not defined $code;
284 $output = ExtUtils::Typemaps::OutputMap->new(
290 if ($args{skip} and $args{replace}) {
291 die("Cannot use both 'skip' and 'replace'");
294 if ($args{replace}) {
295 $self->remove_outputmap(xstype => $output->xstype);
297 elsif ($args{skip}) {
298 return() if exists $self->{output_lookup}{$output->xstype};
301 $self->validate(outputmap_xstype => $output->xstype);
305 push @{$self->{output_section}}, $output;
306 # remember type for lookup, too.
307 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}};
314 Parses a string as a typemap and merge it into the typemap object.
316 Required named argument: C<string> to specify the string to parse.
323 die("Need 'string' argument") if not defined $args{string};
325 # no, this is not elegant.
326 my $other = ExtUtils::Typemaps->new(string => $args{string});
327 $self->merge(typemap => $other);
330 =head2 remove_typemap
332 Removes a C<TYPEMAP> entry from the typemap.
334 Required named argument: C<ctype> to specify the entry to remove from the typemap.
336 Alternatively, you may pass a single C<ExtUtils::Typemaps::Type> object.
345 $ctype = $args{ctype};
346 die("Need ctype argument") if not defined $ctype;
347 $ctype = _tidy_type($ctype);
350 $ctype = $_[0]->tidy_ctype;
353 return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup});
356 =head2 remove_inputmap
358 Removes an C<INPUT> entry from the typemap.
360 Required named argument: C<xstype> to specify the entry to remove from the typemap.
362 Alternatively, you may pass a single C<ExtUtils::Typemaps::InputMap> object.
366 sub remove_inputmap {
371 $xstype = $args{xstype};
372 die("Need xstype argument") if not defined $xstype;
375 $xstype = $_[0]->xstype;
378 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup});
381 =head2 remove_inputmap
383 Removes an C<OUTPUT> entry from the typemap.
385 Required named argument: C<xstype> to specify the entry to remove from the typemap.
387 Alternatively, you may pass a single C<ExtUtils::Typemaps::OutputMap> object.
391 sub remove_outputmap {
396 $xstype = $args{xstype};
397 die("Need xstype argument") if not defined $xstype;
400 $xstype = $_[0]->xstype;
403 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup});
412 # Just fetch the index of the item from the lookup table
413 my $index = $lookup->{$rm};
414 return() if not defined $index;
416 # Nuke the item from storage
417 splice(@$array, $index, 1);
419 # Decrement the storage position of all items thereafter
420 foreach my $key (keys %$lookup) {
421 if ($lookup->{$key} > $index) {
430 Fetches an entry of the TYPEMAP section of the typemap.
432 Mandatory named arguments: The C<ctype> of the entry.
434 Returns the C<ExtUtils::Typemaps::Type>
435 object for the entry if found.
441 die("Need named parameters, got uneven number") if @_ % 2;
444 my $ctype = $args{ctype};
445 die("Need ctype argument") if not defined $ctype;
446 $ctype = _tidy_type($ctype);
448 my $index = $self->{typemap_lookup}{$ctype};
449 return() if not defined $index;
450 return $self->{typemap_section}[$index];
455 Fetches an entry of the INPUT section of the
458 Mandatory named arguments: The C<xstype> of the
459 entry or the C<ctype> of the typemap that can be used to find
460 the C<xstype>. To wit, the following pieces of code
463 my $type = $typemap->get_typemap(ctype => $ctype)
464 my $input_map = $typemap->get_inputmap(xstype => $type->xstype);
466 my $input_map = $typemap->get_inputmap(ctype => $ctype);
468 Returns the C<ExtUtils::Typemaps::InputMap>
469 object for the entry if found.
475 die("Need named parameters, got uneven number") if @_ % 2;
478 my $xstype = $args{xstype};
479 my $ctype = $args{ctype};
480 die("Need xstype or ctype argument")
481 if not defined $xstype
482 and not defined $ctype;
483 die("Need xstype OR ctype arguments, not both")
484 if defined $xstype and defined $ctype;
486 if (defined $ctype) {
487 my $tm = $self->get_typemap(ctype => $ctype);
488 $xstype = $tm && $tm->xstype;
489 return() if not defined $xstype;
492 my $index = $self->{input_lookup}{$xstype};
493 return() if not defined $index;
494 return $self->{input_section}[$index];
499 Fetches an entry of the OUTPUT section of the
502 Mandatory named arguments: The C<xstype> of the
503 entry or the C<ctype> of the typemap that can be used to
504 resolve the C<xstype>. (See above for an example.)
506 Returns the C<ExtUtils::Typemaps::InputMap>
507 object for the entry if found.
513 die("Need named parameters, got uneven number") if @_ % 2;
516 my $xstype = $args{xstype};
517 my $ctype = $args{ctype};
518 die("Need xstype or ctype argument")
519 if not defined $xstype
520 and not defined $ctype;
521 die("Need xstype OR ctype arguments, not both")
522 if defined $xstype and defined $ctype;
524 if (defined $ctype) {
525 my $tm = $self->get_typemap(ctype => $ctype);
526 $xstype = $tm && $tm->xstype;
527 return() if not defined $xstype;
530 my $index = $self->{output_lookup}{$xstype};
531 return() if not defined $index;
532 return $self->{output_section}[$index];
537 Write the typemap to a file. Optionally takes a C<file> 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 C<-E<gt>file> above, this defaults to the file
540 it was read from if any).
547 my $file = defined $args{file} ? $args{file} : $self->file();
548 die("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
549 if not defined $file;
551 open my $fh, '>', $file
552 or die "Cannot open typemap file '$file' for writing: $!";
553 print $fh $self->as_string();
559 Generates and returns the string form of the typemap.
565 my $typemap = $self->{typemap_section};
567 push @code, "TYPEMAP\n";
568 foreach my $entry (@$typemap) {
570 # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
571 push @code, $entry->ctype . "\t" . $entry->xstype
572 . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
575 my $input = $self->{input_section};
577 push @code, "\nINPUT\n";
578 foreach my $entry (@$input) {
579 push @code, $entry->xstype, "\n", $entry->code, "\n";
583 my $output = $self->{output_section};
585 push @code, "\nOUTPUT\n";
586 foreach my $entry (@$output) {
587 push @code, $entry->xstype, "\n", $entry->code, "\n";
590 return join '', @code;
593 =head2 as_embedded_typemap
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
599 TYPEMAP: <<END_OF_TYPEMAP
600 ... typemap here (see as_string) ...
603 The method takes care not to use a HERE-doc end marker that
604 appears in the typemap string itself.
608 sub as_embedded_typemap {
610 my $string = $self->as_string;
612 my @ident_cand = qw(END_TYPEMAP END_OF_TYPEMAP END);
614 my $cand_suffix = "";
615 while ($string =~ /^\Q$ident_cand[$icand]$cand_suffix\E\s*$/m) {
617 if ($icand == @ident_cand) {
623 my $marker = "$ident_cand[$icand]$cand_suffix";
624 return "TYPEMAP: <<$marker;\n$string\n$marker\n";
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.
632 Mandatory named arguments: Either C<typemap =E<gt> $another_typemap_obj>
633 or C<file =E<gt> $path_to_typemap_file> but not both.
635 Optional arguments: C<replace =E<gt> 1> to force replacement
636 of existing typemap entries without warning or C<skip =E<gt> 1>
637 to skip entries that exist already in the typemap.
645 if (exists $args{typemap} and exists $args{file}) {
646 die("Need {file} OR {typemap} argument. Not both!");
648 elsif (not exists $args{typemap} and not exists $args{file}) {
649 die("Need {file} or {typemap} argument!");
653 push @params, 'replace' => $args{replace} if exists $args{replace};
654 push @params, 'skip' => $args{skip} if exists $args{skip};
656 my $typemap = $args{typemap};
657 if (not defined $typemap) {
658 $typemap = ref($self)->new(file => $args{file}, @params);
661 # FIXME breaking encapsulation. Add accessor code.
662 foreach my $entry (@{$typemap->{typemap_section}}) {
663 $self->add_typemap( $entry, @params );
666 foreach my $entry (@{$typemap->{input_section}}) {
667 $self->add_inputmap( $entry, @params );
670 foreach my $entry (@{$typemap->{output_section}}) {
671 $self->add_outputmap( $entry, @params );
679 Returns a bool indicating whether this typemap is entirely empty.
686 return @{ $self->{typemap_section} } == 0
687 && @{ $self->{input_section} } == 0
688 && @{ $self->{output_section} } == 0;
691 =head2 list_mapped_ctypes
693 Returns a list of the C types that are mappable by
698 sub list_mapped_ctypes {
700 return sort keys %{ $self->{typemap_lookup} };
703 =head2 _get_typemap_hash
705 Returns a hash mapping the C types to the XS types:
708 'char **' => 'T_PACKEDARRAY',
711 'InputStream' => 'T_IN',
712 'double' => 'T_DOUBLE',
716 This is documented because it is used by C<ExtUtils::ParseXS>,
717 but it's not intended for general consumption. May be removed
722 sub _get_typemap_hash {
724 my $lookup = $self->{typemap_lookup};
725 my $storage = $self->{typemap_section};
728 foreach my $ctype (keys %$lookup) {
729 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
735 =head2 _get_inputmap_hash
737 Returns a hash mapping the XS types (identifiers) to the
738 corresponding INPUT code:
741 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
743 'T_OUT' => ' $var = IoOFP(sv_2io($arg))
745 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
749 This is documented because it is used by C<ExtUtils::ParseXS>,
750 but it's not intended for general consumption. May be removed
755 sub _get_inputmap_hash {
757 my $lookup = $self->{input_lookup};
758 my $storage = $self->{input_section};
761 foreach my $xstype (keys %$lookup) {
762 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
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 $rv{$xstype} =~ s/\s*\z/\n/;
774 =head2 _get_outputmap_hash
776 Returns a hash mapping the XS types (identifiers) to the
777 corresponding OUTPUT code:
780 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
781 $var.context.value().size());
784 GV *gv = newGVgen("$Package");
785 if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
788 sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))
797 This is documented because it is used by C<ExtUtils::ParseXS>,
798 but it's not intended for general consumption. May be removed
803 sub _get_outputmap_hash {
805 my $lookup = $self->{output_lookup};
806 my $storage = $self->{output_section};
809 foreach my $xstype (keys %$lookup) {
810 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
812 # Squash trailing whitespace to one line break
813 # This isn't strictly necessary, but makes the output more similar
814 # to the original ExtUtils::ParseXS.
815 $rv{$xstype} =~ s/\s*\z/\n/;
821 =head2 _get_prototype_hash
823 Returns a hash mapping the C types of the typemap to their
824 corresponding prototypes.
830 'InputStream' => '$',
835 This is documented because it is used by C<ExtUtils::ParseXS>,
836 but it's not intended for general consumption. May be removed
841 sub _get_prototype_hash {
843 my $lookup = $self->{typemap_lookup};
844 my $storage = $self->{typemap_section};
847 foreach my $ctype (keys %$lookup) {
848 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
856 # make sure that the provided types wouldn't collide with what's
857 # in the object already.
862 if ( exists $args{ctype}
863 and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} )
865 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
868 if ( exists $args{inputmap_xstype}
869 and exists $self->{input_lookup}{$args{inputmap_xstype}} )
871 die("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
874 if ( exists $args{outputmap_xstype}
875 and exists $self->{output_lookup}{$args{outputmap_xstype}} )
877 die("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
885 my $stringref = shift;
886 my $lineno_offset = shift;
887 $lineno_offset = 0 if not defined $lineno_offset;
888 my $filename = shift;
889 $filename = '<string>' if not defined $filename;
891 my $replace = $self->{replace};
892 my $skip = $self->{skip};
893 die "Can only replace OR skip" if $replace and $skip;
895 push @add_params, replace => 1 if $replace;
896 push @add_params, skip => 1 if $skip;
898 # TODO comments should round-trip, currently ignoring
899 # TODO order of sections, multiple sections of same type
900 # Heavily influenced by ExtUtils::ParseXS
901 my $section = 'typemap';
902 my $lineno = $lineno_offset;
904 my $current = \$junk;
907 while ($$stringref =~ /^(.*)$/gcm) {
917 elsif (/^OUTPUT\s*$/) {
922 elsif (/^TYPEMAP\s*$/) {
923 $section = 'typemap';
928 if ($section eq 'typemap') {
931 next if $_ eq '' or /^#/;
932 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
933 or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
935 # prototype defaults to '$'
936 $proto = '$' unless $proto;
937 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
938 unless _valid_proto_string($proto);
940 ExtUtils::Typemaps::Type->new(
941 xstype => $kind, proto => $proto, ctype => $type
947 $$current .= $$current eq '' ? $_ : "\n".$_;
950 } elsif ($section eq 'input') {
952 push @input_expr, {xstype => $_, code => ''};
953 $current = \$input_expr[-1]{code};
954 } else { # output section
956 push @output_expr, {xstype => $_, code => ''};
957 $current = \$output_expr[-1]{code};
962 foreach my $inexpr (@input_expr) {
963 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params );
965 foreach my $outexpr (@output_expr) {
966 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params );
972 # taken from ExtUtils::ParseXS
976 # rationalise any '*' by joining them into bunches and removing whitespace
980 # trim leading & trailing whitespace
983 # change multiple whitespace into a single space
990 # taken from ExtUtils::ParseXS
991 sub _valid_proto_string {
993 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
1000 # taken from ExtUtils::ParseXS (C_string)
1001 sub _escape_backslashes {
1003 $string =~ s[\\][\\\\]g;
1009 Inherits some evil code from C<ExtUtils::ParseXS>.
1013 The parser is heavily inspired from the one in L<ExtUtils::ParseXS>.
1015 For details on typemaps: L<perlxstut>, L<perlxs>.
1019 Steffen Mueller C<<smueller@cpan.org>>
1021 =head1 COPYRIGHT & LICENSE
1023 Copyright 2009, 2010, 2011, 2012 Steffen Mueller
1025 This program is free software; you can redistribute it and/or
1026 modify it under the same terms as Perl itself.