1# Copyright (C) 2004-2014, Parrot Foundation. 2 3=head1 NAME 4 5Parrot::Pmc2c::PMC - PMC model object 6 7=head1 SYNOPSIS 8 9 use Parrot::Pmc2c::PMC; 10 11=head1 DESCRIPTION 12 13C<Parrot::Pmc2c::PMC> is used by F<tools/build/pmc2c.pl> to generate C code from PMC files. 14 15=head2 Functions 16 17=over 18 19=cut 20 21package Parrot::Pmc2c::PMC; 22use strict; 23use warnings; 24use base qw( Exporter ); 25our @EXPORT_OK = qw(); 26use Storable (); 27use Parrot::PMC (); 28use Parrot::Pmc2c::Emitter (); 29use Parrot::Pmc2c::Method (); 30use Parrot::Pmc2c::UtilFunctions qw( 31 dont_edit 32 dynext_load_code 33 c_code_coda 34 gen_multi_name 35); 36use Parrot::Pmc2c::PMC::RO (); 37use Parrot::BuildUtil; 38 39sub create { 40 my ( $this, $pmc_classname ) = @_; 41 42 my $classname = ref($this) || $this; 43 44 # test to see if specific subclass exists 45 eval "use ${classname}::$pmc_classname"; 46 $classname = $@ ? "$classname" : "${classname}::${pmc_classname}"; 47 my $self = Parrot::Pmc2c::PMC->new; 48 bless $self, $classname; 49} 50 51sub new { 52 my ( $class, $self ) = @_; 53 54 $self ||= {}; 55 $self = { 56 attributes => [], 57 methods => [], 58 super => {}, 59 variant => '', 60 mixins => [], 61 %{$self}, 62 dynpmc_list => { map { $_ => 1 } 63 ( 'default', 'delegate', 'deleg_pmc', 'scalar' ) }, 64 }; 65 66 bless $self, $class; 67} 68 69sub dump { 70 my ($self) = @_; 71 72 # gen_parent_lookup_info( $self, $pmc2cMain, $pmcs ); 73 # gen_parent_reverse_lookup_info( $self, $pmcs, $vtable_dump ); 74 my $filename = $self->filename('.dump'); 75 Storable::nstore( $self, $filename ); 76 # add_to_generated( $filename, "[devel]", "src") unless $self->is_dynamic; 77} 78 79# methods 80sub add_method { 81 my ( $self, $method ) = @_; 82 die "FATAL ERROR: Duplicated VTABLE function: " . $method->name 83 if exists $self->{has_method}{$method->name}; 84 $self->{has_method}{ $method->name } = @{ $self->{methods} }; 85 push @{ $self->{methods} }, $method; 86} 87 88sub has_method { 89 my ( $self, $methodname ) = @_; 90 return exists $self->{has_method}{$methodname}; 91} 92 93sub method_index { 94 my ( $self, $methodname ) = @_; 95 return $self->{has_method}{$methodname}; 96} 97 98sub get_method { 99 my ( $self, $methodname ) = @_; 100 my $method_index = $self->method_index($methodname); 101 return unless defined $method_index; 102 return $self->{methods}[$method_index]; 103} 104 105sub inherits_method { 106 my ( $self, $vt_meth ) = @_; 107 return $self->super_method($vt_meth); 108} 109 110sub parent_has_method { 111 my ( $self, $parent_name, $vt_meth ) = @_; 112 return exists $self->{has_parent}{$parent_name}{$vt_meth}; 113} 114 115# parents 116sub is_parent { 117 my ( $self, $parent_name ) = @_; 118 return grep /$parent_name/, @{ $self->{parents} }; 119} 120 121sub add_parent { 122 my ( $self, $parent ) = @_; 123 my $parent_name = $parent->name; 124 $self->{has_parent}{$parent_name} = { %{ $parent->{has_method} } }; 125 push @{ $self->{parents} }, $parent_name unless $self->is_parent($parent_name); 126} 127 128sub add_mixin { 129 my ( $self, $mixin_name ) = @_; 130 push @{ $self->{mixins} }, $mixin_name unless grep /$mixin_name/, @{ $self->{mixins} }; 131} 132 133sub add_attribute { 134 my ( $self, $attribute ) = @_; 135 $self->{has_attribute}{ $attribute->{name} } = @{ $self->{attributes} }; 136 push @{ $self->{attributes} }, $attribute; 137} 138 139=item C<is_dynpmc> 140 141Determines if a given PMC type is dynamically loaded or not. 142 143=item C<implements_vtable($method)> 144 145True if pmc generates code for vtable C<$method>. 146 147=cut 148 149sub no_init { 150 my ($self) = @_; 151 return $self->flag('no_init'); 152} 153 154sub singleton { 155 my ($self) = @_; 156 return $self->flag('singleton'); 157} 158 159sub abstract { 160 my ($self) = @_; 161 return $self->flag('abstract'); 162} 163 164sub is_const { 165 my ($self) = @_; 166 return $self->flag('const'); 167} 168 169sub is_ro { 170 my ($self) = @_; 171 return $self->flag('ro'); 172} 173 174sub is_dynamic { 175 my ( $self, $pmcname ) = @_; 176 return $self->flag('dynpmc') unless $pmcname; 177 return 0 if exists $self->{dynpmc_list}->{$pmcname}; 178 return 0 if exists $Parrot::PMC::pmc_types{$pmcname}; 179 return 1; 180} 181 182sub export { 183 my ( $self ) = @_; 184 185 return $self->is_dynamic ? 'PARROT_DYNEXT_EXPORT' : 'PARROT_EXPORT'; 186} 187 188sub implements_vtable { 189 my ( $self, $vt_meth ) = @_; 190 return 0 unless $self->has_method($vt_meth); 191 return $self->get_method( $vt_meth )->is_vtable; 192} 193 194sub unimplemented_vtable { 195 my ( $self, $vt_meth ) = @_; 196 return 0 if $vt_meth eq 'class_init'; 197 return 0 if $self->has_method($vt_meth); 198 return 1; 199} 200 201sub normal_unimplemented_vtable { 202 my ( $self, $vt_meth ) = @_; 203 return 0 if $vt_meth eq 'class_init'; 204 return 0 if $self->has_method($vt_meth); 205 return 1; 206} 207 208# getters 209sub parents { 210 my ($self) = @_; 211 return $self->{parents}; 212} 213 214sub direct_parents { 215 my ($self) = @_; 216 return $self->{direct_parents}; 217} 218 219sub mixins { 220 my ($self) = @_; 221 return $self->{mixins}; 222} 223 224sub methods { 225 my ($self) = @_; 226 return $self->{methods}; 227} 228 229sub attributes { 230 my ($self) = @_; 231 return $self->{attributes}; 232} 233 234sub filename { 235 my ( $self, $type, $is_dynamic ) = @_; 236 return $self->{filename} unless $type; 237 return Parrot::Pmc2c::UtilFunctions::filename( $self->{filename}, $type, $is_dynamic ); 238} 239 240sub get_flags { 241 my ($self) = @_; 242 return $self->{flags}; 243} 244 245# setters 246# should only be called once by the pmc parser 247sub set_parents { 248 my ( $self, $value ) = @_; 249 $value ||= []; 250 $self->{parents} = $value; 251 for my $dp (@{ $value }) { 252 push @{$self->{direct_parents}}, $dp; 253 } 254 return 1; 255} 256 257sub set_flag { 258 my ( $self, $name, $value ) = @_; 259 $self->{flags}{$name} = ( $value or 1 ); 260 return $self->flag($name); 261} 262 263sub set_flags { 264 my ( $self, $flags ) = @_; 265 while ( my ( $name, $value ) = each( %{$flags} ) ) { 266 $self->set_flag( $name, $value ); 267 } 268} 269 270sub set_filename { 271 my ( $self, $value ) = @_; 272 $self->{filename} = $value if $value; 273 return 1; 274} 275 276# getters/setters 277sub name { 278 my ( $self, $value ) = @_; 279 $self->{name} = $value if $value; 280 return $self->{name}; 281} 282 283sub ro { 284 my ( $self, $value ) = @_; 285 $self->{ro} = $value if $value; 286 return $self->{ro}; 287} 288 289sub flag { 290 my ( $self, $name ) = @_; 291 return $self->{flags}{$name}; 292} 293 294sub preamble { 295 my ( $self, $value ) = @_; 296 $self->{preamble} = $value if $value; 297 return $self->{preamble}; 298} 299 300sub hdr_preamble { 301 my ( $self, $value ) = @_; 302 $self->{hdr_preamble} = $value if $value; 303 return $self->{hdr_preamble}; 304} 305 306sub postamble { 307 my ( $self, $value ) = @_; 308 $self->{postamble} = $value if $value; 309 return $self->{postamble}; 310} 311 312sub super_attrs { 313 my ( $self, $vt_name, $value ) = @_; 314 $self->{super_attrs}{$vt_name} = $value if $value; 315 return $self->{super_attrs}{$vt_name}; 316} 317 318# applies to vtable entries only 319sub method_attrs { 320 my ( $self, $methodname ) = @_; 321 my $attrs; 322 323 # try self 324 if ( $self->has_method($methodname) ) { 325 $attrs = $self->get_method($methodname)->attrs; 326 } 327 328 # try parent 329 elsif ( $self->inherits_method($methodname) ) { 330 $attrs = $self->super_attrs($methodname); 331 } 332 return $attrs; 333} 334 335=item C<vtable_method_does_write($method)> 336 337Returns true if the vtable C<$method> writes our value. 338 339=back 340 341=cut 342 343sub vtable_method_does_write { 344 my ( $self, $methodname ) = @_; 345 346 my $attrs = $self->method_attrs($methodname); 347 return 1 if $attrs->{write}; 348 return 0 if $attrs->{read}; 349 return $self->vtable->attrs($methodname)->{write}; 350} 351 352sub vtable_method_has_manual_wb { 353 my ( $self, $methodname ) = @_; 354 355 my $attrs = $self->method_attrs($methodname); 356 return $self->vtable->attrs($methodname)->{manual_wb}; 357} 358 359sub vtable_method_does_multi { 360 my ( $self, $methodname ) = @_; 361 362 return 1 if ($methodname =~ m/^ 363 (?:add|subtract|multiply|divide|floor_divide|modulus) 364 (?:_int|_float)? 365 $/x); 366} 367 368sub super_method { 369 my ( $self, $vt_meth, $super_pmc ) = @_; 370 if ($super_pmc) { 371 my $super_pmc_name; 372 if ( ref($super_pmc) ) { 373 my $super_method = $super_pmc->get_method($vt_meth); 374 $super_pmc_name = $super_method->parent_name; 375 376 $self->add_mixin($super_pmc_name) 377 unless $self->is_parent($super_pmc_name); 378 379 $self->super_attrs( $vt_meth, $super_method->attrs ); 380 381 $self->inherit_attrs($vt_meth) if $self->get_method($vt_meth); 382 } 383 else { 384 $super_pmc_name = $super_pmc; 385 } 386 $self->{super}{$vt_meth} = $super_pmc_name; 387 } 388 389 return $self->{super}{$vt_meth}; 390} 391 392=head3 C<inherit_attrs()> 393 394 $class = inherit_attrs($class, $meth); 395 396B<Purpose:> Modify $attrs to inherit attrs from $super_attrs as appropriate. 397 398B<Arguments:> List of two arguments: 399 400=over 4 401 402=item * 403 404Method name. 405 406=back 407 408B<Return Values:> Reference to hash holding the data structure being built up 409within C<dump_pmc()>. 410 411B<Comments:> Called within C<gen_super_meths()>. 412 413 414=cut 415 416sub inherit_attrs { 417 my ( $self, $vt_meth ) = @_; 418 my $attrs = $self->get_method($vt_meth)->attrs; 419 my $super_attrs = $self->super_attrs($vt_meth); 420 421 if ( ( $super_attrs->{read} or $super_attrs->{write} ) 422 and not( $attrs->{read} or $attrs->{write} ) ) 423 { 424 $attrs->{read} = $super_attrs->{read} if exists $super_attrs->{read}; 425 $attrs->{write} = $super_attrs->{write} if exists $super_attrs->{write}; 426 } 427 return $;; 428} 429 430=head2 These are auxiliary subroutines called inside the methods described above. 431 432=head3 C<dump_is_current()> 433 434 dump_is_current($existing); 435 436B<Purpose:> Determines whether the dump of a file is newer than the PMC file. 437(If it's not, then the PMC file has changed and the dump has not been updated.) 438 439B<Arguments:> String holding filename. 440 441B<Return Values:> Returns true if timestamp of existing is more recent than 442that of PMC. 443 444B<Comments:> Called within C<dump_pmc()>. 445 446=cut 447 448sub dump_is_current { 449 my ($self, $dumpfile) = @_; 450 $dumpfile ||= $self->filename('.dump'); 451 return 0 unless -e $dumpfile; 452 453 my $pmcfile = $self->filename('.pmc'); 454 return 1 unless -e $pmcfile; 455 456 return ( stat $dumpfile )[9] >= ( stat $pmcfile )[9]; 457} 458 459sub vtable { 460 my ( $self, $value ) = @_; 461 $self->{vtable} = $value if $value; 462 return $self->{vtable}; 463} 464 465 466sub prep_for_emit { 467 my ( $this, $pmc, $vtable_dump ) = @_; 468 469 $pmc->vtable($vtable_dump); 470 $pmc->init(); 471 472 return $pmc; 473} 474 475sub generate { 476 my ($self) = @_; 477 478 my $c_file = $self->filename(".c"); 479 my $c_emitter = $self->{emitter} = 480 Parrot::Pmc2c::Emitter->new( $c_file ); 481 $self->generate_c_file; 482 $c_emitter->write_to_file; 483 # add_to_generated($c_file, "[]", ""); 484 485 my $h_file = $self->filename(".h", $self->is_dynamic); 486 my $h_emitter = $self->{emitter} = 487 Parrot::Pmc2c::Emitter->new( $h_file ); 488 $self->generate_h_file; 489 $h_emitter->write_to_file; 490 #add_to_generated($h_file, "[devel]", "include") 491 # unless $self->is_dynamic and $self->name =~ /^(foo|foo2|rotest|pccmethod_test)$/; 492} 493 494=over 4 495 496=item C<generate_c_file()> 497 498Generates the C implementation file code for the PMC. 499 500=cut 501 502sub generate_c_file { 503 my ($self) = @_; 504 my $c = $self->{emitter}; 505 506 $c->emit( dont_edit( $self->filename ) ); 507 if ($self->is_dynamic) { 508 my $uc_name = uc $self->name; 509 $c->emit("#define PARROT_IN_EXTENSION\n"); 510 $c->emit("#define PARROT_DYNPMC_CLASS_LOAD\n"); 511 $c->emit("#define CONST_STRING(i, s) Parrot_str_new_constant((i), s)\n"); 512 $c->emit("#define CONST_STRING_GEN(i, s) Parrot_str_new_constant((i), s)\n"); 513 } 514 515 $self->gen_includes; 516 517 # The PCC code needs Continuation-related macros from these headers. 518 $c->emit("#include \"pmc_continuation.h\"\n"); 519 $c->emit("#include \"pmc_callcontext.h\"\n"); 520 $c->emit("#undef PARROT_DYNPMC_CLASS_LOAD\n") if $self->is_dynamic; 521 522 $c->emit( $self->preamble ); 523 524 $c->emit( $self->hdecls ) unless $self->name eq 'CallContext'; 525 $c->emit( $self->{ro}->hdecls ) if ( $self->{ro} ); 526 $self->gen_methods; 527 528 my $ro = $self->ro; 529 if ($ro) { 530 $ro->{emitter} = $self->{emitter}; 531 $ro->gen_methods; 532 } 533 534 $c->emit("#include \"pmc_default.h\"\n"); 535 536 $c->emit( $self->update_vtable_func ); 537 $c->emit( $self->get_vtable_func ); 538 $c->emit( $self->get_mro_func ); 539 $c->emit( $self->get_isa_func ); 540 $c->emit( $self->pmc_class_init_func ); 541 $c->emit( $self->init_func ); 542 $c->emit( $self->postamble ); 543 544 return 1; 545} 546 547=item C<generate_h_file()> 548 549Generates the C header file code for the PMC. 550 551=cut 552 553sub generate_h_file { 554 my ($self) = @_; 555 my $h = $self->{emitter}; 556 my $uc_name = uc $self->name; 557 my $lc_name = lc $self->name; 558 my $name = $self->name; 559 560 $h->emit( dont_edit( $self->filename ) ); 561 $h->emit(<<"EOH"); 562 563#ifndef PARROT_PMC_${uc_name}_H_GUARD 564#define PARROT_PMC_${uc_name}_H_GUARD 565 566EOH 567 568 $h->emit("#define PARROT_IN_EXTENSION\n") if ( $self->is_dynamic ); 569 570 # Emit header preamble 571 $h->emit($self->hdr_preamble) if $self->hdr_preamble; 572 573 # Emit available functions for work with vtables. 574 my $export = 'PARROT_EXPORT '; 575 if ($self->is_dynamic) { 576 $export = 'PARROT_DYNEXT_EXPORT '; 577 $h->emit("${export}VTABLE* Parrot_${name}_get_vtable_pointer(PARROT_INTERP);\n"); 578 $h->emit("${export}void Parrot_${name}_class_init(PARROT_INTERP, int, int);\n"); 579 } 580 581 if ($name ne 'default') { 582 $h->emit("${export}VTABLE* Parrot_${name}_update_vtable(ARGMOD(VTABLE*));\n"); 583 $h->emit("${export}VTABLE* Parrot_${name}_ro_update_vtable(ARGMOD(VTABLE*));\n"); 584 } 585 $h->emit("${export}VTABLE* Parrot_${name}_get_vtable(PARROT_INTERP);\n"); 586 $h->emit("${export}VTABLE* Parrot_${name}_ro_get_vtable(PARROT_INTERP);\n"); 587 $h->emit("${export}PMC* Parrot_${name}_get_mro(PARROT_INTERP, ARGMOD(PMC* mro));\n"); 588 $h->emit("${export}Hash* Parrot_${name}_get_isa(PARROT_INTERP, ARGMOD_NULLOK(Hash* isa));\n"); 589 590 $self->gen_attributes; 591 592 if ($name eq 'CallContext') { 593 $h->emit( $self->hdecls ); 594 } 595 596 if ($self->is_dynamic) { 597 $h->emit(<<"EOH"); 598 599${export}Parrot_PMC Parrot_lib_${lc_name}_load(PARROT_INTERP); 600 601#ifndef PARROT_DYNPMC_CLASS_LOAD 602PARROT_DATA INTVAL dynpmc_class_${name}; 603#endif 604EOH 605 } 606 607 $h->emit(<<"EOH"); 608 609#endif /* PARROT_PMC_${uc_name}_H_GUARD */ 610 611EOH 612 $h->emit( c_code_coda() ); 613 return 1; 614} 615 616=item C<hdecls()> 617 618Returns the C code function declarations for all the methods for inclusion 619in the PMC's C header file. 620 621=cut 622 623sub hdecls { 624 my ($self) = @_; 625 626 my $hout = ''; 627 my $name = $self->name; 628 my $lc_name = lc($name); 629 630 # generate decls for all vtables in this PMC 631 foreach my $vt_method_name ( @{ $self->vtable->names } ) { 632 if ( $self->implements_vtable($vt_method_name) ) { 633 $hout .= 634 $self->get_method($vt_method_name)->generate_headers($self); 635 } 636 } 637 638 # generate decls for all nci methods in this PMC 639 foreach my $method ( @{ $self->{methods} } ) { 640 next if $method->is_vtable; 641 $hout .= $method->generate_headers($self); 642 } 643 644 $self->{hdecls} .= $hout; 645 return $self->{hdecls}; 646} 647 648=back 649 650=head2 Instance Methods 651 652=over 653 654=item C<init()> 655 656Initializes the instance. 657 658=cut 659 660sub init { 661 my ($self) = @_; 662 663 #!( singleton or abstract ) everything else gets readonly version of 664 # methods too. 665 666 $self->ro( Parrot::Pmc2c::PMC::RO->new($self) ) 667 unless $self->abstract or $self->singleton; 668} 669 670=item C<gen_includes()> 671 672Returns the C C<#include> for the header file of each of the PMC's superclasses. 673 674=cut 675 676sub gen_includes { 677 my ($self) = @_; 678 my $c = $self->{emitter}; 679 680 $c->emit(<<"EOC"); 681#include "parrot/parrot.h" 682#include "parrot/extend.h" 683#include "parrot/dynext.h" 684EOC 685 686 $c->emit(qq{#include "pmc_fixedintegerarray.h"\n}) 687 if $self->flag('need_fia_header'); 688 689 foreach my $parent_name ( $self->name, @{ $self->parents } ) { 690 $c->emit( '#include "pmc_' . lc $parent_name . ".h\"\n" ); 691 } 692 693 foreach my $mixin_name ( @{ $self->mixins } ) { 694 $c->emit( '#include "pmc_' . lc $mixin_name . ".h\"\n" ); 695 } 696 697 $c->emit( '#include "' . lc $self->name . ".str\"\n" ) 698 unless $self->is_dynamic; 699} 700 701=item C<pre_method_gen> 702 703Generate switch-bases VTABLE for MULTI 704 705=cut 706 707sub pre_method_gen { 708 my ($self) = @_; 709 710 $self->gen_switch_vtable; 711 712 1; 713} 714 715=item C<post_method_gen> 716 717Generate write barriers. 718 719=cut 720 721sub post_method_gen { 722 my ($self) = @_; 723 724 # vtables 725 foreach my $method ( @{ $self->vtable->methods } ) { 726 my $name = $method->name; 727 next if $name eq 'class_init'; 728 next unless $self->implements_vtable($name); 729 # Skip non-updating methods 730 next unless $self->vtable_method_does_write($name); 731 732 # Skip methods with manual WBs. 733 next if $method->vtable_method_has_manual_wb; 734 next if $self->vtable_method_has_manual_wb($name); 735 736 # Skip unimplemented methods 737 next if $self->unimplemented_vtable($name); 738 739 # Skip for Proxy and Null, they just raise an exception 740 next if $self->name =~ /^Null|Proxy/; 741 742 $method = $self->get_method($name); 743 744 # Rewrite RETURNs or add simple write barrier to body 745 $method->{need_write_barrier} = 1; 746 $method->body->add_write_barrier($method, $self); 747 } 748 749 # generate PCC-variants for multis 750 foreach ( @{ $self->find_multi_functions } ) { 751 my ($name, $fsig, $ns, $func, $method) = @$_; 752 (my $new_name = $method->full_method_name($self->name) . '_pcc') =~ s/.*?_multi_/multi_/; 753 my $new_method = $method->clone({ 754 name => $new_name, 755 type => "MULTI_PCC", 756 parameters => '', 757 return_type => 'void' 758 }); 759 760 # Get parameters. Strip type from param 761 my @parameters = map { /\s*\*?(\S+)$/; $1 } (split /,/, $method->parameters); 762 763 my $need_result = $method->return_type && $method->return_type !~ 'void'; 764 765 (my $pcc_sig) = $method->pcc_signature; 766 my ($pcc_args, $pcc_ret) = $pcc_sig =~ /(.*)->(.*)/; 767 768 # Get paramete storage. Types are already provided, but we need semi-colon delimitation. 769 (my $body = $method->parameters) =~ s/,/;/g; 770 $body .= ";\n"; 771 $body .= $method->return_type . " _result;\n" if $need_result; 772 $body .= "PMC *_call_obj = Parrot_pcc_get_signature(interp, CURRENT_CONTEXT(interp));\n"; 773 774 # pcc params 775 $body .= "Parrot_pcc_fill_params_from_c_args(interp, _call_obj, \"Pi$pcc_args\", &_self" . 776 (join '', map { ", &$_" } @parameters) . ");\n"; 777 778 # C call 779 $body .= "_result = " if $need_result; 780 my $parameters = join ', ', 'INTERP', 'SELF', @parameters; 781 $body .= $method->full_method_name($self->name) . "($parameters);\n"; 782 783 # pcc return 784 $body .= <<EOC if $need_result; 785 Parrot_pcc_set_call_from_c_args(interp, _call_obj, "$pcc_ret", _result); 786EOC 787 788 $new_method->body(Parrot::Pmc2c::Emitter->text($body)); 789 $self->add_method($new_method); 790 } 791} 792 793=item C<gen_methods()> 794 795Returns the C code for the pmc methods. 796 797=cut 798 799sub gen_methods { 800 my ($self) = @_; 801 802 # vtables 803 foreach my $method ( @{ $self->vtable->methods } ) { 804 my $vt_method_name = $method->name; 805 next if $vt_method_name eq 'class_init'; 806 807 if ( $self->implements_vtable($vt_method_name) ) { 808 $self->get_method($vt_method_name)->generate_body($self); 809 } 810 } 811 812 # methods 813 foreach my $method ( @{ $self->methods } ) { 814 next if $method->is_vtable; 815 $method->generate_body($self); 816 } 817} 818 819=item C<gen_attributes()> 820 821Returns the C code for the attribute struct definition. 822 823=cut 824 825sub gen_attributes { 826 my ($self) = @_; 827 828 if ( @{$self->attributes} ) { 829 830 $self->generate_start(); 831 832 foreach my $attr ( @{$self->attributes} ) { 833 $self->generate_declaration($attr); 834 } 835 836 $self->generate_end(); 837 838 foreach my $attr ( @{$self->attributes} ) { 839 $self->generate_accessor($attr); 840 } 841 } 842} 843 844=item C<find_multi_functions()> 845 846Returns an arrayref of MULTI function names declared in the PMC. Used to 847initialize the multiple dispatch function list. 848 849=cut 850 851sub find_multi_functions { 852 my ($self) = @_; 853 854 my $pmcname = $self->name; 855 my ( @multi_names ); 856 857 foreach my $method ( @{ $self->methods } ) { 858 next unless $method->is_multi; 859 my $full_sig = $pmcname . "," . $method->{MULTI_full_sig}; 860 my $functionname = 'Parrot_' . $pmcname . '_' . $method->name; 861 push @multi_names, [ $method->symbol, $full_sig, 862 $pmcname, $functionname, $method ]; 863 } 864 return ( \@multi_names ); 865} 866 867sub build_full_c_vt_method_name { 868 my ( $self, $vt_method_name ) = @_; 869 870 my $implementor; 871 if ( $self->implements_vtable($vt_method_name) ) { 872 return $self->get_method($vt_method_name) 873 ->full_method_name( $self->name . $self->{variant} ); 874 } 875 elsif ( $self->{super}{$vt_method_name} ) { 876 $implementor = $self->{super}{$vt_method_name}; 877 } 878 else { 879 $implementor = "default"; 880 } 881 882 return "Parrot_${implementor}_$vt_method_name"; 883} 884 885=item C<vtable_flags()> 886 887Returns C code to produce a PMC's flags. 888 889=cut 890 891sub vtable_flags { 892 my ($self) = @_; 893 894 my $vtbl_flag = 0; 895 $vtbl_flag .= '|VTABLE_PMC_IS_SINGLETON' if $self->flag('singleton'); 896 $vtbl_flag .= '|VTABLE_IS_SHARED_FLAG' if $self->flag('is_shared'); 897 $vtbl_flag .= '|VTABLE_IS_READONLY_FLAG' if $self->flag('is_ro'); 898 $vtbl_flag .= '|VTABLE_HAS_READONLY_FLAG' if $self->flag('has_ro'); 899 900 return $vtbl_flag; 901} 902 903=item C<vtable_decl($name)> 904 905Returns the C code for the declaration of a vtable temporary named 906C<$name> with the functions for this class. 907 908=cut 909 910sub vtable_decl { 911 my ( $self, $temp_struct_name, $enum_name ) = @_; 912 913 # gen vtable flags 914 my $vtbl_flag = $self->vtable_flags; 915 916 my @vt_methods; 917 foreach my $vt_method ( @{ $self->vtable->methods } ) { 918 push @vt_methods, 919 $self->build_full_c_vt_method_name( $vt_method->name ); 920 } 921 922 my $methlist = join( ",\n ", @vt_methods ); 923 924 my $cout = <<ENDOFCODE; 925 static const VTABLE $temp_struct_name = { 926 NULL, /* namespace */ 927 $enum_name, /* base_type */ 928 NULL, /* whoami */ 929 $vtbl_flag, /* flags */ 930 NULL, /* provides_str */ 931 NULL, /* isa_hash */ 932 NULL, /* class */ 933 NULL, /* mro */ 934 NULL, /* attribute_defs */ 935 NULL, /* ro_variant_vtable */ 936 $methlist, 937 0 /* attr size */ 938 }; 939ENDOFCODE 940 return $cout; 941} 942 943=item C<pmc_class_init_func()> 944 945Returns the C code for the PMC's class_init function as a static 946function to be called from the exported class_init. 947 948=cut 949 950sub pmc_class_init_func { 951 my ($self) = @_; 952 my $class_init_code = ""; 953 954 if ($self->has_method('class_init')) { 955 $class_init_code .= $self->get_method('class_init')->body; 956 957 $class_init_code =~ s/INTERP/interp/g; 958 959 # fix indenting 960 $class_init_code =~ s/^/ /mg; 961 $class_init_code = <<"ENDOFCODE"; 962static void thispmc_class_init(PARROT_INTERP, int entry) 963{ 964$class_init_code 965} 966ENDOFCODE 967 } 968 return $class_init_code; 969} 970 971=item C<init_func()> 972 973Returns the C code for the PMC's initialization method, or an empty 974string if the PMC has a C<no_init> flag. 975 976=cut 977 978sub init_func { 979 my ($self) = @_; 980 return '' if $self->no_init; 981 982 my $cout = ''; 983 my $classname = $self->name; 984 my $enum_name = $self->is_dynamic ? -1 : "enum_class_$classname"; 985 my $multi_funcs = $self->find_multi_functions(); 986 987 my @multi_list; 988 my %strings_seen; 989 my $multi_strings = ''; 990 my $cache = {}; 991 992 my $i = 0; 993 for my $multi (@$multi_funcs) { 994 my ($name, $fsig, $ns, $func) = @$multi; 995 my ($name_str, $fsig_str, $ns_name) = 996 map { gen_multi_name($_, $cache) } ($name, $fsig, $ns); 997 998 for my $s ([$name, $name_str], 999 [$fsig, $fsig_str], 1000 [$ns, $ns_name ]) { 1001 my ($raw_string, $name) = @$s; 1002 next if $strings_seen{$name}++; 1003 $multi_strings .= " STRING * const $name = " 1004 . qq|CONST_STRING_GEN(interp, "$raw_string");\n|; 1005 } 1006 1007 push @multi_list, <<END_MULTI_LIST; 1008_temp_func = Parrot_pmc_new(interp, enum_class_NativePCCMethod); 1009VTABLE_set_pointer_keyed_str(interp, _temp_func, CONST_STRING(interp, "->"), (void *)${func}_pcc); 1010Parrot_mmd_add_multi_from_long_sig(interp, $name_str, $fsig_str, _temp_func); 1011END_MULTI_LIST 1012 $i++; 1013 1014 } 1015 1016 my $multi_list_size = @multi_list; 1017 my $multi_list = join( "\n", @multi_list); 1018 1019 my $provides = join( " ", keys( %{ $self->{flags}{provides} } ) ); 1020 my $class_init_code = ""; 1021 1022 if ($self->has_method('class_init')) { 1023 $class_init_code .= " thispmc_class_init(interp, entry);\n"; 1024 } 1025 1026 my %extra_vt; 1027 $extra_vt{ro} = $self->{ro} if $self->{ro}; 1028 1029 $cout .= <<"EOC"; 1030void 1031Parrot_${classname}_class_init(PARROT_INTERP, int entry, int pass) 1032{ 1033 static const char attr_defs [] = 1034EOC 1035 $cout .= ' "'; 1036 1037 my $attributes = $self->attributes; 1038 foreach my $attribute ( @$attributes ) { 1039 my $attrtype = $attribute->{type}; 1040 my $attrname = $attribute->{name}; 1041 my $typeid = ':'; # Unhandled 1042 if($attrname =~ m/\(*(\w+)\)\(.*?\)/) { 1043 $attrname = $1; 1044 } 1045 elsif ($attrtype eq "INTVAL") { 1046 $typeid = 'I'; 1047 } 1048 elsif ($attrtype eq "FLOATVAL") { 1049 $typeid = 'F'; 1050 } 1051 elsif ($attrtype =~ /STRING\s*\*$/) { 1052 $typeid = 'S'; 1053 } 1054 elsif ($attrtype =~ /PMC\s*\*$/) { 1055 $typeid = 'F'; 1056 } 1057 1058 $cout .= $typeid; 1059 $cout .= $attrname; 1060 $cout .= ' '; 1061 } 1062 1063 $cout .= "\";\n"; 1064 1065 my $const = ( $self->{flags}{dynpmc} ) ? " " : " const "; 1066 1067 my $flags = $self->vtable_flags; 1068 $cout .= <<"EOC"; 1069 if (pass == 0) { 1070 VTABLE * const vt = Parrot_${classname}_get_vtable(interp); 1071 vt->base_type = $enum_name; 1072 vt->flags = $flags; 1073 vt->attribute_defs = attr_defs; 1074 interp->vtables[entry] = vt; 1075 1076EOC 1077 1078 # init vtable slot 1079 if ( $self->is_dynamic ) { 1080 $cout .= <<"EOC"; 1081 vt->base_type = entry; 1082 vt->whoami = Parrot_str_new_init(interp, "$classname", @{[length($classname)]}, 1083 Parrot_ascii_encoding_ptr, PObj_constant_FLAG|PObj_external_FLAG); 1084 vt->provides_str = Parrot_str_concat(interp, vt->provides_str, 1085 Parrot_str_new_init(interp, "$provides", @{[length($provides)]}, Parrot_ascii_encoding_ptr, 1086 PObj_constant_FLAG|PObj_external_FLAG)); 1087 1088EOC 1089 } 1090 else { 1091 $cout .= <<"EOC"; 1092 vt->whoami = CONST_STRING_GEN(interp, "$classname"); 1093 vt->provides_str = CONST_STRING_GEN(interp, "$provides"); 1094EOC 1095 } 1096 1097 $cout .= <<"EOC"; 1098 vt->isa_hash = Parrot_${classname}_get_isa(interp, NULL); 1099EOC 1100 1101 for my $k ( sort keys %extra_vt ) { 1102 my $k_flags = $self->$k->vtable_flags; 1103 my $var = "vt_$k"; 1104 $cout .= <<"EOC"; 1105 { 1106 VTABLE * const $var = Parrot_${classname}_${k}_get_vtable(interp); 1107 ${var}->base_type = $enum_name; 1108 ${var}->flags = $k_flags; 1109 1110 ${var}->attribute_defs = attr_defs; 1111 1112 ${var}->base_type = entry; 1113 ${var}->whoami = vt->whoami; 1114 ${var}->provides_str = vt->provides_str; 1115 vt->${k}_variant_vtable = ${var}; 1116 ${var}->${k}_variant_vtable = vt; 1117 ${var}->isa_hash = vt->isa_hash; 1118 } 1119 1120EOC 1121 } 1122 1123 $cout .= <<"EOC"; 1124 } 1125 else { /* pass */ 1126EOC 1127 1128 # To make use of the .HLL directive, register any mapping... 1129 if ( $self->{flags}{hll} && $self->{flags}{maps} ) { 1130 1131 my $hll = $self->{flags}{hll}; 1132 $cout .= <<"EOC"; 1133 1134 { 1135 /* Register this PMC as a HLL mapping */ 1136 INTVAL hll_id = Parrot_hll_register_HLL( interp, CONST_STRING_GEN(interp, "$hll")); 1137EOC 1138 foreach my $maps ( sort keys %{ $self->{flags}{maps} } ) { 1139 $cout .= <<"EOC"; 1140 Parrot_hll_register_HLL_type( interp, hll_id, enum_class_$maps, entry); 1141EOC 1142 } 1143 $cout .= <<"EOC"; 1144 } /* Register */ 1145EOC 1146 } 1147 1148 $cout .= <<"EOC"; 1149 { 1150 VTABLE * const vt = interp->vtables[entry]; 1151 1152 vt->mro = Parrot_${classname}_get_mro(interp, PMCNULL); 1153 1154 if (vt->ro_variant_vtable) 1155 vt->ro_variant_vtable->mro = vt->mro; 1156 } 1157 1158 /* set up MRO and _namespace */ 1159 Parrot_pmc_create_mro(interp, entry); 1160EOC 1161 1162 # declare each nci method for this class 1163 foreach my $method ( @{ $self->{methods} } ) { 1164 next unless $method->type eq Parrot::Pmc2c::Method::NON_VTABLE; 1165 1166 #these differ for METHODs 1167 my $method_name = $method->name; 1168 my $symbol_name = $method->symbol; 1169 my ($pcc_signature) = $method->pcc_signature; 1170 1171 $cout .= <<"EOC"; 1172 { 1173 STRING * const method_name = CONST_STRING_GEN(interp, "$symbol_name"); 1174 STRING * const signature = CONST_STRING_GEN(interp, "$pcc_signature"); 1175 Parrot_interp_register_native_pcc_method_in_ns(interp, entry, 1176 F2DPTR(Parrot_${classname}_${method_name}), 1177 method_name, signature); 1178 } 1179EOC 1180 if ( $method->{attrs}{write} ) { 1181 $cout .= <<"EOC"; 1182 Parrot_interp_mark_method_writes(interp, entry, "$symbol_name"); 1183EOC 1184 } 1185 } 1186 1187 # include any class specific init code from the .pmc file 1188 if ($class_init_code) { 1189 $cout .= <<"EOC"; 1190 1191 /* class_init */ 1192$class_init_code 1193 1194EOC 1195 } 1196 1197 $cout .= <<"EOC"; 1198 { 1199EOC 1200 1201 1202 if ( @$multi_funcs ) { 1203 # Don't const the list, breaks some older C compilers 1204 $cout .= $multi_strings . <<"EOC"; 1205 PMC *_temp_func; 1206$multi_list 1207EOC 1208 } 1209 1210 $cout .= <<"EOC"; 1211 } 1212 } /* pass */ 1213} /* Parrot_${classname}_class_init */ 1214 1215EOC 1216 1217 if ( $self->is_dynamic ) { 1218 $cout .= dynext_load_code( $classname, $classname => {} ); 1219 } 1220 1221 $cout; 1222} 1223 1224=item C<update_vtable_func()> 1225 1226Returns the C code for the PMC's update_vtable. 1227 1228=cut 1229 1230sub update_vtable_func { 1231 my ($self) = @_; 1232 1233 my $cout = ""; 1234 my $classname = $self->name; 1235 my $export = $self->is_dynamic ? 'PARROT_DYNEXT_EXPORT ' : 'PARROT_EXPORT'; 1236 1237 # Sets the attr_size field: 1238 # - If the auto_attrs flag is set, use the current data. 1239 # - If manual_attrs is set, set to 0. 1240 # - If none is set, check if this PMC has init or init_pmc vtable functions, 1241 # setting it to 0 in that case, and keeping the value from the 1242 # parent otherwise. 1243 my $set_attr_size = ''; 1244 my $flag_auto_attrs = $self->{flags}{auto_attrs}; 1245 my $flag_manual_attrs = $self->{flags}{manual_attrs}; 1246 die 'manual_attrs and auto_attrs can not be used together' 1247 . 'in PMC ' . $self->name 1248 if ($flag_auto_attrs && $flag_manual_attrs); 1249 die 'PMC ' . $self->name . ' has attributes but no auto_attrs or manual_attrs' 1250 if (@{$self->attributes} && ! ($flag_auto_attrs || $flag_manual_attrs)); 1251 1252 if ( @{$self->attributes} && $flag_auto_attrs) { 1253 $set_attr_size .= "sizeof(Parrot_${classname}_attributes)"; 1254 } 1255 else { 1256 $set_attr_size .= "0" if $flag_manual_attrs || 1257 exists($self->{has_method}{init}) || 1258 exists($self->{has_method}{init_pmc}); 1259 } 1260 $set_attr_size = " vt->attr_size = " . $set_attr_size . ";\n" 1261 if $set_attr_size ne ''; 1262 1263 my $vtable_updates = ''; 1264 for my $name ( @{ $self->vtable->names } ) { 1265 if (exists $self->{has_method}{$name}) { 1266 $vtable_updates .= " vt->$name = Parrot_${classname}_${name};\n"; 1267 } 1268 } 1269 1270 $vtable_updates .= $set_attr_size; 1271 1272 $cout .= <<"EOC"; 1273 1274$export 1275VTABLE *Parrot_${classname}_update_vtable(VTABLE *vt) { 1276$vtable_updates 1277 return vt; 1278} 1279 1280EOC 1281 1282 # Generate RO vtable for implemented non-updating methods 1283 $vtable_updates = ''; 1284 foreach my $name ( @{ $self->vtable->names} ) { 1285 next unless exists $self->{has_method}{$name}; 1286 if ($self->vtable_method_does_write($name)) { 1287 # If we override constantness status of vtable 1288 if (!$self->vtable->attrs($name)->{write}) { 1289 $vtable_updates .= " vt->$name = Parrot_${classname}_ro_${name};\n"; 1290 } 1291 } 1292 else { 1293 $vtable_updates .= " vt->$name = Parrot_${classname}_${name};\n"; 1294 } 1295 } 1296 1297 $vtable_updates .= $set_attr_size; 1298 1299 $cout .= <<"EOC"; 1300 1301$export 1302VTABLE *Parrot_${classname}_ro_update_vtable(ARGMOD(VTABLE *vt)) { 1303$vtable_updates 1304 return vt; 1305} 1306 1307EOC 1308 1309 $cout; 1310} 1311 1312=item C<get_mro_func()> 1313 1314Returns the C code for the PMC's get_mro function. 1315 1316=cut 1317 1318sub get_mro_func { 1319 my ($self) = @_; 1320 1321 my $cout = ""; 1322 my $classname = $self->name; 1323 my $get_mro = ''; 1324 my @parent_names; 1325 my $export = $self->is_dynamic ? 'PARROT_DYNEXT_EXPORT ' : 'PARROT_EXPORT'; 1326 1327 if ($classname ne 'default') { 1328 for my $dp (reverse @{ $self->direct_parents}) { 1329 $get_mro .= " mro = Parrot_${dp}_get_mro(interp, mro);\n" 1330 unless $dp eq 'default'; 1331 } 1332 } 1333 1334 $cout .= <<"EOC"; 1335$export 1336PARROT_CANNOT_RETURN_NULL 1337PARROT_WARN_UNUSED_RESULT 1338PMC* Parrot_${classname}_get_mro(PARROT_INTERP, ARGMOD(PMC* mro)) { 1339 if (PMC_IS_NULL(mro)) { 1340 mro = Parrot_pmc_new(interp, enum_class_ResizableStringArray); 1341 } 1342$get_mro 1343 VTABLE_unshift_string(interp, mro, CONST_STRING_GEN(interp, "$classname")); 1344 return mro; 1345} 1346 1347EOC 1348 1349 $cout; 1350} 1351 1352=item C<get_isa_func()> 1353 1354Returns the C code for the PMC's get_isa function. 1355 1356=cut 1357 1358sub get_isa_func { 1359 my ($self) = @_; 1360 1361 my $cout = ""; 1362 my $classname = $self->name; 1363 my $get_isa = ''; 1364 my @parent_names; 1365 my $export = $self->is_dynamic ? 'PARROT_DYNEXT_EXPORT ' : 'PARROT_EXPORT'; 1366 1367 if ($classname ne 'default') { 1368 for my $dp (reverse @{ $self->direct_parents}) { 1369 $get_isa .= " isa = Parrot_${dp}_get_isa(interp, isa);\n" 1370 unless $dp eq 'default'; 1371 } 1372 } 1373 1374 $cout .= <<"EOC"; 1375$export 1376PARROT_CANNOT_RETURN_NULL 1377PARROT_WARN_UNUSED_RESULT 1378Hash* Parrot_${classname}_get_isa(PARROT_INTERP, ARGIN_NULLOK(Hash* isa)) { 1379EOC 1380 1381 if ($get_isa ne '') { 1382 $cout .= $get_isa; 1383 } 1384 else { 1385 $cout .= <<"EOC"; 1386 if (isa == NULL) { 1387 isa = Parrot_hash_new(interp); 1388 } 1389EOC 1390 } 1391 $cout .= <<"EOC"; 1392 Parrot_hash_put(interp, isa, (void *)(CONST_STRING_GEN(interp, "$classname")), PMCNULL); 1393 return isa; 1394} 1395 1396EOC 1397 1398 $cout; 1399} 1400 1401 1402=item C<get_vtable_func()> 1403 1404Returns the C code for the PMC's update_vtable. 1405 1406=cut 1407 1408sub get_vtable_func { 1409 my ($self) = @_; 1410 1411 my $cout = ""; 1412 my $classname = $self->name; 1413 my @other_parents = reverse @{ $self->direct_parents }; 1414 my $first_parent = shift @other_parents; 1415 my $export = $self->is_dynamic ? 'PARROT_DYNEXT_EXPORT ' : 'PARROT_EXPORT'; 1416 1417 my $get_vtable = ''; 1418 1419 if ($first_parent eq 'default') { 1420 $get_vtable .= " vt = Parrot_default_get_vtable(interp);\n"; 1421 } 1422 else { 1423 $get_vtable .= " vt = Parrot_${first_parent}_get_vtable(interp);\n"; 1424 } 1425 1426 foreach my $parent_name ( @other_parents) { 1427 $get_vtable .= " Parrot_${parent_name}_update_vtable(vt);\n"; 1428 } 1429 1430 $get_vtable .= " Parrot_${classname}_update_vtable(vt);\n"; 1431 1432 $cout .= <<"EOC"; 1433$export 1434PARROT_CANNOT_RETURN_NULL 1435PARROT_WARN_UNUSED_RESULT 1436VTABLE* Parrot_${classname}_get_vtable(PARROT_INTERP) { 1437 VTABLE *vt; 1438$get_vtable 1439 return vt; 1440} 1441 1442EOC 1443 1444 my $get_extra_vtable = ''; 1445 1446 if ($first_parent eq 'default') { 1447 $get_extra_vtable .= " vt = Parrot_default_ro_get_vtable(interp);\n"; 1448 } 1449 else { 1450 $get_extra_vtable .= " vt = Parrot_${first_parent}_ro_get_vtable(interp);\n"; 1451 } 1452 1453 foreach my $parent_name ( @other_parents ) { 1454 $get_extra_vtable .= " Parrot_${parent_name}_ro_update_vtable(vt);\n"; 1455 } 1456 1457 if ($self->is_dynamic) { 1458 # The C could be optimized, but the case when Parrot_x_get_vtable_pointer 1459 # is needed is very rare. See TT #898 for more info. 1460 $cout .= <<"EOC"; 1461$export 1462PARROT_CANNOT_RETURN_NULL 1463PARROT_WARN_UNUSED_RESULT 1464VTABLE* Parrot_${classname}_get_vtable_pointer(PARROT_INTERP) { 1465 STRING * const type_name = Parrot_str_new_constant(interp, "${classname}"); 1466 const INTVAL type_num = Parrot_pmc_get_type_str(interp, type_name); 1467 return interp->vtables[type_num]; 1468} 1469 1470EOC 1471 } 1472 1473 $get_extra_vtable .= " Parrot_${classname}_ro_update_vtable(vt);\n"; 1474 1475 $cout .= <<"EOC"; 1476$export 1477PARROT_CANNOT_RETURN_NULL 1478PARROT_WARN_UNUSED_RESULT 1479VTABLE* Parrot_${classname}_ro_get_vtable(PARROT_INTERP) { 1480 VTABLE *vt; 1481$get_extra_vtable 1482 return vt; 1483} 1484 1485EOC 1486 1487 $cout; 1488} 1489 1490sub is_vtable_method { 1491 my ( $self, $vt_method_name ) = @_; 1492 return 1 if $self->vtable->has_method($vt_method_name); 1493 return 0; 1494} 1495 1496=item C<gen_switch_vtable> 1497 1498Generate switch-bases VTABLE for MULTI 1499 1500=back 1501 1502=cut 1503 1504sub gen_switch_vtable { 1505 my ($self) = @_; 1506 1507 # No cookies for DynPMC. At least not now. 1508 return 1 if $self->is_dynamic; 1509 1510 # Convert list of multis to name->[(type,fsig,ns,func,method)] hash. 1511 my %multi_methods; 1512 foreach (@{$self->find_multi_functions}) { 1513 my ($name, $fsig, $ns, $func, $method) = @$_; 1514 my @sig = split /,/, $fsig; 1515 push @{ $multi_methods{ $name } }, [ $sig[1], $fsig, $ns, $func, $method ]; 1516 } 1517 1518 # vtables 1519 foreach my $method ( @{ $self->vtable->methods } ) { 1520 my $vt_method_name = $method->name; 1521 next if $vt_method_name eq 'class_init'; 1522 1523 next if $self->implements_vtable($vt_method_name); 1524 next unless exists $multi_methods{$vt_method_name}; 1525 1526 my $multis = $multi_methods{$vt_method_name}; 1527 1528 # Get parameters. strip type from param 1529 my @parameters = map { s/(\s*\S+\s*\*?\s*)//; $_ } split (/,/, $method->parameters); 1530 1531 # Gather "case :" 1532 my @cases = map { $self->generate_single_case($vt_method_name, $_, @parameters) } @$multis; 1533 my $cases = join "", @cases; 1534 1535 my $body = <<"BODY"; 1536 INTVAL type = VTABLE_type(INTERP, $parameters[0]); 1537 /* For dynpmc fallback to MMD */ 1538 if ((type >= enum_class_core_max) || (SELF.type() >= enum_class_core_max)) 1539 type = enum_class_core_max; 1540 switch(type) { 1541$cases 1542 } 1543BODY 1544 1545 my $vtable = $method->clone({ 1546 body => Parrot::Pmc2c::Emitter->text($body), 1547 }); 1548 $self->add_method($vtable); 1549 } 1550 1551 1; 1552} 1553 1554# Generate single case for switch VTABLE 1555sub generate_single_case { 1556 my ($self, $vt_method_name, $multi, @parameters) = @_; 1557 1558 my ($type, $fsig, $ns, $func, $impl) = @$multi; 1559 my $case; 1560 1561 # Gather parameters names 1562 my $parameters = join ', ', @parameters; 1563 # ISO C forbids return with expression from void functions. 1564 my $return = $impl->return_type =~ /^void\s*$/ 1565 ? '' 1566 : 'return '; 1567 1568 if ($type eq 'DEFAULT' || $type eq 'PMC') { 1569 # For default case we have to handle return manually. 1570 my ($pcc_signature, $retval, $call_tail, $pcc_return) 1571 = gen_default_case_wrapping($impl); 1572 my $dispatch = "Parrot_mmd_multi_dispatch_from_c_args(INTERP, \"$vt_method_name\", \"$pcc_signature\", SELF, $parameters$call_tail);"; 1573 1574 $case = <<"CASE"; 1575 case enum_class_core_max: 1576CASE 1577 if ($retval eq '') { 1578 $case .= <<"CASE"; 1579 $dispatch 1580CASE 1581 } 1582 else { 1583 $case .= <<"CASE"; 1584 { 1585 $retval 1586 $dispatch 1587 $pcc_return 1588 } 1589CASE 1590 } 1591 $case .= <<"CASE"; 1592 break; 1593 default: 1594 $return$func(INTERP, SELF, $parameters); 1595 break; 1596CASE 1597 } 1598 else { 1599 $case = <<"CASE"; 1600 case enum_class_$type: 1601 $return$func(INTERP, SELF, $parameters); 1602 break; 1603CASE 1604 } 1605 1606 $case; 1607} 1608 1609# Generate (pcc_signature, retval holder, pcc_call_tail, return statement) 1610# for default case in switch. 1611sub gen_default_case_wrapping { 1612 my $method = shift; 1613 1614 local $_ = $method->return_type; 1615 if (/INTVAL/) { 1616 return ( 1617 "PP->I", 1618 "INTVAL retval;", 1619 ', &retval', 1620 'return retval;', 1621 ); 1622 } 1623 elsif (/STRING/) { 1624 return ( 1625 "PP->S", 1626 "STRING *retval;", 1627 ', &retval', 1628 'return retval;', 1629 ); 1630 } 1631 elsif (/PMC/) { 1632 return ( 1633 'PPP->P', 1634 'PMC *retval = PMCNULL;', 1635 ", &retval", 1636 "return retval;", 1637 ); 1638 } 1639 elsif (/void\s*$/) { 1640 return ( 1641 'PP->', 1642 '', 1643 '', 1644 'return;', 1645 ); 1646 } 1647 else { 1648 die "Can't handle return type `$_'!"; 1649 } 1650} 1651 1652=head2 C<generate_start> 1653 1654Generate and emit the C code for the start of an attribute struct. 1655 1656=cut 1657 1658sub generate_start { 1659 my ( $pmc ) = @_; 1660 1661 $pmc->{emitter}->emit(<<"EOH"); 1662 1663/* $pmc->{name} PMC's underlying struct. */ 1664typedef struct Parrot_$pmc->{name}_attributes { 1665EOH 1666 1667 return 1; 1668} 1669 1670=head2 C<generate_end> 1671 1672Generate and emit the C code for the end of an attribute struct. 1673 1674=cut 1675 1676sub generate_end { 1677 my ( $pmc ) = @_; 1678 my $name = $pmc->{name}; 1679 my $ucname = uc($name); 1680 1681 $pmc->{emitter}->emit(<<"EOH"); 1682} Parrot_${name}_attributes; 1683 1684/* Macro to access underlying structure of a $name PMC. */ 1685#define PARROT_${ucname}(o) ((Parrot_${name}_attributes *) PMC_data(o)) 1686 1687EOH 1688 1689 return 1; 1690} 1691 1692=head2 C<generate_declaration> 1693 1694Generate and emit the C code for an attribute declaration. 1695 1696=cut 1697 1698sub generate_declaration { 1699 my ( $pmc, $attribute ) = @_; 1700 my $decl = ' ' . $attribute->{type} . ' ' . $attribute->{name} . $attribute->{array_size} . ";\n"; 1701 1702 $pmc->{emitter}->emit($decl); 1703 1704 return 1; 1705} 1706 1707=head2 C<generate_accessor> 1708 1709Generate and emit the C code for an attribute get/set accessor pair. 1710 1711=cut 1712 1713sub generate_accessor { 1714 my ( $pmc, $attribute ) = @_; 1715 1716 my $pmcname = $pmc->{name}; 1717 my $attrtype = $attribute->{type}; 1718 my $attrname = $attribute->{name}; 1719 my $isfuncptr = 0; 1720 my $origtype = $attrtype; 1721 if($attrname =~ m/\(\*(\w*)\)\((.*?)\)/) { 1722 $isfuncptr = 1; 1723 $origtype = $attrtype . " (*)(" . $2 . ")"; 1724 $attrname = $1; 1725 } 1726 1727 # Store regexes used to check some types to avoid repetitions 1728 my $isptrtostring = qr/STRING\s*\*$/; 1729 my $isptrtopmc = qr/PMC\s*\*$/; 1730 1731 my $inherit = 1; 1732 my $decl = <<"EOA"; 1733 1734/* Generated macro accessors for '$attrname' attribute of $pmcname PMC. */ 1735#define GETATTR_${pmcname}_${attrname}(interp, pmc, dest) \\ 1736EOA 1737 1738 # Nobody derives from CallContext, the arg is always proper, and we need the speed 1739 if ($pmcname eq "CallContext") { 1740 $decl .= <<"EOA"; 1741 (dest) = PARROT_CALLCONTEXT(pmc)->${attrname} 1742 1743#define SETATTR_${pmcname}_${attrname}(interp, pmc, value) \\ 1744 PARROT_CALLCONTEXT(pmc)->${attrname} = (value) 1745EOA 1746 } 1747 1748 else { 1749 $decl .= <<"EOA"; 1750 do { \\ 1751 if (!PObj_is_object_TEST(pmc)) { \\ 1752 (dest) = ((Parrot_${pmcname}_attributes *)PMC_data(pmc))->$attrname; \\ 1753 } \\ 1754 else { \\ 1755EOA 1756 1757 if ($isfuncptr == 1) { 1758 $decl .= <<"EOA"; 1759 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION, \\ 1760 "Attributes of type '$origtype' cannot be " \\ 1761 "subclassed from a high-level PMC."); \\ 1762EOA 1763 } 1764 elsif ($attrtype eq "INTVAL") { 1765 $decl .= <<"EOA"; 1766 PMC * const attr_value = VTABLE_get_attr_str(interp, \\ 1767 pmc, Parrot_str_new_constant(interp, "$attrname")); \\ 1768 (dest) = (PMC_IS_NULL(attr_value) ? (INTVAL) 0: VTABLE_get_integer(interp, attr_value)); \\ 1769EOA 1770 } 1771 elsif ($attrtype eq "FLOATVAL") { 1772 $decl .= <<"EOA"; 1773 PMC * const attr_value = VTABLE_get_attr_str(interp, \\ 1774 pmc, Parrot_str_new_constant(interp, "$attrname")); \\ 1775 (dest) = (PMC_IS_NULL(attr_value) ? (FLOATVAL) 0.0: VTABLE_get_number(interp, attr_value)); \\ 1776EOA 1777 } 1778 elsif ($attrtype =~ $isptrtostring) { 1779 $decl .= <<"EOA"; 1780 PMC * const attr_value = VTABLE_get_attr_str(interp, \\ 1781 pmc, Parrot_str_new_constant(interp, "$attrname")); \\ 1782 (dest) = (PMC_IS_NULL(attr_value) ? (STRING *)NULL : VTABLE_get_string(interp, attr_value)); \\ 1783EOA 1784 } 1785 elsif ($attrtype =~ $isptrtopmc) { 1786 $decl .= <<"EOA"; 1787 (dest) = VTABLE_get_attr_str(interp, \\ 1788 pmc, Parrot_str_new_constant(interp, "$attrname")); \\ 1789EOA 1790 } 1791 1792 else { 1793 $inherit = 0; 1794 $decl .= <<"EOA"; 1795 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION, \\ 1796 "Attributes of type '$attrtype' cannot be " \\ 1797 "subclassed from a high-level PMC."); \\ 1798EOA 1799 } 1800 1801 $decl .= <<"EOA"; 1802 } \\ 1803 } while (0) 1804EOA 1805 1806 $decl .= <<"EOA"; 1807#define SETATTR_${pmcname}_${attrname}(interp, pmc, value) \\ 1808 do { \\ 1809 if (PObj_is_object_TEST(pmc)) { \\ 1810EOA 1811 1812 if ($isfuncptr == 1) { 1813 $decl .= <<"EOA"; 1814 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION, \\ 1815 "Attributes of type '$origtype' cannot be " \\ 1816 "subclassed from a high-level PMC."); \\ 1817EOA 1818 } 1819 elsif ($attrtype eq "INTVAL") { 1820 $decl .= <<"EOA"; 1821 PMC * const attr_value = Parrot_pmc_new_init_int(interp, enum_class_Integer, value); \\ 1822 VTABLE_set_attr_str(interp, pmc, \\ 1823 Parrot_str_new_constant(interp, "$attrname"), attr_value); \\ 1824EOA 1825 } 1826 elsif ($attrtype eq "FLOATVAL") { 1827 $decl .= <<"EOA"; 1828 PMC * const attr_value = Parrot_pmc_new(interp, enum_class_Float); \\ 1829 VTABLE_set_number_native(interp, attr_value, value); \\ 1830 VTABLE_set_attr_str(interp, pmc, \\ 1831 Parrot_str_new_constant(interp, "$attrname"), attr_value); \\ 1832EOA 1833 } 1834 elsif ($attrtype =~ $isptrtostring) { 1835 $decl .= <<"EOA"; 1836 PMC * const attr_value = Parrot_pmc_new(interp, enum_class_String); \\ 1837 VTABLE_set_string_native(interp, attr_value, value); \\ 1838 VTABLE_set_attr_str(interp, pmc, \\ 1839 Parrot_str_new_constant(interp, "$attrname"), attr_value); \\ 1840EOA 1841 } 1842 elsif ($attrtype =~ $isptrtopmc) { 1843 $decl .= <<"EOA"; 1844 VTABLE_set_attr_str(interp, pmc, \\ 1845 Parrot_str_new_constant(interp, "$attrname"), value); \\ 1846EOA 1847 } 1848 1849 else { 1850 $decl .= <<"EOA"; 1851 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION, \\ 1852 "Attributes of type '$attrtype' cannot be " \\ 1853 "subclassed from a high-level PMC."); \\ 1854EOA 1855 } 1856 1857 $decl .= <<"EOA"; 1858 } \\ 1859 else \\ 1860 ((Parrot_${pmcname}_attributes *)PMC_data(pmc))->$attrname = (value); \\ 1861 } while (0) 1862 1863EOA 1864 1865 } 1866 #my $assertion = ($attrtype =~ $isptrtopmc and not $isfuncptr) 1867 # ? 'PARROT_ASSERT_INTERP((PMC *)(value), interp);' 1868 # : ''; 1869 #$decl .= <<"EOA"; 1870 # } \\ 1871 # else {\\ 1872 # $assertion \\ 1873 # ((Parrot_${pmcname}_attributes *)PMC_data(pmc))->$attrname = (value); \\ 1874 # } \\ 1875 #} while (0) 1876#EOA 1877 1878 $attribute->{inherit} = $inherit; 1879 1880 $pmc->{emitter}->emit($decl); 1881 1882 return 1; 1883} 1884 1885 18861; 1887 1888# Local Variables: 1889# mode: cperl 1890# cperl-indent-level: 4 1891# fill-column: 100 1892# End: 1893# vim: expandtab shiftwidth=4: 1894