Acme-Perl-VM
view release on metacpan or search on metacpan
lib/Acme/Perl/VM.pm view on Meta::CPAN
APVM_CX => scalar(APVM_DEBUG =~ /\b (?: cx | context ) \b/xmsi),
APVM_STACK => scalar(APVM_DEBUG =~ /\b stack \b/xmsi),
APVM_DUMMY => scalar(APVM_DEBUG =~ /\b dummy \b/xmsi),
};
use Exporter qw(import);
BEGIN{
our @EXPORT = qw(run_block call_sv);
our @EXPORT_OK = qw(
$PL_op $PL_curcop
@PL_stack @PL_markstack @PL_cxstack @PL_scopestack @PL_savestack @PL_tmps
$PL_tmps_floor
$PL_comppad $PL_comppad_name @PL_curpad
$PL_last_in_gv
$PL_runops
@PL_ppaddr
PUSHMARK POPMARK TOPMARK
PUSH POP TOP SET SETval
mPUSH
GET_TARGET
GET_TARGETSTACKED
GET_ATARGET
MAXARG
PUSHBLOCK POPBLOCK TOPBLOCK
PUSHSUB POPSUB
PUSHLOOP POPLOOP
dounwind
ENTER LEAVE LEAVE_SCOPE
SAVETMPS FREETMPS
SAVE SAVECOMPPAD SAVECLEARSV
SAVEPADSV
save_scalar save_ary save_hash
OP_GIMME GIMME_V LVRET
PAD_SV PAD_SET_CUR_NOSAVE PAD_SET_CUR
CX_CURPAD_SAVE CX_CURPAD_SV
dopoptosub dopoptoloop dopoptolabel
deb apvm_warn apvm_die croak ddx
GVOP_gv
vivify_ref
sv_newmortal sv_mortalcopy sv_2mortal
SvPV SvNV SvIV SvTRUE
av_assign av_store
hv_store hv_store_ent hv_scalar
defoutgv
gv_fullname
looks_like_number
sv_defined is_null is_not_null
mark_list
not_implemented
dump_object dump_value dump_stack dump_si
apvm_extern
cv_external
APVM_DEBUG APVM_DUMMY
APVM_SCOPE APVM_TRACE
);
our %EXPORT_TAGS = (
perl_h => \@EXPORT_OK,
);
if(APVM_DEBUG && -t *STDERR){
require Term::ANSIColor;
*deb = \&_deb_colored;
}
else{
*deb = \&_deb;
}
}
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')){
lib/Acme/Perl/VM.pm view on Meta::CPAN
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;
if(!defined ${$ref}){
apvm_warn 'Use of uninitialized value';
return q{};
}
return "${$ref}";
}
sub SvNV{
my($sv) = @_;
my $ref = $sv->object_2svref;
if(!defined ${$ref}){
apvm_warn 'Use of uninitialized value';
return 0;
}
return ${$ref} + 0;
}
sub SvIV{
lib/Acme/Perl/VM.pm view on Meta::CPAN
tie $ref->[$i], 'Acme::Perl::VM::Alias', $_[$i]->object_2svref;
}
return;
}
sub av_store{
my($av, $ix, $sv) = @_;
tie $av->object_2svref->[$ix],
'Acme::Perl::VM::Alias', $sv->object_2svref;
return;
}
sub hv_store{
my($hv, $key, $sv) = @_;
tie $hv->object_2svref->{$key},
'Acme::Perl::VM::Alias', $sv->object_2svref;
return;
}
sub hv_store_ent{
my($hv, $key, $sv) = @_;
tie $hv->object_2svref->{ ${$key->object_2svref} },
'Acme::Perl::VM::Alias', $sv->object_2svref;
return;
}
sub hv_scalar{
my($hv) = @_;
my $sv = sv_newmortal();
$sv->setval(scalar %{ $hv->object_2svref });
return $sv;
}
sub defoutgv{
no strict 'refs';
return \*{ select() };
}
sub gv_fullname{
my($gv, $prefix) = @_;
$prefix = '' unless defined $prefix;
my $stashname = $gv->STASH->NAME;
if($stashname eq 'main'){
$prefix .= $gv->SAFENAME;
}
else{
$prefix .= join q{::}, $stashname, $gv->SAFENAME;
}
return $prefix;
}
# Utilities
sub sv_defined{
my($sv) = @_;
return $sv && ${$sv} && defined(${ $sv->object_2svref });
}
sub is_not_null{
my($sv) = @_;
return ${$sv};
}
sub is_null{
my($sv) = @_;
return !${$sv};
}
my %not_a_scalar;
@not_a_scalar{qw(AV HV CV IO)} = ();
sub is_scalar{
my($sv) = @_;
return !exists $not_a_scalar{ $sv->class };
}
sub mark_list{
my($mark) = @_;
return map{ ${ $_->object_2svref } } splice @PL_stack, $mark+1;
}
our %external;
sub apvm_extern{
foreach my $arg(@_){
if(ref $arg){
if(ref($arg) ne 'CODE'){
Carp::croak('Not a CODE reference for apvm_extern()');
}
$external{refaddr $arg} = 1;
}
else{
my $stash = do{ no strict 'refs'; \%{$arg .'::'} };
while(my $name = each %{$stash}){
my $code_ref = do{ no strict 'refs'; *{$arg . '::' . $name}{CODE} };
if(defined $code_ref){
$external{refaddr $code_ref} = 1;
}
}
}
}
return;
}
sub cv_external{
my($cv) = @_;
return $cv->XSUB || $external{ ${$cv} };
}
sub ddx{
require Data::Dumper;
my $ddx = Data::Dumper->new(@_);
$ddx->Indent(1);
$ddx->Terse(TRUE);
$ddx->Quotekeys(FALSE);
$ddx->Useqq(TRUE);
return $ddx if defined wantarray;
my $name = ( split '::', (caller 2)[3] )[-1];
print STDERR $name, ': ', $ddx->Dump(), "\n";
return;
}
sub dump_object{
ddx([[ map{ $_ ? $_->object_2svref : $_ } @_ ]]);
}
sub dump_value{
ddx([\@_]);
}
sub dump_stack{
require Data::Dumper;
lib/Acme/Perl/VM.pm view on Meta::CPAN
$varname = '&' . gv_fullname($_->GV);
}
else{
for(my $padix = 0; $padix < @PL_curpad; $padix++){
my $padname;
if(${ $PL_curpad[$padix] } == ${ $_ }){
$padname = $PL_comppad_name->ARRAYelt($padix);
}
elsif($_->ROK && ${$PL_curpad[$padix]} == ${ $_->RV }){
$padname = $PL_comppad_name->ARRAYelt($padix);
$varname .= '\\';
}
if($padname){
if($padname->POK){
$varname .= $padname->PVX . ' ';
}
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;
( run in 2.102 seconds using v1.01-cache-2.11-cpan-9581c071862 )