Acme-Perl-VM
view release on metacpan or search on metacpan
lib/Acme/Perl/VM/PP.pm view on Meta::CPAN
package Acme::Perl::VM::PP;
use strict;
use warnings;
use Acme::Perl::VM qw(:perl_h);
use Acme::Perl::VM::B;
#NOTE:
# perl APVM
#
# dSP (nothing)
# SP $#PL_stack
# *SP $PL_stack[-1]
# dMARK my $mark = POPMARK
# MARK $mark
# *MARK $PL_stack[$mark]
# dORIGMARK my $origmark = $mark
# ORIGMARK $origmark
# SPAGAIN (nothing)
# PUTBACK (nothing)
sub pp_nextstate{
$PL_curcop = $PL_op;
$#PL_stack = $PL_cxstack[-1]->oldsp;
FREETMPS;
return $PL_op->next;
}
sub pp_pushmark{
PUSHMARK;
return $PL_op->next;
}
sub pp_const{
my $sv = is_not_null($PL_op->sv) ? $PL_op->sv : PAD_SV($PL_op->targ);
PUSH($sv);
return $PL_op->next;
}
sub pp_gv{
PUSH( GVOP_gv($PL_op) );
return $PL_op->next;
}
sub pp_gvsv{
if($PL_op->private & OPpLVAL_INTRO){
PUSH(save_scalar(GVOP_gv($PL_op)));
}
else{
PUSH(GVOP_gv($PL_op)->SV);
}
return $PL_op->next;
}
sub _do_kv{
my $hv = POP;
if($hv->class ne 'HV'){
apvm_die 'panic: do_kv';
}
my $gimme = GIMME_V;
if($gimme == G_VOID){
return $PL_op->next;
}
elsif($gimme == G_SCALAR){
if($PL_op->flags & OPf_MOD || LVRET){
not_implemented $PL_op->name . ' for lvalue';
}
my $num = keys %{ $hv->object_2svref };
mPUSH( svref_2object(\$num) );
return $PL_op->next;
}
my($dokeys, $dovalues);
if($PL_op->name eq 'keys'){
$dokeys = TRUE;
}
elsif($PL_op->name eq 'values'){
$dovalues = TRUE;
}
else{
$dokeys = $dovalues = TRUE;
}
my $hash_ref = $hv->object_2svref;
while(my $k = each %{$hash_ref}){
mPUSH( svref_2object(\$k) ) if $dokeys;
PUSH( svref_2object(\$hash_ref->{$k}) ) if $dovalues;
}
return $PL_op->next;
}
sub pp_rv2gv{
my $sv = TOP;
if($sv->ROK){
$sv = $sv->RV;
}
if($sv->class ne 'GV'){
apvm_die 'Not a GLOB reference';
}
if($PL_op->private & OPpLVAL_INTRO){
not_implemented 'rv2gv for OPpLVAL_INTRO';
}
SET($sv);
return $PL_op->next;
}
sub pp_rv2sv{
my $sv = TOP;
my $gv;
if($sv->ROK){
if(!is_scalar($sv)){
apvm_die 'Not a SCALAR reference';
}
}
else{
if($sv->class ne 'GV'){
not_implemented 'rv2xv for soft references';
}
$gv = $sv;
}
if($PL_op->flags & OPf_MOD){
if($PL_op->private & OPpLVAL_INTRO){
if($PL_op->first->name eq 'null'){
$sv = save_scalar(TOP);
}
else{
$sv = save_scalar($gv);
}
}
elsif($PL_op->private & OPpDEREF){
vivify_ref($sv, $PL_op->private & OPpDEREF);
}
}
SET($sv);
return $PL_op->next;
}
sub pp_rv2av{
my $sv = TOP;
my $name;
my $class;
my $save;
if($PL_op->name eq 'rv2av'){
$name = 'an ARRAY';
$class = 'AV';
$save = \&save_ary;
}
else{
$name = 'a HASH';
$class = 'HV';
$save = \&save_hash;
}
my $gimme = GIMME_V;
if($sv->ROK){
$sv = $sv->RV;
if($sv->class ne $class){
apvm_die "Not $name reference";
}
if($PL_op->flags & OPf_REF){
SET($sv);
return $PL_op->next;
}
elsif(LVRET){
not_implemented 'rv2av for lvalue';
}
elsif($PL_op->flags & OPf_MOD
&& $PL_op->private & OPpLVAL_INTRO){
apvm_die q{Can't localize through a reference};
}
}
else{
if($sv->class eq $class){
if($PL_op->flags & OPf_REF){
SET($sv);
return $PL_op->next;
}
elsif(LVRET){
not_implemented 'rv2av for lvalue';
}
}
else{
if($sv->class ne 'GV'){
not_implemented 'rv2av for symbolic reference';
}
if($PL_op->private & OPpLVAL_INTRO){
$sv = $save->($sv);
}
else{
$sv = $sv->$class();
}
if($PL_op->flags & OPf_REF){
SET($sv);
return $PL_op->next;
}
elsif(LVRET){
not_implemented 'rv2av for lvalue';
}
}
}
if($class eq 'AV'){ # rv2av
if($gimme == G_ARRAY){
POP;
PUSH( $sv->ARRAY );
}
elsif($gimme == G_SCALAR){
SETval( $sv->FILL + 1 );
}
}
else{ # rv2hv
if($gimme == G_ARRAY){
return &_do_kv;
}
elsif($gimme == G_SCALAR){
SET(hv_scalar($sv));
}
}
return $PL_op->next;
}
sub pp_rv2hv{
goto &pp_rv2av;
}
sub pp_padsv{
my $targ = GET_TARGET;
PUSH($targ);
if($PL_op->flags & OPf_MOD){
if(($PL_op->private & (OPpLVAL_INTRO|OPpPAD_STATE)) == OPpLVAL_INTRO){
SAVECLEARSV($targ);
}
}
return $PL_op->next;
}
sub pp_padav{
my $targ = GET_TARGET;
if(($PL_op->private & (OPpLVAL_INTRO|OPpPAD_STATE)) == OPpLVAL_INTRO){
SAVECLEARSV($targ);
}
if($PL_op->flags & OPf_REF){
PUSH($targ);
return $PL_op->next;;
}
elsif(LVRET){
not_implemented 'padav for lvalue';
}
my $gimme = GIMME_V;
if($gimme == G_ARRAY){
PUSH( $targ->ARRAY );
}
elsif($gimme == G_SCALAR){
my $sv = sv_newmortal();
$sv->setval($targ->FILL + 1);
PUSH($sv);
}
return $PL_op->next;
}
sub pp_padhv{
my $targ = GET_TARGET;
if(($PL_op->private & (OPpLVAL_INTRO|OPpPAD_STATE)) == OPpLVAL_INTRO){
SAVECLEARSV($targ);
}
PUSH($targ);
if($PL_op->flags & OPf_REF){
return $PL_op->next;
}
elsif(LVRET){
not_implemented 'padhv for lvalue';
}
my $gimme = GIMME_V;
if($gimme == G_ARRAY){
return &_do_kv;
}
elsif($gimme == G_SCALAR){
SET( hv_scalar($targ) );
}
return $PL_op->next;;
}
sub pp_anonlist{
my $mark = POPMARK;
my @ary = mark_list($mark);
if($PL_op->flags & OPf_SPECIAL){
my $ref = \@ary;
mPUSH(svref_2object(\$ref));
}
else{
mPUSH(svref_2object(\@ary));
}
return $PL_op->next;
}
sub pp_anonhash{
my $mark = POPMARK;
my $origmark = $mark;
my %hash;
while($mark < $#PL_stack){
my $key = $PL_stack[++$mark];
my $val;
if($mark < $#PL_stack){
$val = ${ $PL_stack[++$mark]->object_2svref };
}
else{
apvm_warn 'Odd number of elements';
}
$hash{ ${ $key->object_2svref } } = $val;
}
$#PL_stack = $origmark;
if($PL_op->flags & OPf_SPECIAL){
my $ref = \%hash;
mPUSH(svref_2object(\$ref));
}
else{
mPUSH(svref_2object(\%hash));
}
return $PL_op->next;
}
sub _refto{
my($sv) = @_;
if($sv->class eq 'PVLV'){
not_implemented 'ref to PVLV';
}
my $rv = $sv->object_2svref;
return sv_2mortal( svref_2object(\$rv) );
}
sub pp_srefgen{
$PL_stack[-1] = _refto($PL_stack[-1]);
return $PL_op->next;
}
sub pp_refgen{
my $mark = POPMARK;
if(GIMME_V == G_ARRAY){
while(++$mark <= $#PL_stack){
$PL_stack[$mark] = _refto($PL_stack[$mark]);
}
}
else{
if(++$mark <= $#PL_stack){
$PL_stack[$mark] = _refto($PL_stack[-1]);
}
else{
$PL_stack[$mark] = _refto(sv_undef);
}
$#PL_stack = $mark;
}
return $PL_op->next;
}
sub pp_list{
my $mark = POPMARK;
if(GIMME_V != G_ARRAY){
if(++$mark <= $#PL_stack){
$PL_stack[$mark] = $PL_stack[-1];
}
else{
$PL_stack[$mark] = sv_undef;
}
$#PL_stack = $mark;
}
return $PL_op->next;
}
sub _method_common{
my($meth) = @_;
my $name = SvPV($meth);
my $sv = $PL_stack[ TOPMARK() + 1];
if(!sv_defined($sv)){
apvm_die q{Can't call method "%s" on an undefined value}, $name;
}
my $invocant = ${$sv->object_2svref};
my $code = do{
local $@;
eval{ $invocant->can($name) };
};
if(!$code){
apvm_die q{Can't locate object method "%s" via package "%s"}, $name, ref($invocant) || $invocant;
}
return svref_2object($code);
}
sub pp_method{
my $sv = TOP;
if($sv->ROK){
if($sv->RV->class eq 'CV'){
SET($sv->RV);
return $PL_op->next;
}
}
SET(_method_common($sv));
return $PL_op->next;
}
sub pp_method_named{
my $sv = is_not_null($PL_op->sv) ? $PL_op->sv : PAD_SV($PL_op->targ);
PUSH(_method_common($sv));
return $PL_op->next;
}
sub pp_entersub{
my $sv = POP;
my $cv = $sv->toCV();
if(is_null($cv)){
apvm_die 'Undefined subroutine %s called', gv_fullname($sv, '&');
}
my $hasargs = ($PL_op->flags & OPf_STACKED) != 0;
ENTER;
SAVETMPS;
my $mark = POPMARK;
my $gimme = GIMME_V;
if(!cv_external($cv)){
my $cx = PUSHBLOCK(SUB =>
oldsp => $mark,
gimme => $gimme,
cv => $cv,
hasargs => $hasargs,
retop => $PL_op->next,
);
#XXX: How to do {$cv->DEPTH++}?
PAD_SET_CUR($cv->PADLIST, $cv->DEPTH+1);
if($hasargs){
my $av = PAD_SV(0);
$cx->savearray(\@_);
*_ = $av->object_2svref;
$cx->CURPAD_SAVE();
$cx->argarray($av);
#@_ = mark_list($mark);
av_assign($av, splice @PL_stack, $mark+1);
}
return $cv->START;
}
else{
my @args;
av_assign(svref_2object(\@args), splice @PL_stack, $mark+1);
if($gimme == G_SCALAR){
my $ret = $cv->object_2svref->(@args);
mPUSH(svref_2object(\$ret));
}
elsif($gimme == G_ARRAY){
my @ret = $cv->object_2svref->(@args);
mPUSH(map{ svref_2object(\$_) } @ret);
}
else{
$cv->object_2svref->(@args);
}
return $PL_op->next;
}
}
sub pp_leavesub{
my $cx = POPBLOCK;
my $newsp = $cx->oldsp;
my $gimme = $cx->gimme;
if($gimme == G_SCALAR){
my $mark = $newsp + 1;
if($mark <= $#PL_stack){
$PL_stack[$mark] = sv_mortalcopy(TOP);
}
else{
$PL_stack[$mark] = sv_undef;
}
$#PL_stack = $mark;
}
elsif($gimme == G_ARRAY){
for(my $mark = $newsp + 1; $mark <= $#PL_stack; $mark++){
$PL_stack[$mark] = sv_mortalcopy($PL_stack[$mark]);
}
}
else{
$#PL_stack = $newsp;
}
LEAVE;
POPSUB($cx);
# XXX: How to do {$cv->DEPTH = $cx->olddepth}?
return $cx->retop;
}
sub pp_return{
my $mark = POPMARK;
my $cxix = dopoptosub($#PL_cxstack);
if($cxix < 0){
apvm_die q{Can't return outside a subroutine};
}
if($cxix < $#PL_cxstack){
dounwind($cxix);
}
my $cx = POPBLOCK;
my $popsub2;
my $retop;
if($cx->type eq 'SUB'){
$popsub2 = TRUE;
$retop = $cx->retop;
}
else{
not_implemented 'return for ' . $cx->type
}
my $newsp = $cx->oldsp;
my $gimme = $cx->gimme;
if($gimme == G_SCALAR){
if($mark < $#PL_stack){
$PL_stack[++$newsp] = sv_mortalcopy(TOP);
}
else{
$PL_stack[++$newsp] = sv_undef;
}
}
elsif($gimme == G_ARRAY){
while(++$mark <= $#PL_stack){
$PL_stack[++$newsp] = sv_mortalcopy($PL_stack[$mark]);
}
}
$#PL_stack = $newsp;
LEAVE;
if($popsub2){
POPSUB($cx);
}
return $retop;
}
sub pp_enter{
my $gimme = OP_GIMME($PL_op, -1);
if($gimme == -1){
if(@PL_cxstack){
$gimme = $PL_cxstack[-1]->gimme;
}
else{
$gimme = G_SCALAR;
}
}
ENTER;
SAVETMPS;
PUSHBLOCK(BLOCK =>
oldsp => $#PL_stack,
gimme => $gimme
);
return $PL_op->next;
}
sub pp_leave{
my $cx = POPBLOCK;
my $newsp = $cx->oldsp;
my $gimme = OP_GIMME($PL_op, -1);
if($gimme == -1){
if(@PL_cxstack){
$gimme = $PL_cxstack[-1]->gimme;
}
else{
lib/Acme/Perl/VM/PP.pm view on Meta::CPAN
my $cx = PUSHBLOCK(FOREACH =>
oldsp => $#PL_stack,
gimme => GIMME_V,
resetsp => $mark,
iterdata => $iterdata,
padvar => $padvar,
for_def => $for_def,
);
if($PL_op->flags & OPf_STACKED){
my $iterary = POP;
if($iterary->class ne 'AV'){
my $sv = POP;
my $right = $iterary;
if(_range_is_numeric($sv, $right)){
$cx->iterix(SvIV($sv));
$cx->itermax(SvIV($right));
}
else{
$cx->iterlval(SvPV($sv));
$cx->iterary(SvPV($sv));
}
}
else{
$cx->iterary([$iterary->ARRAY]);
if($PL_op->private & OPpITER_REVERSED){
$cx->itermax(0);
$cx->iterix($iterary->FILL + 1);
}
else{
$cx->iterix(-1);
}
}
# XXX: original code does not have this adjustment.
# is it OK?
$cx->oldsp($#PL_stack);
}
else{
$cx->iterary(\@PL_stack);
if($PL_op->private & OPpITER_REVERSED){
$cx->itermax($mark + 1);
$cx->iterix($cx->oldsp + 1);
}
else{
$cx->iterix($mark);
}
}
return $PL_op->next;
}
sub pp_iter{
my $cx = $PL_cxstack[-1];
my $itersv = $cx->ITERVAR;
my $iterary = $cx->iterary;
if(ref($iterary) ne 'ARRAY'){ # iterate range
if(my $cur = $cx->iterlval){
not_implemented 'string range in foreach';
}
# integer increment
if($cx->iterix > $cx->itermax){
PUSH(sv_no);
return $PL_op->next;
}
$itersv->setval($cx->iterix);
$cx->iterix($cx->iterix+1);
PUSH(sv_yes);
return $PL_op->next;
}
# iteratte array
if($PL_op->private & OPpITER_REVERSED){
if($cx->iterix <= $cx->itermax){
PUSH(sv_no);
return $PL_op->next;
}
$cx->iterix($cx->iterix-1);
}
else{
my $max = $iterary == \@PL_stack ? $cx->oldsp : $#{$iterary};
if($cx->iterix >= $max){
PUSH(sv_no);
return $PL_op->next;
}
$cx->iterix($cx->iterix+1);
}
my $sv = $iterary->[$cx->iterix] || sv_no;
$itersv->setsv($sv);
PUSH(sv_yes);
return $PL_op->next;
}
sub pp_lineseq{
return $PL_op->next;
}
sub pp_unstack{
$#PL_stack = $PL_cxstack[-1]->oldsp;
FREETMPS;
LEAVE_SCOPE($PL_scopestack[-1]);
return $PL_op->next;
}
sub pp_stub{
if(GIMME_V == G_SCALAR){
PUSH(sv_undef);
}
return $PL_op->next;
}
sub _dopoptoloop{
my $cxix;
if($PL_op->flags & OPf_SPECIAL){
$cxix = dopoptoloop($#PL_cxstack);
if($cxix < 0){
apvm_die q{Can't "%s" outside a loop block}, $PL_op->name
}
}
else{
$cxix = dopoptolabel($PL_op->pv);
if($cxix < 0){
apvm_die q{Label not found for "%s %s"}, $PL_op->name, $PL_op->pv;
}
}
return $cxix;
}
sub pp_last{
my $cxix = _dopoptoloop();
if($cxix < $#PL_cxstack){
dounwind($cxix);
}
my $cx = POPBLOCK;
my $newsp= $cx->oldsp;
my $mark = $newsp;
my $type = $cx->type;
my $nextop;
if($type eq 'LOOP'){
$newsp = $cx->resetsp;
$nextop = $cx->myop->lastop->next;
}
elsif($type eq 'SUB'){
$nextop = $cx->retop;
}
else{
not_implemented "last($type)";
}
my $gimme = $cx->gimme;
if($gimme == G_SCALAR){
if($mark < $#PL_stack){
$PL_stack[++$newsp] = sv_mortalcopy($PL_stack[-1]);
}
else{
$PL_stack[++$newsp] = sv_undef;
}
}
elsif($gimme == G_SCALAR){
while($mark < $#PL_stack){
$PL_stack[++$newsp] = sv_mortalcopy($PL_stack[-1]);
}
}
$#PL_stack = $newsp;
LEAVE;
if($type eq 'LOOP'){
POPLOOP($cx);
LEAVE;
}
elsif($type eq 'SUB'){
POPSUB($cx);
}
return $nextop;
}
sub pp_next{
my $cxix = _dopoptoloop();
if($cxix < $#PL_cxstack){
dounwind($cxix);
}
my $cx = TOPBLOCK;
LEAVE_SCOPE($PL_scopestack[-1]);
$PL_curcop = $cx->oldcop;
return $cx->nextop;
}
sub pp_redo{
my $cxix = _dopoptoloop();
my $op = $PL_cxstack[$cxix]->myop->redoop;
if($op->name eq 'enter'){
$cxix++;
$op = $op->next;
}
if($cxix < $#PL_cxstack){
dounwind($cxix);
}
my $cx = TOPBLOCK;
LEAVE_SCOPE($PL_scopestack[-2]);
FREETMPS;
$PL_curcop = $cx->oldcop;
return $op;
lib/Acme/Perl/VM/PP.pm view on Meta::CPAN
}
else{
return $PL_op->other;
}
}
sub pp_orassign{
if(SvTRUE(TOP)){
return $PL_op->next;
}
else{
return $PL_op->other;
}
}
sub pp_stringify{
my $sv = TOP;
SETval(SvPV($sv));
return $PL_op->next;
}
sub pp_defined{
my $sv = POP;
my $type = $sv->class;
my $ref = $sv->object_2svref;
my $defined;
if($type eq 'AV'){
$defined = defined @{$ref};
}
elsif($type eq 'HV'){
$defined = defined %{$ref};
}
elsif($type eq 'CV'){
$defined = defined &{$ref};
}
else{
$defined = defined ${$ref};
}
PUSH($defined ? sv_yes : sv_no);
return $PL_op->next;
}
sub pp_range{
if(GIMME_V == G_ARRAY){
return $PL_op->next;
}
if(SvTRUE(GET_TARGET)){
return $PL_op->other;
}
else{
return $PL_op->next;
}
}
sub pp_flip{
if(GIMME_V == G_ARRAY){
return $PL_op->first->other;
}
not_implemented 'flip-flop in scalar context';
}
sub pp_flop{
if(GIMME_V == G_ARRAY){
my $right = POP;
my $left = POP;
my $i = ${$left->object_2svref};
my $max = ${$right->object_2svref};
if(_range_is_numeric($left, $right) && $i >= $max){
return $PL_op->next;
}
$max++;
while($i ne $max){
my $sv = sv_newmortal();
$sv->setval($i);
PUSH($sv);
$i++;
}
}
else{
not_implemented 'flip-flop in scalar context';
}
return $PL_op->next;
}
sub pp_preinc{
${ TOP()->object_2svref }++;
return $PL_op->next;
}
sub pp_postinc{
my $targ = GET_TARGET;
my $sv = TOP;
my $ref = $sv->object_2svref;
if(defined ${$sv}){
$targ->setsv($sv);
}
else{
$targ->setval(0);
}
${$ref}++;
SET($targ);
return $PL_op->next;
}
sub pp_eq{
my $right = POP;
my $left = TOP;
SET(SvNV($left) == SvNV($right) ? sv_yes : sv_no);
return $PL_op->next;
}
sub pp_ne{
my $right = POP;
my $left = TOP;
SET(SvNV($left) != SvNV($right) ? sv_yes : sv_no);
return $PL_op->next;
}
sub pp_lt{
my $right = POP;
my $left = TOP;
SET(SvNV($left) < SvNV($right) ? sv_yes : sv_no);
return $PL_op->next;
}
sub pp_le{
my $right = POP;
my $left = TOP;
SET(SvNV($left) <= SvNV($right) ? sv_yes : sv_no);
return $PL_op->next;
}
sub pp_gt{
my $right = POP;
my $left = TOP;
SET(SvNV($left) > SvNV($right) ? sv_yes : sv_no);
return $PL_op->next;
}
sub pp_ge{
my $right = POP;
lib/Acme/Perl/VM/PP.pm view on Meta::CPAN
my $lval = $PL_op->flags & OPf_MOD || LVRET;
PUSH( svref_2object(\$av->object_2svref->[$PL_op->private]) );
return $PL_op->next;
}
sub pp_aelem{
my $elemsv = POP;
my $av = TOP;
my $lval = $PL_op->flags & OPf_MOD || LVRET;
if($elemsv->ROK){
apvm_warn q{Use of reference %s as array index}, $elemsv->object_2svref;
}
SET( svref_2object(\$av->object_2svref->[SvIV($elemsv)]) );
return $PL_op->next;
}
sub pp_helem{
my $keysv = POP;
my $hv = TOP;
my $lval = $PL_op->flags & OPf_MOD || LVRET;
SET( svref_2object(\$hv->object_2svref->{SvPV($keysv)}) );
return $PL_op->next;
}
sub pp_keys{
return &_do_kv;
}
sub pp_values{
return &_do_kv;
}
sub pp_wantarray{
my $cxix = dopoptosub($#PL_cxstack);
if($cxix < 0){
PUSH(sv_undef);
}
else{
my $gimme = $PL_cxstack[$cxix]->gimme;
if($gimme == G_ARRAY){
PUSH(sv_yes);
}
elsif($gimme == G_SCALAR){
PUSH(sv_no);
}
else{
PUSH(sv_undef);
}
}
return $PL_op->next;
}
sub pp_undef{
if(!$PL_op->private){
PUSH(sv_undef);
return $PL_op->next;
}
not_implemented 'undef(expr)';
}
sub pp_scalar{
return $PL_op->next;
}
sub pp_not{
SET( !SvTRUE(TOP) ? sv_yes : sv_no );
return $PL_op->next;
}
sub pp_qr{
my $re = $PL_op->precomp;
mPUSH(svref_2object(\qr/$re/));
return $PL_op->next;
}
1;
__END__
=head1 NAME
Acme::Perl::VM::PP - ppcodes for APVM
=head1 SYNOPSIS
use Acme::Perl::VM;
=head1 PPCODE
Implemented ppcodes:
=over 4
=item pp_nextstate
=item pp_pushmark
=item pp_const
=item pp_gv
=item pp_padsv
=item pp_padav
=item pp_rv2av
=item pp_list
=item pp_method
=item pp_method_named
=item pp_entersub
=item pp_leavesub
( run in 1.190 second using v1.01-cache-2.11-cpan-5b529ec07f3 )