1package ExtUtils::Typemaps; 2use 5.006001; 3use strict; 4use warnings; 5our $VERSION = '3.51'; 6 7require ExtUtils::ParseXS; 8require ExtUtils::ParseXS::Constants; 9require ExtUtils::Typemaps::InputMap; 10require ExtUtils::Typemaps::OutputMap; 11require ExtUtils::Typemaps::Type; 12 13=head1 NAME 14 15ExtUtils::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 48This module can read, modify, create and write Perl XS typemap files. If you don't know 49what a typemap is, please confer the L<perlxstut> and L<perlxs> manuals. 50 51The module is not entirely round-trip safe: For example it currently simply strips all comments. 52The order of entries in the maps is, however, preserved. 53 54We check for duplicate entries in the typemap, but do not check for missing 55C<TYPEMAP> entries for C<INPUTMAP> or C<OUTPUTMAP> entries since these might be hidden 56in a different typemap. 57 58=head1 METHODS 59 60=cut 61 62=head2 new 63 64Returns a new typemap object. Takes an optional C<file> parameter. 65If set, the given file will be read. If the file doesn't exist, an empty typemap 66is returned. 67 68Alternatively, if the C<string> parameter is given, the supplied 69string will be parsed instead of a file. 70 71=cut 72 73sub new { 74 my $class = shift; 75 my %args = @_; 76 77 if (defined $args{file} and defined $args{string}) { 78 die("Cannot handle both 'file' and 'string' arguments to constructor"); 79 } 80 81 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 $self->_init(); 93 94 return $self; 95} 96 97sub _init { 98 my $self = shift; 99 if (defined $self->{string}) { 100 $self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename}); 101 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 . $self->{file} . "' for reading: $!"; 107 local $/ = undef; 108 my $string = <$fh>; 109 $self->_parse(\$string, $self->{lineno_offset}, $self->{file}); 110 } 111} 112 113 114=head2 file 115 116Get/set the file that the typemap is written to when the 117C<write> method is called. 118 119=cut 120 121sub file { 122 $_[0]->{file} = $_[1] if @_ > 1; 123 $_[0]->{file} 124} 125 126=head2 add_typemap 127 128Add a C<TYPEMAP> entry to the typemap. 129 130Required named arguments: The C<ctype> (e.g. C<ctype =E<gt> 'double'>) 131and the C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>). 132 133Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of 134existing C<TYPEMAP> entries of the same C<ctype>. C<skip =E<gt> 1> 135triggers a I<"first come first serve"> logic by which new entries that conflict 136with existing entries are silently ignored. 137 138As an alternative to the named parameters usage, you may pass in 139an C<ExtUtils::Typemaps::Type> object as first argument, a copy of which will be 140added to the typemap. In that case, only the C<replace> or C<skip> named parameters 141may be used after the object. Example: 142 143 $map->add_typemap($type_obj, replace => 1); 144 145=cut 146 147sub add_typemap { 148 my $self = shift; 149 my $type; 150 my %args; 151 152 if ((@_ % 2) == 1) { 153 my $orig = shift; 154 $type = $orig->new(); 155 %args = @_; 156 } 157 else { 158 %args = @_; 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; 163 164 $type = ExtUtils::Typemaps::Type->new( 165 xstype => $xstype, 166 'prototype' => $args{'prototype'}, 167 ctype => $ctype, 168 ); 169 } 170 171 if ($args{skip} and $args{replace}) { 172 die("Cannot use both 'skip' and 'replace'"); 173 } 174 175 if ($args{replace}) { 176 $self->remove_typemap(ctype => $type->ctype); 177 } 178 elsif ($args{skip}) { 179 return() if exists $self->{typemap_lookup}{$type->ctype}; 180 } 181 else { 182 $self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype); 183 } 184 185 # store 186 push @{$self->{typemap_section}}, $type; 187 # remember type for lookup, too. 188 $self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}}; 189 190 return 1; 191} 192 193=head2 add_inputmap 194 195Add an C<INPUT> entry to the typemap. 196 197Required named arguments: 198The C<xstype> (e.g. C<xstype =E<gt> 'T_NV'>) 199and the C<code> to associate with it for input. 200 201Optional named arguments: C<replace =E<gt> 1> forces removal/replacement of 202existing C<INPUT> entries of the same C<xstype>. C<skip =E<gt> 1> 203triggers a I<"first come first serve"> logic by which new entries that conflict 204with existing entries are silently ignored. 205 206As an alternative to the named parameters usage, you may pass in 207an C<ExtUtils::Typemaps::InputMap> object as first argument, a copy of which will be 208added to the typemap. In that case, only the C<replace> or C<skip> named parameters 209may be used after the object. Example: 210 211 $map->add_inputmap($type_obj, replace => 1); 212 213=cut 214 215sub add_inputmap { 216 my $self = shift; 217 my $input; 218 my %args; 219 220 if ((@_ % 2) == 1) { 221 my $orig = shift; 222 $input = $orig->new(); 223 %args = @_; 224 } 225 else { 226 %args = @_; 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; 231 232 $input = ExtUtils::Typemaps::InputMap->new( 233 xstype => $xstype, 234 code => $code, 235 ); 236 } 237 238 if ($args{skip} and $args{replace}) { 239 die("Cannot use both 'skip' and 'replace'"); 240 } 241 242 if ($args{replace}) { 243 $self->remove_inputmap(xstype => $input->xstype); 244 } 245 elsif ($args{skip}) { 246 return() if exists $self->{input_lookup}{$input->xstype}; 247 } 248 else { 249 $self->validate(inputmap_xstype => $input->xstype); 250 } 251 252 # store 253 push @{$self->{input_section}}, $input; 254 # remember type for lookup, too. 255 $self->{input_lookup}{$input->xstype} = $#{$self->{input_section}}; 256 257 return 1; 258} 259 260=head2 add_outputmap 261 262Add an C<OUTPUT> entry to the typemap. 263Works exactly the same as C<add_inputmap>. 264 265=cut 266 267sub add_outputmap { 268 my $self = shift; 269 my $output; 270 my %args; 271 272 if ((@_ % 2) == 1) { 273 my $orig = shift; 274 $output = $orig->new(); 275 %args = @_; 276 } 277 else { 278 %args = @_; 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; 283 284 $output = ExtUtils::Typemaps::OutputMap->new( 285 xstype => $xstype, 286 code => $code, 287 ); 288 } 289 290 if ($args{skip} and $args{replace}) { 291 die("Cannot use both 'skip' and 'replace'"); 292 } 293 294 if ($args{replace}) { 295 $self->remove_outputmap(xstype => $output->xstype); 296 } 297 elsif ($args{skip}) { 298 return() if exists $self->{output_lookup}{$output->xstype}; 299 } 300 else { 301 $self->validate(outputmap_xstype => $output->xstype); 302 } 303 304 # store 305 push @{$self->{output_section}}, $output; 306 # remember type for lookup, too. 307 $self->{output_lookup}{$output->xstype} = $#{$self->{output_section}}; 308 309 return 1; 310} 311 312=head2 add_string 313 314Parses a string as a typemap and merge it into the typemap object. 315 316Required named argument: C<string> to specify the string to parse. 317 318=cut 319 320sub add_string { 321 my $self = shift; 322 my %args = @_; 323 die("Need 'string' argument") if not defined $args{string}; 324 325 # no, this is not elegant. 326 my $other = ExtUtils::Typemaps->new(string => $args{string}); 327 $self->merge(typemap => $other); 328} 329 330=head2 remove_typemap 331 332Removes a C<TYPEMAP> entry from the typemap. 333 334Required named argument: C<ctype> to specify the entry to remove from the typemap. 335 336Alternatively, you may pass a single C<ExtUtils::Typemaps::Type> object. 337 338=cut 339 340sub remove_typemap { 341 my $self = shift; 342 my $ctype; 343 if (@_ > 1) { 344 my %args = @_; 345 $ctype = $args{ctype}; 346 die("Need ctype argument") if not defined $ctype; 347 $ctype = tidy_type($ctype); 348 } 349 else { 350 $ctype = $_[0]->tidy_ctype; 351 } 352 353 return $self->_remove($ctype, $self->{typemap_section}, $self->{typemap_lookup}); 354} 355 356=head2 remove_inputmap 357 358Removes an C<INPUT> entry from the typemap. 359 360Required named argument: C<xstype> to specify the entry to remove from the typemap. 361 362Alternatively, you may pass a single C<ExtUtils::Typemaps::InputMap> object. 363 364=cut 365 366sub remove_inputmap { 367 my $self = shift; 368 my $xstype; 369 if (@_ > 1) { 370 my %args = @_; 371 $xstype = $args{xstype}; 372 die("Need xstype argument") if not defined $xstype; 373 } 374 else { 375 $xstype = $_[0]->xstype; 376 } 377 378 return $self->_remove($xstype, $self->{input_section}, $self->{input_lookup}); 379} 380 381=head2 remove_outputmap 382 383Removes an C<OUTPUT> entry from the typemap. 384 385Required named argument: C<xstype> to specify the entry to remove from the typemap. 386 387Alternatively, you may pass a single C<ExtUtils::Typemaps::OutputMap> object. 388 389=cut 390 391sub remove_outputmap { 392 my $self = shift; 393 my $xstype; 394 if (@_ > 1) { 395 my %args = @_; 396 $xstype = $args{xstype}; 397 die("Need xstype argument") if not defined $xstype; 398 } 399 else { 400 $xstype = $_[0]->xstype; 401 } 402 403 return $self->_remove($xstype, $self->{output_section}, $self->{output_lookup}); 404} 405 406sub _remove { 407 my $self = shift; 408 my $rm = shift; 409 my $array = shift; 410 my $lookup = shift; 411 412 # Just fetch the index of the item from the lookup table 413 my $index = $lookup->{$rm}; 414 return() if not defined $index; 415 416 # Nuke the item from storage 417 splice(@$array, $index, 1); 418 419 # Decrement the storage position of all items thereafter 420 foreach my $key (keys %$lookup) { 421 if ($lookup->{$key} > $index) { 422 $lookup->{$key}--; 423 } 424 } 425 return(); 426} 427 428=head2 get_typemap 429 430Fetches an entry of the TYPEMAP section of the typemap. 431 432Mandatory named arguments: The C<ctype> of the entry. 433 434Returns the C<ExtUtils::Typemaps::Type> 435object for the entry if found. 436 437=cut 438 439sub get_typemap { 440 my $self = shift; 441 die("Need named parameters, got uneven number") if @_ % 2; 442 443 my %args = @_; 444 my $ctype = $args{ctype}; 445 die("Need ctype argument") if not defined $ctype; 446 $ctype = tidy_type($ctype); 447 448 my $index = $self->{typemap_lookup}{$ctype}; 449 return() if not defined $index; 450 return $self->{typemap_section}[$index]; 451} 452 453=head2 get_inputmap 454 455Fetches an entry of the INPUT section of the 456typemap. 457 458Mandatory named arguments: The C<xstype> of the 459entry or the C<ctype> of the typemap that can be used to find 460the C<xstype>. To wit, the following pieces of code 461are 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 468Returns the C<ExtUtils::Typemaps::InputMap> 469object for the entry if found. 470 471=cut 472 473sub get_inputmap { 474 my $self = shift; 475 die("Need named parameters, got uneven number") if @_ % 2; 476 477 my %args = @_; 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; 485 486 if (defined $ctype) { 487 my $tm = $self->get_typemap(ctype => $ctype); 488 $xstype = $tm && $tm->xstype; 489 return() if not defined $xstype; 490 } 491 492 my $index = $self->{input_lookup}{$xstype}; 493 return() if not defined $index; 494 return $self->{input_section}[$index]; 495} 496 497=head2 get_outputmap 498 499Fetches an entry of the OUTPUT section of the 500typemap. 501 502Mandatory named arguments: The C<xstype> of the 503entry or the C<ctype> of the typemap that can be used to 504resolve the C<xstype>. (See above for an example.) 505 506Returns the C<ExtUtils::Typemaps::InputMap> 507object for the entry if found. 508 509=cut 510 511sub get_outputmap { 512 my $self = shift; 513 die("Need named parameters, got uneven number") if @_ % 2; 514 515 my %args = @_; 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; 523 524 if (defined $ctype) { 525 my $tm = $self->get_typemap(ctype => $ctype); 526 $xstype = $tm && $tm->xstype; 527 return() if not defined $xstype; 528 } 529 530 my $index = $self->{output_lookup}{$xstype}; 531 return() if not defined $index; 532 return $self->{output_section}[$index]; 533} 534 535=head2 write 536 537Write the typemap to a file. Optionally takes a C<file> argument. If given, the 538typemap will be written to the specified file. If not, the typemap is written 539to the currently stored file name (see L</file> above, this defaults to the file 540it was read from if any). 541 542=cut 543 544sub write { 545 my $self = shift; 546 my %args = @_; 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; 550 551 open my $fh, '>', $file 552 or die "Cannot open typemap file '$file' for writing: $!"; 553 print $fh $self->as_string(); 554 close $fh; 555} 556 557=head2 as_string 558 559Generates and returns the string form of the typemap. 560 561=cut 562 563sub as_string { 564 my $self = shift; 565 my $typemap = $self->{typemap_section}; 566 my @code; 567 push @code, "TYPEMAP\n"; 568 foreach my $entry (@$typemap) { 569 # type kind proto 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"; 573 } 574 575 my $input = $self->{input_section}; 576 if (@$input) { 577 push @code, "\nINPUT\n"; 578 foreach my $entry (@$input) { 579 push @code, $entry->xstype, "\n", $entry->code, "\n"; 580 } 581 } 582 583 my $output = $self->{output_section}; 584 if (@$output) { 585 push @code, "\nOUTPUT\n"; 586 foreach my $entry (@$output) { 587 push @code, $entry->xstype, "\n", $entry->code, "\n"; 588 } 589 } 590 return join '', @code; 591} 592 593=head2 as_embedded_typemap 594 595Generates and returns the string form of the typemap with the 596appropriate prefix around it for verbatim inclusion into an 597XS file as an embedded typemap. This will return a string like 598 599 TYPEMAP: <<END_OF_TYPEMAP 600 ... typemap here (see as_string) ... 601 END_OF_TYPEMAP 602 603The method takes care not to use a HERE-doc end marker that 604appears in the typemap string itself. 605 606=cut 607 608sub as_embedded_typemap { 609 my $self = shift; 610 my $string = $self->as_string; 611 612 my @ident_cand = qw(END_TYPEMAP END_OF_TYPEMAP END); 613 my $icand = 0; 614 my $cand_suffix = ""; 615 while ($string =~ /^\Q$ident_cand[$icand]$cand_suffix\E\s*$/m) { 616 $icand++; 617 if ($icand == @ident_cand) { 618 $icand = 0; 619 ++$cand_suffix; 620 } 621 } 622 623 my $marker = "$ident_cand[$icand]$cand_suffix"; 624 return "TYPEMAP: <<$marker;\n$string\n$marker\n"; 625} 626 627=head2 merge 628 629Merges a given typemap into the object. Note that a failed merge 630operation leaves the object in an inconsistent state so clone it if necessary. 631 632Mandatory named arguments: Either C<typemap =E<gt> $another_typemap_obj> 633or C<file =E<gt> $path_to_typemap_file> but not both. 634 635Optional arguments: C<replace =E<gt> 1> to force replacement 636of existing typemap entries without warning or C<skip =E<gt> 1> 637to skip entries that exist already in the typemap. 638 639=cut 640 641sub merge { 642 my $self = shift; 643 my %args = @_; 644 645 if (exists $args{typemap} and exists $args{file}) { 646 die("Need {file} OR {typemap} argument. Not both!"); 647 } 648 elsif (not exists $args{typemap} and not exists $args{file}) { 649 die("Need {file} or {typemap} argument!"); 650 } 651 652 my @params; 653 push @params, 'replace' => $args{replace} if exists $args{replace}; 654 push @params, 'skip' => $args{skip} if exists $args{skip}; 655 656 my $typemap = $args{typemap}; 657 if (not defined $typemap) { 658 $typemap = ref($self)->new(file => $args{file}, @params); 659 } 660 661 # FIXME breaking encapsulation. Add accessor code. 662 foreach my $entry (@{$typemap->{typemap_section}}) { 663 $self->add_typemap( $entry, @params ); 664 } 665 666 foreach my $entry (@{$typemap->{input_section}}) { 667 $self->add_inputmap( $entry, @params ); 668 } 669 670 foreach my $entry (@{$typemap->{output_section}}) { 671 $self->add_outputmap( $entry, @params ); 672 } 673 674 return 1; 675} 676 677=head2 is_empty 678 679Returns a bool indicating whether this typemap is entirely empty. 680 681=cut 682 683sub is_empty { 684 my $self = shift; 685 686 return @{ $self->{typemap_section} } == 0 687 && @{ $self->{input_section} } == 0 688 && @{ $self->{output_section} } == 0; 689} 690 691=head2 list_mapped_ctypes 692 693Returns a list of the C types that are mappable by 694this typemap object. 695 696=cut 697 698sub list_mapped_ctypes { 699 my $self = shift; 700 return sort keys %{ $self->{typemap_lookup} }; 701} 702 703=head2 _get_typemap_hash 704 705Returns 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 716This is documented because it is used by C<ExtUtils::ParseXS>, 717but it's not intended for general consumption. May be removed 718at any time. 719 720=cut 721 722sub _get_typemap_hash { 723 my $self = shift; 724 my $lookup = $self->{typemap_lookup}; 725 my $storage = $self->{typemap_section}; 726 727 my %rv; 728 foreach my $ctype (keys %$lookup) { 729 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->xstype; 730 } 731 732 return \%rv; 733} 734 735=head2 _get_inputmap_hash 736 737Returns a hash mapping the XS types (identifiers) to the 738corresponding 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 749This is documented because it is used by C<ExtUtils::ParseXS>, 750but it's not intended for general consumption. May be removed 751at any time. 752 753=cut 754 755sub _get_inputmap_hash { 756 my $self = shift; 757 my $lookup = $self->{input_lookup}; 758 my $storage = $self->{input_section}; 759 760 my %rv; 761 foreach my $xstype (keys %$lookup) { 762 $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 $rv{$xstype} =~ s/\s*\z/\n/; 768 } 769 770 return \%rv; 771} 772 773 774=head2 _get_outputmap_hash 775 776Returns a hash mapping the XS types (identifiers) to the 777corresponding 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 799This is documented because it is used by C<ExtUtils::ParseXS>, 800but it's not intended for general consumption. May be removed 801at any time. 802 803=cut 804 805sub _get_outputmap_hash { 806 my $self = shift; 807 my $lookup = $self->{output_lookup}; 808 my $storage = $self->{output_section}; 809 810 my %rv; 811 foreach my $xstype (keys %$lookup) { 812 $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 $rv{$xstype} =~ s/\s*\z/\n/; 818 } 819 820 return \%rv; 821} 822 823=head2 _get_prototype_hash 824 825Returns a hash mapping the C types of the typemap to their 826corresponding prototypes. 827 828 { 829 'char **' => '$', 830 'bool_t' => '$', 831 'AV *' => '$', 832 'InputStream' => '$', 833 'double' => '$', 834 # ... 835 } 836 837This is documented because it is used by C<ExtUtils::ParseXS>, 838but it's not intended for general consumption. May be removed 839at any time. 840 841=cut 842 843sub _get_prototype_hash { 844 my $self = shift; 845 my $lookup = $self->{typemap_lookup}; 846 my $storage = $self->{typemap_section}; 847 848 my %rv; 849 foreach my $ctype (keys %$lookup) { 850 $rv{$ctype} = $storage->[ $lookup->{$ctype} ]->proto || '$'; 851 } 852 853 return \%rv; 854} 855 856 857 858# make sure that the provided types wouldn't collide with what's 859# in the object already. 860sub validate { 861 my $self = shift; 862 my %args = @_; 863 864 if ( exists $args{ctype} 865 and exists $self->{typemap_lookup}{tidy_type($args{ctype})} ) 866 { 867 die("Multiple definition of ctype '$args{ctype}' in TYPEMAP section"); 868 } 869 870 if ( exists $args{inputmap_xstype} 871 and exists $self->{input_lookup}{$args{inputmap_xstype}} ) 872 { 873 die("Multiple definition of xstype '$args{inputmap_xstype}' in INPUTMAP section"); 874 } 875 876 if ( exists $args{outputmap_xstype} 877 and exists $self->{output_lookup}{$args{outputmap_xstype}} ) 878 { 879 die("Multiple definition of xstype '$args{outputmap_xstype}' in OUTPUTMAP section"); 880 } 881 882 return 1; 883} 884 885=head2 clone 886 887Creates and returns a clone of a full typemaps object. 888 889Takes named parameters: If C<shallow> is true, 890the clone will share the actual individual type/input/outputmap objects, 891but not share their storage. Use with caution. Without C<shallow>, 892the clone will be fully independent. 893 894=cut 895 896sub clone { 897 my $proto = shift; 898 my %args = @_; 899 900 my $self; 901 if ($args{shallow}) { 902 $self = bless( { 903 %$proto, 904 typemap_section => [@{$proto->{typemap_section}}], 905 typemap_lookup => {%{$proto->{typemap_lookup}}}, 906 input_section => [@{$proto->{input_section}}], 907 input_lookup => {%{$proto->{input_lookup}}}, 908 output_section => [@{$proto->{output_section}}], 909 output_lookup => {%{$proto->{output_lookup}}}, 910 } => ref($proto) ); 911 } 912 else { 913 $self = bless( { 914 %$proto, 915 typemap_section => [map $_->new, @{$proto->{typemap_section}}], 916 typemap_lookup => {%{$proto->{typemap_lookup}}}, 917 input_section => [map $_->new, @{$proto->{input_section}}], 918 input_lookup => {%{$proto->{input_lookup}}}, 919 output_section => [map $_->new, @{$proto->{output_section}}], 920 output_lookup => {%{$proto->{output_lookup}}}, 921 } => ref($proto) ); 922 } 923 924 return $self; 925} 926 927=head2 tidy_type 928 929Function to (heuristically) canonicalize a C type. Works to some 930degree with C++ types. 931 932 $halfway_canonical_type = tidy_type($ctype); 933 934Moved from C<ExtUtils::ParseXS>. 935 936=cut 937 938sub tidy_type { 939 local $_ = shift; 940 941 # for templated C++ types, do some bit of flawed canonicalization 942 # wrt. templates at least 943 if (/[<>]/) { 944 s/\s*([<>])\s*/$1/g; 945 s/>>/> >/g; 946 } 947 948 # rationalise any '*' by joining them into bunches and removing whitespace 949 s#\s*(\*+)\s*#$1#g; 950 s#(\*+)# $1 #g ; 951 952 # trim leading & trailing whitespace 953 s/^\s+//; s/\s+$//; 954 955 # change multiple whitespace into a single space 956 s/\s+/ /g; 957 958 $_; 959} 960 961 962 963sub _parse { 964 my $self = shift; 965 my $stringref = shift; 966 my $lineno_offset = shift; 967 $lineno_offset = 0 if not defined $lineno_offset; 968 my $filename = shift; 969 $filename = '<string>' if not defined $filename; 970 971 my $replace = $self->{replace}; 972 my $skip = $self->{skip}; 973 die "Can only replace OR skip" if $replace and $skip; 974 my @add_params; 975 push @add_params, replace => 1 if $replace; 976 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 my $section = 'typemap'; 982 my $lineno = $lineno_offset; 983 my $junk = ""; 984 my $current = \$junk; 985 my @input_expr; 986 my @output_expr; 987 while ($$stringref =~ /^(.*)$/gcm) { 988 local $_ = $1; 989 ++$lineno; 990 chomp; 991 next if /^\s*#/; 992 if (/^INPUT\s*$/) { 993 $section = 'input'; 994 $current = \$junk; 995 next; 996 } 997 elsif (/^OUTPUT\s*$/) { 998 $section = 'output'; 999 $current = \$junk; 1000 next; 1001 } 1002 elsif (/^TYPEMAP\s*$/) { 1003 $section = 'typemap'; 1004 $current = \$junk; 1005 next; 1006 } 1007 1008 if ($section eq 'typemap') { 1009 my $line = $_; 1010 s/^\s+//; s/\s+$//; 1011 next if $_ eq '' or /^#/; 1012 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 $proto = '$' unless $proto; 1017 warn("Warning: File '$filename' Line $lineno '$line' Invalid prototype '$proto'\n") 1018 unless _valid_proto_string($proto); 1019 $self->add_typemap( 1020 ExtUtils::Typemaps::Type->new( 1021 xstype => $kind, proto => $proto, ctype => $type 1022 ), 1023 @add_params 1024 ); 1025 } elsif (/^\s/) { 1026 s/\s+$//; 1027 $$current .= $$current eq '' ? $_ : "\n".$_; 1028 } elsif ($_ eq '') { 1029 next; 1030 } elsif ($section eq 'input') { 1031 s/\s+$//; 1032 push @input_expr, {xstype => $_, code => ''}; 1033 $current = \$input_expr[-1]{code}; 1034 } else { # output section 1035 s/\s+$//; 1036 push @output_expr, {xstype => $_, code => ''}; 1037 $current = \$output_expr[-1]{code}; 1038 } 1039 1040 } # end while lines 1041 1042 foreach my $inexpr (@input_expr) { 1043 $self->add_inputmap( ExtUtils::Typemaps::InputMap->new(%$inexpr), @add_params ); 1044 } 1045 foreach my $outexpr (@output_expr) { 1046 $self->add_outputmap( ExtUtils::Typemaps::OutputMap->new(%$outexpr), @add_params ); 1047 } 1048 1049 return 1; 1050} 1051 1052# taken from ExtUtils::ParseXS 1053sub _valid_proto_string { 1054 my $string = shift; 1055 if ($string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/o) { 1056 return $string; 1057 } 1058 1059 return 0 ; 1060} 1061 1062# taken from ExtUtils::ParseXS (C_string) 1063sub _escape_backslashes { 1064 my $string = shift; 1065 $string =~ s[\\][\\\\]g; 1066 $string; 1067} 1068 1069=head1 CAVEATS 1070 1071Inherits some evil code from C<ExtUtils::ParseXS>. 1072 1073=head1 SEE ALSO 1074 1075The parser is heavily inspired from the one in L<ExtUtils::ParseXS>. 1076 1077For details on typemaps: L<perlxstut>, L<perlxs>. 1078 1079=head1 AUTHOR 1080 1081Steffen Mueller C<<smueller@cpan.org>> 1082 1083=head1 COPYRIGHT & LICENSE 1084 1085Copyright 2009, 2010, 2011, 2012, 2013 Steffen Mueller 1086 1087This program is free software; you can redistribute it and/or 1088modify it under the same terms as Perl itself. 1089 1090=cut 1091 10921; 1093 1094