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 croak("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 croak("Need ctype argument") if not defined $ctype;
156 my $xstype = $args{xstype};
157 croak("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 croak("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 croak("Need xstype argument") if not defined $xstype;
224 my $code = $args{code};
225 croak("Need code argument") if not defined $code;
227 $input = ExtUtils::Typemaps::InputMap->new(
233 if ($args{skip} and $args{replace}) {
234 croak("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 croak("Need xstype argument") if not defined $xstype;
276 my $code = $args{code};
277 croak("Need code argument") if not defined $code;
279 $output = ExtUtils::Typemaps::OutputMap->new(
285 if ($args{skip} and $args{replace}) {
286 croak("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 croak("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 croak("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 croak("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 croak("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 croak("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
454 Returns the C<ExtUtils::Typemaps::InputMap>
455 object for the entry if found.
462 my $xstype = $args{xstype};
463 croak("Need xstype argument") if not defined $xstype;
465 my $index = $self->{input_lookup}{$xstype};
466 return() if not defined $index;
467 return $self->{input_section}[$index];
472 Fetches an entry of the OUTPUT section of the
475 Mandatory named arguments: The C<xstype> of the
478 Returns the C<ExtUtils::Typemaps::InputMap>
479 object for the entry if found.
486 my $xstype = $args{xstype};
487 croak("Need xstype argument") if not defined $xstype;
489 my $index = $self->{output_lookup}{$xstype};
490 return() if not defined $index;
491 return $self->{output_section}[$index];
496 Write the typemap to a file. Optionally takes a C<file> argument. If given, the
497 typemap will be written to the specified file. If not, the typemap is written
498 to the currently stored file name (see C<-E<gt>file> above, this defaults to the file
499 it was read from if any).
506 my $file = defined $args{file} ? $args{file} : $self->file();
507 croak("write() needs a file argument (or set the file name of the typemap using the 'file' method)")
508 if not defined $file;
510 open my $fh, '>', $file
511 or die "Cannot open typemap file '$file' for writing: $!";
512 print $fh $self->as_string();
518 Generates and returns the string form of the typemap.
524 my $typemap = $self->{typemap_section};
526 push @code, "TYPEMAP\n";
527 foreach my $entry (@$typemap) {
529 # /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
530 push @code, $entry->ctype . "\t" . $entry->xstype
531 . ($entry->proto ne '' ? "\t".$entry->proto : '') . "\n";
534 my $input = $self->{input_section};
536 push @code, "\nINPUT\n";
537 foreach my $entry (@$input) {
538 push @code, $entry->xstype, "\n", $entry->code, "\n";
542 my $output = $self->{output_section};
544 push @code, "\nOUTPUT\n";
545 foreach my $entry (@$output) {
546 push @code, $entry->xstype, "\n", $entry->code, "\n";
549 return join '', @code;
554 Merges a given typemap into the object. Note that a failed merge
555 operation leaves the object in an inconsistent state so clone it if necessary.
557 Mandatory named arguments: Either C<typemap =E<gt> $another_typemap_obj>
558 or C<file =E<gt> $path_to_typemap_file> but not both.
560 Optional arguments: C<replace =E<gt> 1> to force replacement
561 of existing typemap entries without warning or C<skip =E<gt> 1>
562 to skip entries that exist already in the typemap.
570 if (exists $args{typemap} and exists $args{file}) {
571 croak("Need {file} OR {typemap} argument. Not both!");
573 elsif (not exists $args{typemap} and not exists $args{file}) {
574 croak("Need {file} or {typemap} argument!");
577 my $typemap = $args{typemap};
578 if (not defined $typemap) {
579 $typemap = ref($self)->new(file => $args{file});
583 push @params, 'replace' => $args{replace} if exists $args{replace};
584 push @params, 'skip' => $args{skip} if exists $args{skip};
586 # FIXME breaking encapsulation. Add accessor code.
587 foreach my $entry (@{$typemap->{typemap_section}}) {
588 $self->add_typemap( $entry, @params );
591 foreach my $entry (@{$typemap->{input_section}}) {
592 $self->add_inputmap( $entry, @params );
595 foreach my $entry (@{$typemap->{output_section}}) {
596 $self->add_outputmap( $entry, @params );
603 =head2 _get_typemap_hash
605 Returns a hash mapping the C types to the XS types:
608 'char **' => 'T_PACKEDARRAY',
611 'InputStream' => 'T_IN',
612 'double' => 'T_DOUBLE',
616 This is documented because it is used by C<ExtUtils::ParseXS>,
617 but it's not intended for general consumption. May be removed
622 sub _get_typemap_hash {
624 my $lookup = $self->{typemap_lookup};
625 my $storage = $self->{typemap_section};
628 foreach my $ctype (keys %$lookup) {
629 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype;
635 =head2 _get_inputmap_hash
637 Returns a hash mapping the XS types (identifiers) to the
638 corresponding INPUT code:
641 'T_CALLBACK' => ' $var = make_perl_cb_$type($arg)
643 'T_OUT' => ' $var = IoOFP(sv_2io($arg))
645 'T_REF_IV_PTR' => ' if (sv_isa($arg, \\"${ntype}\\")) {
649 This is documented because it is used by C<ExtUtils::ParseXS>,
650 but it's not intended for general consumption. May be removed
655 sub _get_inputmap_hash {
657 my $lookup = $self->{input_lookup};
658 my $storage = $self->{input_section};
661 foreach my $xstype (keys %$lookup) {
662 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
664 # Squash trailing whitespace to one line break
665 # This isn't strictly necessary, but makes the output more similar
666 # to the original ExtUtils::ParseXS.
667 $rv{$xstype} =~ s/\s*\z/\n/;
674 =head2 _get_outputmap_hash
676 Returns a hash mapping the XS types (identifiers) to the
677 corresponding OUTPUT code:
680 'T_CALLBACK' => ' sv_setpvn($arg, $var.context.value().chp(),
681 $var.context.value().size());
684 GV *gv = newGVgen("$Package");
685 if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) )
686 sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1)));
694 This is documented because it is used by C<ExtUtils::ParseXS>,
695 but it's not intended for general consumption. May be removed
700 sub _get_outputmap_hash {
702 my $lookup = $self->{output_lookup};
703 my $storage = $self->{output_section};
706 foreach my $xstype (keys %$lookup) {
707 $rv{$xstype} = $storage->[ $lookup->{$xstype} ]->code;
709 # Squash trailing whitespace to one line break
710 # This isn't strictly necessary, but makes the output more similar
711 # to the original ExtUtils::ParseXS.
712 $rv{$xstype} =~ s/\s*\z/\n/;
718 =head2 _get_prototype_hash
720 Returns a hash mapping the C types of the typemap to their
721 corresponding prototypes.
727 'InputStream' => '$',
732 This is documented because it is used by C<ExtUtils::ParseXS>,
733 but it's not intended for general consumption. May be removed
738 sub _get_prototype_hash {
740 my $lookup = $self->{typemap_lookup};
741 my $storage = $self->{typemap_section};
744 foreach my $ctype (keys %$lookup) {
745 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$';
753 # make sure that the provided types wouldn't collide with what's
754 # in the object already.
759 if ( exists $args{ctype}
760 and exists $self->{typemap_lookup}{_tidy_type($args{ctype})} )
762 croak("Multiple definition of ctype '$args{ctype}' in TYPEMAP section");
765 if ( exists $args{inputmap_xstype}
766 and exists $self->{input_lookup}{$args{inputmap_xstype}} )
768 croak("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section");
771 if ( exists $args{outputmap_xstype}
772 and exists $self->{output_lookup}{$args{outputmap_xstype}} )
774 croak("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section");
782 my $stringref = shift;
783 my $filename = shift;
784 $filename = '<string>' if not defined $filename;
786 # TODO comments should round-trip, currently ignoring
787 # TODO order of sections, multiple sections of same type
788 # Heavily influenced by ExtUtils::ParseXS
789 my $section = 'typemap';
792 my $current = \$junk;
795 while ($$stringref =~ /^(.*)$/gcm) {
805 elsif (/^OUTPUT\s*$/) {
810 elsif (/^TYPEMAP\s*$/) {
811 $section = 'typemap';
816 if ($section eq 'typemap') {
819 next if $_ eq '' or /^#/;
820 my($type, $kind, $proto) = /^(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::PrototypeRegexp*)$/o
821 or warn("Warning: File '$filename' Line $lineno '$line' TYPEMAP entry needs 2 or 3 columns\n"),
823 # prototype defaults to '$'
824 $proto = '$' unless $proto;
825 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n")
826 unless _valid_proto_string($proto);
828 ExtUtils::Typemaps::Type->new(
829 xstype => $kind, proto => $proto, ctype => $type
834 $$current .= $$current eq '' ? $_ : "\n".$_;
837 } elsif ($section eq 'input') {
839 push @input_expr, {xstype => $_, code => ''};
840 $current = \$input_expr[-1]{code};
841 } else { # output section
843 push @output_expr, {xstype => $_, code => ''};
844 $current = \$output_expr[-1]{code};
849 foreach my $inexpr (@input_expr) {
850 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr) );
852 foreach my $outexpr (@output_expr) {
853 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr) );
859 # taken from ExtUtils::ParseXS
863 # rationalise any '*' by joining them into bunches and removing whitespace
867 # trim leading & trailing whitespace
870 # change multiple whitespace into a single space
877 # taken from ExtUtils::ParseXS
878 sub _valid_proto_string {
880 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) {
887 # taken from ExtUtils::ParseXS (C_string)
888 sub _escape_backslashes {
890 $string =~ s[\\][\\\\]g;
896 Inherits some evil code from C<ExtUtils::ParseXS>.
900 The parser is heavily inspired from the one in L<ExtUtils::ParseXS>.
902 For details on typemaps: L<perlxstut>, L<perlxs>.
906 Steffen Mueller C<<smueller@cpan.org>>
908 =head1 COPYRIGHT & LICENSE
910 Copyright 2009-2011 Steffen Mueller
912 This program is free software; you can redistribute it and/or
913 modify it under the same terms as Perl itself.