Acme-Perl-VM
view release on metacpan or search on metacpan
lib/Acme/Perl/VM.pm view on Meta::CPAN
}
}
use Scalar::Util qw(looks_like_number refaddr);
use Carp ();
use Acme::Perl::VM::Context;
use Acme::Perl::VM::Scope;
use Acme::Perl::VM::PP;
use Acme::Perl::VM::B;
our $PL_runops = (APVM_TRACE || APVM_STACK)
? \&runops_debug
: \&runops_standard;
our $PL_op;
our $PL_curcop;
our @PL_stack;
our @PL_markstack;
our @PL_cxstack;
our @PL_scopestack;
our @PL_savestack;
our @PL_tmps;
our $PL_tmps_floor;
our $PL_comppad;
our $PL_comppad_name;
our @PL_curpad;
our $PL_last_in_gv;
our @PL_ppaddr;
our $color = 'GREEN BOLD'; # for debugging log
sub not_implemented;
{
my $i = 0;
while(my $ppname = B::ppname($i)){
my $ppaddr = \$Acme::Perl::VM::PP::{$ppname};
if(ref($ppaddr) eq 'GLOB'){
$PL_ppaddr[$i] = *{$ppaddr}{CODE};
}
$PL_ppaddr[$i] ||= sub{ not_implemented($ppname) };
$i++;
}
}
sub runops_standard{ # run.c
1 while(${ $PL_op = &{$PL_ppaddr[ $PL_op->type ]} });
return;
}
sub _op_trace{
my $flags = $PL_op->flags;
my $name = $PL_op->name;
deb '.%s', $name;
if(ref($PL_op) eq 'B::COP'){
deb '(%s%s %s:%d)',
($PL_op->label ? $PL_op->label.': ' : ''),
$PL_op->stashpv,
$PL_op->file, $PL_op->line,
;
}
elsif($name eq 'entersub'){
my $gv = TOP;
if(!$gv->isa('B::GV')){
$gv = $gv->GV;
}
deb '(%s)', gv_fullname($gv, '&');
}
elsif($name eq 'aelemfast'){
my $name;
if($flags & OPf_SPECIAL){
my $padname = $PL_comppad_name->ARRAYelt($PL_op->targ);
$name = $padname->POK ? '@'.$padname->PVX : '[...]';
}
else{
$name = gv_fullname(GVOP_gv($PL_op), '@');
}
deb '[%s[%s]]', $name, $PL_op->private;
}
elsif($PL_op->targ && $name !~ /leave/){
if($name eq 'const' || $name eq 'method_named'){
my $sv = PAD_SV($PL_op->targ);
if(is_scalar($sv)){
deb '(%s)', $sv->POK ? B::perlstring($sv->PVX) : $sv->as_string;
}
else{
deb '(%s)', ddx([$sv->object_2svref])->Indent(0)->Dump;
}
}
else{
my $padname = $PL_comppad_name->ARRAYelt($PL_op->targ);
if($padname->POK){
deb '(%s)', $padname->PVX;
deb ' INTRO' if $PL_op->private & OPpLVAL_INTRO;
}
}
}
elsif($PL_op->can('sv')){
my $sv = SVOP_sv($PL_op);
if($sv->class eq 'GV'){
my $prefix = $name eq 'gvsv' ? '$' : '*';
deb '(%s)', gv_fullname($sv, $prefix);
deb ' INTRO' if $PL_op->private & OPpLVAL_INTRO;
}
else{
deb '(%s)', B::perlstring(SvPV(SVOP_sv($PL_op)));
}
}
deb ' VOID' if( ($flags & OPf_WANT) == OPf_WANT_VOID );
deb ' SCALAR' if( ($flags & OPf_WANT) == OPf_WANT_SCALAR );
deb ' LIST' if( ($flags & OPf_WANT) == OPf_WANT_LIST );
deb ' KIDS' if $flags & OPf_KIDS;
deb ' PARENS' if $flags & OPf_PARENS;
deb ' REF' if $flags & OPf_REF;
deb ' MOD' if $flags & OPf_MOD;
deb ' STACKED' if $flags & OPf_STACKED;
deb ' SPECIAL' if $flags & OPf_SPECIAL;
deb "\n";
}
sub runops_debug{
_op_trace();
while(${ $PL_op = &{$PL_ppaddr[$PL_op->type]} }){
if(APVM_STACK){
dump_stack();
}
_op_trace();
}
if(APVM_STACK){
dump_stack();
}
return;
}
sub _deb_colored{
my($fmt, @args) = @_;
printf STDERR Term::ANSIColor::colored($fmt, $color), @args;
return;
}
sub _deb{
my($fmt, @args) = @_;
printf STDERR $fmt, @args;
return;
}
sub mess{ # util.c
my($fmt, @args) = @_;
my $msg = sprintf $fmt, @args;
return sprintf "[APVM] %s in %s at %s line %d.\n",
$msg, $PL_op->desc, $PL_curcop->file, $PL_curcop->line;
}
sub longmess{
my $msg = mess(@_);
my $cxix = $#PL_cxstack;
while( ($cxix = dopoptosub($cxix)) >= 0 ){
my $cx = $PL_cxstack[$cxix];
my $cop = $cx->oldcop;
my $args;
if($cx->argarray){
$args = sprintf '(%s)', join q{,},
map{ defined($_) ? qq{'$_'} : 'undef' }
@{ $cx->argarray->object_2svref };
}
else{
$args = '';
}
my $cvgv = $cx->cv->GV;
$msg .= sprintf qq{[APVM] %s%s called at %s line %d.\n},
gv_fullname($cvgv), $args,
$cop->file, $cop->line;
$cxix--;
}
return $msg;
}
sub apvm_warn{
#warn APVM_DEBUG ? longmess(@_) : mess(@_);
print STDERR longmess(@_);
}
sub apvm_die{
# not yet implemented completely
# cf.
# die_where() in pp_ctl.c
# vdie() in util.c
die APVM_DEBUG ? longmess(@_) : mess(@_);
}
sub croak{
die APVM_DEBUG ? longmess(@_) : mess(@_);
}
sub PUSHMARK(){
push @PL_markstack, $#PL_stack;
return;
}
sub POPMARK(){
return pop @PL_markstack;
}
sub TOPMARK(){
return $PL_markstack[-1];
}
sub PUSH{
push @PL_stack, @_;
return;
}
sub mPUSH{
PUSH(map{ sv_2mortal($_) } @_);
return;
}
sub POP(){
return pop @PL_stack;
}
sub TOP(){
return $PL_stack[-1];
}
sub SET{
my($sv) = @_;
$PL_stack[-1] = $sv;
return;
}
sub SETval{
my($val) = @_;
$PL_stack[-1] = PAD_SV( $PL_op->targ )->setval($val);
return;
}
sub GET_TARGET{
return PAD_SV($PL_op->targ);
}
sub GET_TARGETSTACKED{
return $PL_op->flags & OPf_STACKED ? POP : PAD_SV($PL_op->targ);
}
sub GET_ATARGET{
return $PL_op->flags & OPf_STACKED ? $PL_stack[$#PL_stack-1] : PAD_SV($PL_op->targ);
}
sub MAXARG{
return $PL_op->private & 0x0F;
}
sub PUSHBLOCK{
my($type, %args) = @_;
$args{oldcop} = $PL_curcop;
$args{oldmarksp} = $#PL_markstack;
$args{oldscopesp} = $#PL_scopestack;
my $cx = "Acme::Perl::VM::Context::$type"->new(\%args);
push @PL_cxstack, $cx;
if(APVM_CX){
deb "%s" . "Entering %s\n", (q{>} x @PL_cxstack), $type;
}
return $cx;
}
sub POPBLOCK{
my $cx = pop @PL_cxstack;
$PL_curcop = $cx->oldcop;
$#PL_markstack = $cx->oldmarksp;
$#PL_scopestack = $cx->oldscopesp;
if(APVM_CX){
deb "%s" . "Leaving %s\n", (q{>} x (@PL_cxstack+1)), $cx->type;
}
return $cx;
}
sub TOPBLOCK{
my $cx = $PL_cxstack[-1];
$#PL_stack = $cx->oldsp;
$#PL_markstack = $cx->oldmarksp;
$#PL_scopestack = $cx->oldscopesp;
return $cx;
}
sub POPSUB{
my($cx) = @_;
if($cx->hasargs){
*_ = $cx->savearray;
@{ $cx->argarray->object_2svref } = ();
}
return;
}
sub POPLOOP{
my($cx) = @_;
if($cx->ITERVAR){
lib/Acme/Perl/VM.pm view on Meta::CPAN
return $PL_curpad[ $_[0] ];
}
sub dopoptosub{
my($startingblock) = @_;
for(my $i = $startingblock; $i >= 0; $i--){
my $type = $PL_cxstack[$i]->type;
if($type eq 'EVAL' or $type eq 'SUB'){
return $i;
}
}
return -1;
}
my %loop;
@loop{qw(SUBST SUB EVAL NULL)} = ();
$loop{LOOP} = TRUE;
sub dopoptoloop{
my($startingblock) = @_;
for(my $i = $startingblock; $i >= 0; --$i){
my $cx = $PL_cxstack[$i];
my $type = $cx->type;
if(exists $loop{$type}){
if(!$loop{$type}){
apvm_warn 'Exsiting %s via %s', $type, $PL_op->name;
$i = -1 if $type eq 'NULL';
}
return $i;
}
}
return -1;
}
sub dopoptolabel{
my($label) = @_;
for(my $i = $#PL_cxstack; $i >= 0; --$i){
my $cx = $PL_cxstack[$i];
my $type = $cx->type;
if(exists $loop{$type}){
if(!$loop{$type}){
apvm_warn 'Exsiting %s via %s', $type, $PL_op->name;
return $type eq 'NULL' ? -1 : $i;
}
elsif($cx->label && $cx->label eq $label){
return $i;
}
}
}
return -1;
}
sub OP_GIMME{ # op.h
my($op, $default) = @_;
my $op_gimme = $op->flags & OPf_WANT;
return $op_gimme == OPf_WANT_VOID ? G_VOID
: $op_gimme == OPf_WANT_SCALAR ? G_SCALAR
: $op_gimme == OPf_WANT_LIST ? G_ARRAY
: $default;
}
sub OP_GIMME_REVERSE{ # op.h
my($flags) = @_;
$flags &= G_WANT;
return $flags == G_VOID ? OPf_WANT_VOID
: $flags == G_SCALAR ? OPf_WANT_SCALAR
: OPf_WANT_LIST;
}
sub gimme2want{
my($gimme) = @_;
$gimme &= G_WANT;
return $gimme == G_VOID ? undef
: $gimme == G_SCALAR ? 0
: 1;
}
sub want2gimme{
my($wantarray) = @_;
return !defined($wantarray) ? G_VOID
: !$wantarray ? G_SCALAR
: G_ARRAY;
}
sub block_gimme{
my $cxix = dopoptosub($#PL_cxstack);
if($cxix < 0){
return G_VOID;
}
return $PL_cxstack[$cxix]->gimme;
}
sub GIMME_V(){ # op.h
my $gimme = OP_GIMME($PL_op, -1);
return $gimme != -1 ? $gimme : block_gimme();
}
sub LVRET(){ # cf. is_lvalue_sub() in pp_ctl.h
if($PL_op->flags & OPpMAYBE_LVSUB){
my $cxix = dopoptosub($#PL_cxstack);
if($PL_cxstack[$cxix]->lval && $PL_cxstack[$cxix]->cv->CvFLAGS & CVf_LVALUE){
not_implemented 'lvalue';
return TRUE;
}
}
return FALSE;
}
sub SVOP_sv{
my($op) = @_;
return USE_ITHREADS ? PAD_SV($op->padix) : $op->sv;
}
sub GVOP_gv{
my($op) = @_;
return USE_ITHREADS ? PAD_SV($op->padix) : $op->gv;
}
sub vivify_ref{
not_implemented 'vivify_ref';
}
sub sv_newmortal{
my $sv;
push @PL_tmps, \$sv;
return B::svref_2object(\$sv);
}
sub sv_mortalcopy{
my($sv) = @_;
if(!defined $sv){
Carp::confess('sv_mortalcopy(NULL)');
}
my $newsv =${$sv->object_2svref};
push @PL_tmps, \$newsv;
return B::svref_2object(\$newsv);
}
sub sv_2mortal{
my($sv) = @_;
if(!defined $sv){
Carp::confess('sv_2mortal(NULL)');
}
push @PL_tmps, $sv->object_2svref;
return $sv;
}
sub SvTRUE{
my($sv) = @_;
return ${ $sv->object_2svref } ? TRUE : FALSE;
}
sub SvPV{
my($sv) = @_;
my $ref = $sv->object_2svref;
lib/Acme/Perl/VM.pm view on Meta::CPAN
}
last;
}
}
$varname . Data::Dumper->Dump([is_scalar($_) ? ${$_->object_2svref} : $_->object_2svref], [$_->ROK ? 'SV' : '*SV']);
}
} @PL_stack;
return;
}
sub _dump_stack{
my $warn;
my $ddx = ddx([[map{
if(ref $_){
is_scalar($_) ? ${$_->object_2svref} : $_->object_2svref;
}
else{
$warn++;
$_;
}
} @PL_stack]], ['*PL_stack']);
$ddx->Indent(0);
deb " %s\n", $ddx->Dump();
if($warn){
apvm_die 'No sv found (%d)', $warn;
}
return;
}
sub dump_si{
my %stack_info = (
stack => \@PL_stack,
markstack => \@PL_markstack,
cxstack => \@PL_cxstack,
scopstack => \@PL_scopestack,
savestack => \@PL_savestack,
tmps => \@PL_tmps,
);
ddx([\%stack_info]);
}
sub not_implemented{
if(!@_){
if($PL_op && is_not_null($PL_op)){
@_ = ($PL_op->name);
}
else{
@_ = (caller 0)[3];
}
}
push @_, ' is not implemented';
goto &Carp::confess;
}
sub call_sv{ # perl.h
my($sv, $flags) = @_;
if($flags & G_DISCARD){
ENTER;
SAVETMPS;
}
my $cv = $sv->toCV();
my $old_op = $PL_op;
my $old_cop = $PL_curcop;
$PL_op = Acme::Perl::VM::OP_CallSV->new(
cv => $cv,
next => NULL,
flags => OP_GIMME_REVERSE($flags),
);
$PL_curcop = $PL_op;
PUSH($cv);
my $oldmark = TOPMARK;
$PL_runops->();
my $retval = $#PL_stack - $oldmark;
if($flags & G_DISCARD){
$#PL_stack = $oldmark;
$retval = 0;
FREETMPS;
LEAVE;
}
$PL_op = $old_op;
$PL_curcop = $old_cop;
return $retval;
}
sub run_block(&@){
my($code, @args) = @_;
if(APVM_DUMMY){
return $code->(@args);
}
local $SIG{__DIE__} = \&Carp::confess if APVM_DEBUG;
local $SIG{__WARN__} = \&Carp::cluck if APVM_DEBUG;
ENTER;
SAVETMPS;
PUSHMARK;
PUSH(@args);
my $gimme = want2gimme(wantarray);
my $mark = $#PL_stack - call_sv(B::svref_2object($code), $gimme);
my @retval = mark_list($mark);
FREETMPS;
LEAVE;
if($gimme == G_SCALAR){
return $retval[-1];
}
elsif($gimme == G_ARRAY){
return @retval;
}
return;
}
package
Acme::Perl::VM::OP_CallSV;
use Mouse;
has cv => (
is => 'ro',
isa => 'B::CV',
required => 1,
);
has next => (
is => 'ro',
isa => 'B::OBJECT',
required => 1,
);
has flags => (
is => 'ro',
isa => 'Int',
required => 1,
);
use constant {
class => 'OP',
type => B::opnumber('entersub'),
name => 'entersub',
desc => 'subroutine entry',
file => __FILE__,
line => 0,
};
sub isa{
shift;
return B::COP->isa(@_);
}
no Mouse;
__PACKAGE__->meta->make_immutable();
package
Acme::Perl::VM::Alias;
sub TIESCALAR{
my($class, $scalar_ref) = @_;
return bless [$scalar_ref], $class;
}
sub FETCH{
return ${ $_[0]->[0] }
}
sub STORE{
${ $_[0]->[0] } = $_[1];
return;
}
1;
__END__
=head1 NAME
Acme::Perl::VM - A Perl5 Virtual Machine in Pure Perl (APVM)
=head1 VERSION
This document describes Acme::Perl::VM version 0.006.
=head1 SYNOPSIS
use Acme::Perl::VM;
run_block{
print "Hello, APVM world!\n",
};
( run in 2.056 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )