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