2 # Copyright (c) 2003 Enache Adrian. All rights reserved.
3 # This module is free software; you can redistribute and/or modify
4 # it under the same terms as Perl itself.
6 # Based on the original Bytecode.pm module written by Malcolm Beattie.
10 our $VERSION = '1.02';
14 use B qw(class main_cv main_root main_start cstring comppadlist
15 defstash curstash begin_av init_av end_av inc_gv warnhook diehook
16 dowarn SVt_PVGV SVt_PVHV OPf_SPECIAL OPf_STACKED OPf_MOD
17 OPpLVAL_INTRO SVf_FAKE SVf_READONLY);
18 use B::Asmdata qw(@specialsv_name);
19 use B::Assembler qw(asm newasm endasm);
21 #################################################
23 my ($varix, $opix, $savebegins, %walked, %files, @cloop);
33 my $ithreads = $Config{'useithreads'} eq 'define';
35 sub ITHREADS() { $ithreads }
40 #################################################
44 defined($pv) ? cstring ($pv."\0") : "\"\"";
48 my $str = pvstring shift;
49 my $ix = $strtab{$str};
50 defined($ix) ? $ix : do {
52 asm "stpv", $strtab{$str} = $tix;
59 my $ix = $optab{$$op};
60 defined($ix) ? $ix : do {
61 nice "[".$op->name." $tix]";
62 asm "newopx", $op->size | $op->type <<7;
63 $optab{$$op} = $opix = $ix = $tix++;
71 my $ix = $spectab{$$spec};
72 defined($ix) ? $ix : do {
73 nice '['.$specialsv_name[$$spec].']';
74 asm "ldspecsvx", $$spec;
75 $spectab{$$spec} = $varix = $tix++;
81 my $ix = $svtab{$$sv};
82 defined($ix) ? $ix : do {
83 nice '['.class($sv).']';
84 asm "newsvx", $sv->FLAGS;
85 $svtab{$$sv} = $varix = $ix = $tix++;
92 my ($gv,$desired) = @_;
93 my $ix = $svtab{$$gv};
94 defined($ix) ? $ix : do {
96 my ($svix, $avix, $hvix, $cvix, $ioix, $formix);
98 my $name = $gv->STASH->NAME . "::" . $gv->NAME;
99 asm "gv_fetchpvx", cstring $name;
100 $svtab{$$gv} = $varix = $ix = $tix++;
101 asm "sv_flags", $gv->FLAGS;
102 asm "sv_refcnt", $gv->REFCNT;
103 asm "xgv_flags", $gv->GvFLAGS;
105 asm "gp_refcnt", $gv->GvREFCNT;
106 asm "load_glob", $ix if $name eq "CORE::GLOBAL::glob";
108 unless $desired || desired $gv;
115 $cvix = $$cv && defined $files{$cv->FILE} ? $cv->ix : 0;
116 my $form = $gv->FORM;
117 $formix = $$form && defined $files{$form->FILE} ? $form->ix : 0;
119 $ioix = $name !~ /STDOUT$/ ? $gv->IO->ix : 0;
123 asm "ldsv", $varix = $ix unless $ix == $varix;
129 asm "gp_cvgen", $gv->CVGEN;
130 asm "gp_form", $formix;
131 asm "gp_file", pvix $gv->FILE;
132 asm "gp_line", $gv->LINE;
133 asm "formfeed", $svix if $name eq "main::\cL";
136 asm "newsvx", $gv->FLAGS;
137 $svtab{$$gv} = $varix = $ix = $tix++;
138 my $stashix = $gv->STASH->ix;
139 $gv->B::PVMG::bsave($ix);
140 asm "xgv_flags", $gv->GvFLAGS;
141 asm "xgv_stash", $stashix;
149 my $ix = $svtab{$$hv};
150 defined($ix) ? $ix : do {
152 my $name = $hv->NAME;
155 asm "gv_stashpvx", cstring $name;
156 asm "sv_flags", $hv->FLAGS;
157 $svtab{$$hv} = $varix = $ix = $tix++;
158 asm "xhv_name", pvix $name;
159 # my $pmrootix = $hv->PMROOT->ix; # XXX
160 asm "ldsv", $varix = $ix unless $ix == $varix;
161 # asm "xhv_pmroot", $pmrootix; # XXX
164 asm "newsvx", $hv->FLAGS;
165 $svtab{$$hv} = $varix = $ix = $tix++;
166 my $stashix = $hv->SvSTASH->ix;
167 for (@array = $hv->ARRAY) {
172 asm "ldsv", $varix = $ix unless $ix == $varix;
173 ($i = not $i) ? asm ("newpv", pvstring $_) : asm("hv_store", $_)
175 if (VERSION < 5.009) {
178 asm "xmg_stash", $stashix;
179 asm "xhv_riter", $hv->RITER;
181 asm "sv_refcnt", $hv->REFCNT;
188 $$sv ? $sv->B::SV::ix : 0;
191 sub B::NULL::opwalk { 0 }
193 #################################################
198 nice '-'.class($sv).'-',
199 asm "ldsv", $varix = $ix unless $ix == $varix;
200 asm "sv_refcnt", $sv->REFCNT;
204 *B::SV::bsave = *B::NULL::bsave;
208 my $rvix = $sv->RV->ix;
209 $sv->B::NULL::bsave($ix);
215 $sv->B::NULL::bsave($ix);
216 asm "newpv", pvstring $sv->PVBM;
222 $sv->B::NULL::bsave($ix);
228 $sv->B::NULL::bsave($ix);
229 asm "xnv", sprintf "%.40g", $sv->NVX;
235 $sv->B::PV::bsave($ix):
237 $sv->B::RV::bsave($ix):
238 $sv->B::NULL::bsave($ix);
239 if (VERSION >= 5.009) {
240 # See note below in B::PVNV::bsave
241 return if $sv->isa('B::AV');
242 return if $sv->isa('B::HV');
243 return if $sv->isa('B::CV');
245 asm "xiv", !ITHREADS && $sv->FLAGS & (SVf_FAKE|SVf_READONLY) ?
246 "0 but true" : $sv->IVX;
251 $sv->B::PVIV::bsave($ix);
252 if (VERSION >= 5.009) {
253 # Magical AVs end up here, but AVs now don't have an NV slot actually
254 # allocated. Hence don't write out assembly to store the NV slot if
255 # we're actually an array.
256 return if $sv->isa('B::AV');
257 # Likewise HVs have no NV slot actually allocated.
258 # I don't think that they can get here, but better safe than sorry
259 return if $sv->isa('B::HV');
261 asm "xnv", sprintf "%.40g", $sv->NVX;
264 sub B::PVMG::domagic {
267 my @mglist = $sv->MAGIC;
270 push @mgix, $_->OBJ->ix;
271 push @namix, $_->PTR->ix if $_->LENGTH == B::HEf_SVKEY;
274 nice '-'.class($sv).'-',
275 asm "ldsv", $varix = $ix unless $ix == $varix;
277 asm "sv_magic", cstring $_->TYPE;
278 asm "mg_obj", shift @mgix;
279 my $length = $_->LENGTH;
280 if ($length == B::HEf_SVKEY) {
281 asm "mg_namex", shift @namix;
283 asm "newpv", pvstring $_->PTR;
291 my $stashix = $sv->SvSTASH->ix;
292 $sv->B::PVNV::bsave($ix);
293 asm "xmg_stash", $stashix;
294 $sv->domagic($ix) if $sv->MAGICAL;
299 my $targix = $sv->TARG->ix;
300 $sv->B::PVMG::bsave($ix);
301 asm "xlv_targ", $targix;
302 asm "xlv_targoff", $sv->TARGOFF;
303 asm "xlv_targlen", $sv->TARGLEN;
304 asm "xlv_type", $sv->TYPE;
310 $sv->B::PVMG::bsave($ix);
311 asm "xpv_cur", $sv->CUR;
312 asm "xbm_useful", $sv->USEFUL;
313 asm "xbm_previous", $sv->PREVIOUS;
314 asm "xbm_rare", $sv->RARE;
319 my $topix = $io->TOP_GV->ix;
320 my $fmtix = $io->FMT_GV->ix;
321 my $bottomix = $io->BOTTOM_GV->ix;
322 $io->B::PVMG::bsave($ix);
323 asm "xio_lines", $io->LINES;
324 asm "xio_page", $io->PAGE;
325 asm "xio_page_len", $io->PAGE_LEN;
326 asm "xio_lines_left", $io->LINES_LEFT;
327 asm "xio_top_name", pvix $io->TOP_NAME;
328 asm "xio_top_gv", $topix;
329 asm "xio_fmt_name", pvix $io->FMT_NAME;
330 asm "xio_fmt_gv", $fmtix;
331 asm "xio_bottom_name", pvix $io->BOTTOM_NAME;
332 asm "xio_bottom_gv", $bottomix;
333 asm "xio_subprocess", $io->SUBPROCESS;
334 asm "xio_type", ord $io->IoTYPE;
335 # asm "xio_flags", ord($io->IoFLAGS) & ~32; # XXX XXX
340 my $stashix = $cv->STASH->ix;
341 my $gvix = $cv->GV->ix;
342 my $padlistix = $cv->PADLIST->ix;
343 my $outsideix = $cv->OUTSIDE->ix;
344 my $constix = $cv->CONST ? $cv->XSUBANY->ix : 0;
345 my $startix = $cv->START->opwalk;
346 my $rootix = $cv->ROOT->ix;
348 $cv->B::PVMG::bsave($ix);
349 asm "xcv_stash", $stashix;
350 asm "xcv_start", $startix;
351 asm "xcv_root", $rootix;
352 asm "xcv_xsubany", $constix;
354 asm "xcv_file", pvix $cv->FILE if $cv->FILE; # XXX AD
355 asm "xcv_padlist", $padlistix;
356 asm "xcv_outside", $outsideix;
357 asm "xcv_flags", $cv->CvFLAGS;
358 asm "xcv_outside_seq", $cv->OUTSIDE_SEQ;
359 asm "xcv_depth", $cv->DEPTH;
365 $form->B::CV::bsave($ix);
366 asm "xfm_lines", $form->LINES;
371 return $av->B::PVMG::bsave($ix) if $av->MAGICAL;
372 my @array = $av->ARRAY;
373 $_ = $_->ix for @array;
374 my $stashix = $av->SvSTASH->ix;
377 asm "ldsv", $varix = $ix unless $ix == $varix;
378 asm "av_extend", $av->MAX if $av->MAX >= 0;
379 asm "av_pushx", $_ for @array;
380 asm "sv_refcnt", $av->REFCNT;
381 if (VERSION < 5.009) {
382 asm "xav_flags", $av->AvFLAGS;
384 asm "xmg_stash", $stashix;
390 $files{$gv->FILE} && $gv->LINE
391 || ${$cv = $gv->CV} && $files{$cv->FILE}
392 || ${$form = $gv->FORM} && $files{$form->FILE}
397 return if $walked{$$hv}++;
398 my %stash = $hv->ARRAY;
399 while (my($k,$v) = each %stash) {
400 if ($v->SvTYPE == SVt_PVGV) {
402 if ($$hash && $hash->NAME) {
405 $v->ix(1) if desired $v;
408 asm "gv_fetchpvx", cstring $hv->NAME . "::$k";
409 $svtab{$$v} = $varix = $tix;
411 asm "sv_flags", $v->FLAGS;
416 ######################################################
419 sub B::OP::bsave_thin {
421 my $next = $op->next;
422 my $nextix = $optab{$$next};
423 $nextix = 0, push @cloop, $op unless defined $nextix;
425 nice '-'.$op->name.'-',
426 asm "ldop", $opix = $ix;
428 asm "op_next", $nextix;
429 asm "op_targ", $op->targ if $op->type; # tricky
430 asm "op_flags", $op->flags;
431 asm "op_private", $op->private;
435 *B::OP::bsave = *B::OP::bsave_thin;
439 my $name = $op->name;
440 my $flags = $op->flags;
441 my $first = $op->first;
445 || (!ITHREADS && $name eq 'regcomp')
446 # trick for /$a/o in pp_regcomp
448 && $op->flags & OPf_MOD
449 && $op->private & OPpLVAL_INTRO
450 # change #18774 made my life hard
454 $op->B::OP::bsave($ix);
455 asm "op_first", $firstix;
458 sub B::BINOP::bsave {
460 if ($op->name eq 'aassign' && $op->private & B::OPpASSIGN_HASH()) {
461 my $last = $op->last;
463 local *B::OP::bsave = *B::OP::bsave_fat;
464 local *B::UNOP::bsave = *B::UNOP::bsave_fat;
467 asm "ldop", $lastix unless $lastix == $opix;
468 asm "op_targ", $last->targ;
469 $op->B::OP::bsave($ix);
470 asm "op_last", $lastix;
472 $op->B::OP::bsave($ix);
476 # not needed if no pseudohashes
478 *B::BINOP::bsave = *B::OP::bsave if VERSION >= 5.009;
480 # deal with sort / formline
482 sub B::LISTOP::bsave {
484 my $name = $op->name;
485 sub blocksort() { OPf_SPECIAL|OPf_STACKED }
486 if ($name eq 'sort' && ($op->flags & blocksort) == blocksort) {
487 my $first = $op->first;
488 my $pushmark = $first->sibling;
489 my $rvgv = $pushmark->first;
490 my $leave = $rvgv->first;
492 my $leaveix = $leave->ix;
494 my $rvgvix = $rvgv->ix;
495 asm "ldop", $rvgvix unless $rvgvix == $opix;
496 asm "op_first", $leaveix;
498 my $pushmarkix = $pushmark->ix;
499 asm "ldop", $pushmarkix unless $pushmarkix == $opix;
500 asm "op_first", $rvgvix;
502 my $firstix = $first->ix;
503 asm "ldop", $firstix unless $firstix == $opix;
504 asm "op_sibling", $pushmarkix;
506 $op->B::OP::bsave($ix);
507 asm "op_first", $firstix;
508 } elsif ($name eq 'formline') {
509 $op->B::UNOP::bsave_fat($ix);
511 $op->B::OP::bsave($ix);
517 sub B::OP::bsave_fat {
519 my $siblix = $op->sibling->ix;
521 $op->B::OP::bsave_thin($ix);
522 asm "op_sibling", $siblix;
523 # asm "op_seq", -1; XXX don't allocate OPs piece by piece
526 sub B::UNOP::bsave_fat {
528 my $firstix = $op->first->ix;
530 $op->B::OP::bsave($ix);
531 asm "op_first", $firstix;
534 sub B::BINOP::bsave_fat {
536 my $last = $op->last;
537 my $lastix = $op->last->ix;
538 if (VERSION < 5.009 && $op->name eq 'aassign' && $last->name eq 'null') {
539 asm "ldop", $lastix unless $lastix == $opix;
540 asm "op_targ", $last->targ;
543 $op->B::UNOP::bsave($ix);
544 asm "op_last", $lastix;
547 sub B::LOGOP::bsave {
549 my $otherix = $op->other->ix;
551 $op->B::UNOP::bsave($ix);
552 asm "op_other", $otherix;
557 my ($rrop, $rrarg, $rstart);
559 # my $pmnextix = $op->pmnext->ix; # XXX
562 if ($op->name eq 'subst') {
563 $rrop = "op_pmreplroot";
564 $rrarg = $op->pmreplroot->ix;
565 $rstart = $op->pmreplstart->ix;
566 } elsif ($op->name eq 'pushre') {
567 $rrop = "op_pmreplrootpo";
568 $rrarg = $op->pmreplroot;
570 $op->B::BINOP::bsave($ix);
571 asm "op_pmstashpv", pvix $op->pmstashpv;
573 $rrop = "op_pmreplrootgv";
574 $rrarg = $op->pmreplroot->ix;
575 $rstart = $op->pmreplstart->ix if $op->name eq 'subst';
576 my $stashix = $op->pmstash->ix;
577 $op->B::BINOP::bsave($ix);
578 asm "op_pmstash", $stashix;
581 asm $rrop, $rrarg if $rrop;
582 asm "op_pmreplstart", $rstart if $rstart;
584 asm "op_pmflags", $op->pmflags;
585 asm "op_pmpermflags", $op->pmpermflags;
586 asm "op_pmdynflags", $op->pmdynflags;
587 # asm "op_pmnext", $pmnextix; # XXX
588 asm "newpv", pvstring $op->precomp;
594 my $svix = $op->sv->ix;
596 $op->B::OP::bsave($ix);
600 sub B::PADOP::bsave {
603 $op->B::OP::bsave($ix);
604 asm "op_padix", $op->padix;
609 $op->B::OP::bsave($ix);
610 return unless my $pv = $op->pv;
612 if ($op->name eq 'trans') {
613 asm "op_pv_tr", join ',', length($pv)/2, unpack("s*", $pv);
615 asm "newpv", pvstring $pv;
622 my $nextix = $op->nextop->ix;
623 my $lastix = $op->lastop->ix;
624 my $redoix = $op->redoop->ix;
626 $op->B::BINOP::bsave($ix);
627 asm "op_redoop", $redoix;
628 asm "op_nextop", $nextix;
629 asm "op_lastop", $lastix;
634 my $warnix = $cop->warnings->ix;
635 my $ioix = $cop->io->ix;
637 $cop->B::OP::bsave($ix);
638 asm "cop_stashpv", pvix $cop->stashpv;
639 asm "cop_file", pvix $cop->file;
641 my $stashix = $cop->stash->ix;
642 my $fileix = $cop->filegv->ix(1);
643 $cop->B::OP::bsave($ix);
644 asm "cop_stash", $stashix;
645 asm "cop_filegv", $fileix;
647 asm "cop_label", pvix $cop->label if $cop->label; # XXX AD
648 asm "cop_seq", $cop->cop_seq;
649 asm "cop_arybase", $cop->arybase;
650 asm "cop_line", $cop->line;
651 asm "cop_warnings", $warnix;
657 my $ix = $optab{$$op};
658 defined($ix) ? $ix : do {
660 my @oplist = $op->oplist;
662 $ix = $_->ix while $_ = pop @oplist;
663 while ($_ = pop @cloop) {
664 asm "ldop", $optab{$$_};
665 asm "op_next", $optab{${$_->next}};
671 #################################################
675 if (($av=begin_av)->isa("B::AV")) {
678 next unless $_->FILE eq $0;
679 asm "push_begin", $_->ix;
683 next unless $_->FILE eq $0;
684 # XXX BEGIN { goto A while 1; A: }
685 for (my $op = $_->START; $$op; $op = $op->next) {
686 next unless $op->name eq 'require' ||
687 # this kludge needed for tests
688 $op->name eq 'gv' && do {
689 my $gv = class($op) eq 'SVOP' ?
691 (($_->PADLIST->ARRAY)[1]->ARRAY)[$op->padix];
692 $$gv && $gv->NAME =~ /use_ok|plan/
694 asm "push_begin", $_->ix;
700 if (($av=init_av)->isa("B::AV")) {
702 next unless $_->FILE eq $0;
703 asm "push_init", $_->ix;
706 if (($av=end_av)->isa("B::AV")) {
708 next unless $_->FILE eq $0;
709 asm "push_end", $_->ix;
715 my ($head, $scan, $T_inhinc, $keep_syn);
720 *B::OP::bsave = *B::OP::bsave_fat;
721 *B::UNOP::bsave = *B::UNOP::bsave_fat;
722 *B::BINOP::bsave = *B::BINOP::bsave_fat;
723 *B::LISTOP::bsave = *B::LISTOP::bsave_fat;
725 sub bwarn { print STDERR "Bytecode.pm: @_\n" }
729 *newasm = *endasm = sub { };
730 *asm = sub { print " @_\n" };
731 *nice = sub ($) { print "\n@_\n" };
734 $head = "#! $^X\nuse ByteLoader $ByteLoader::VERSION;\n";
737 } elsif (/^-o(.*)$/) {
738 open STDOUT, ">$1" or die "open $1: $!";
739 } elsif (/^-f(.*)$/) {
741 } elsif (/^-s(.*)$/) {
742 $scan = length($1) ? $1 : $0;
745 # this is here for the testsuite
748 } elsif (/^-TF(.*)/) {
750 *B::COP::file = sub { $thatfile };
752 bwarn "Ignoring '$_' option";
757 if (open $f, $scan) {
759 /^#\s*line\s+\d+\s+("?)(.*)\1/ and $files{$2} = 1;
761 if (/\bgoto\b\s*[^&]/ && !$keep_syn) {
762 bwarn "keeping the syntax tree: \"goto\" op found";
767 bwarn "cannot rescan '$scan'";
773 print $head if $head;
774 newasm sub { print @_ };
777 asm "main_start", main_start->opwalk;
778 asm "main_root", main_root->ix;
779 asm "main_cv", main_cv->ix;
780 asm "curpad", (comppadlist->ARRAY)[1]->ix;
782 asm "signal", cstring "__WARN__" # XXX
784 asm "incav", inc_gv->AV->ix if $T_inhinc;
786 asm "incav", inc_gv->AV->ix if $T_inhinc;
787 asm "dowarn", dowarn;
792 my $dh = *{defstash->NAME."::DATA"};
810 B::Bytecode - Perl compiler's bytecode backend
814 B<perl -MO=Bytecode>[B<,-H>][B<,-o>I<script.plc>] I<script.pl>
818 Compiles a Perl script into a bytecode format that could be loaded
819 later by the ByteLoader module and executed as a regular Perl script.
823 $ perl -MO=Bytecode,-H,-ohi -e 'print "hi!\n"'
833 Save all the BEGIN blocks. Normally only BEGIN blocks that C<require>
834 other files (ex. C<use Foo;>) are saved.
838 prepend a C<use ByteLoader VERSION;> line to the produced bytecode.
842 keep the syntax tree - it is stripped by default.
844 =item B<-o>I<outfile>
846 put the bytecode in <outfile> instead of dumping it to STDOUT.
850 scan the script for C<# line ..> directives and for <goto LABEL>
851 expressions. When gotos are found keep the syntax tree.
861 C<BEGIN { goto A: while 1; A: }> won't even compile.
865 C<?...?> and C<reset> do not work as expected.
869 variables in C<(?{ ... })> constructs are not properly scoped.
873 scripts that use source filters will fail miserably.
879 There are also undocumented bugs and options.
881 THIS CODE IS HIGHLY EXPERIMENTAL. USE AT YOUR OWN RISK.
885 Originally written by Malcolm Beattie <mbeattie@sable.ox.ac.uk> and
886 modified by Benjamin Stuhl <sho_pi@hotmail.com>.
888 Rewritten by Enache Adrian <enache@rdslink.ro>, 2003 a.d.