In a running Perl program if I have an Op address (either by B::Concise, Devel::Callsite or via mysterious other ways) is there a simple way to cast that into the right kind of B::OP, short of walking an Opcode tree?
To try to make this clearer, here's some code:
use Devel::Callsite;
use B::Concise qw(set_style);
use B;
sub testing
{
sub foo { callsite() };
my $op_addr = foo;
printf "Op address is 0x%x\n", $op_addr;
# I can get OPs by walking and looking for $op_addr,
# but I don't want to do that.
my $walker = B::Concise::compile('-terse', '-src', \&testing);
B::Concise::walk_output(\my $buf);
$walker->(); # walks and renders into $buf;
print $buf;
}
testing();
When this is run you'll see something like:
$ perl /tmp/foo.pl
Op address is 0x2026940
B::Concise::compile(CODE(0x1f32b18))
UNOP (0x1f40fd0) leavesub [1]
LISTOP (0x20aa870) lineseq
# 8: my $op_addr = foo;
COP (0x1f7cd80) nextstate
BINOP (0x20aba80) sassign
UNOP (0x20ad200) entersub [2]
UNOP (0x1f39b80) null [148]
OP (0x1fd14f0) pushmark
UNOP (0x1f397c0) null [17]
SVOP (0x1f39890) gv GV (0x1fa0968) *foo
OP (0x2026940) padsv [1]
^^^^^^^^^^
....
So 0x2026940 is the address of a B::OP and and which according to this has next(), sibling(), name() methods. If the address were say 0x20aa870 that would be the address of a LISTOP which has in addition a children() method.
I added B::Concise just to show what's going on. In practice I don't want to walk the optree, because I'm assuming/hoping that the address is in fact where that listop resides.
So perhaps there are two parts, first casting an address to B::Op which I believe is the parent class, but after that I'd like to know which kind of Op, (UNOP, BINOP, LISTOP) we are then talking about.
If I can get the cast part done, the second part is probably easy: all B::OP's have a name() method, so from that I can figure out what subclass of OP I have.
EDIT: ikegami's solution is now part of Devel::Callsite version 1.0.1 al though it isn't quite right.
This duplicates B's internal make_op_object.
use B qw( );
use Inline C => <<'__EOS__';
static const char * const opclassnames[] = {
"B::NULL",
"B::OP",
"B::UNOP",
"B::BINOP",
"B::LOGOP",
"B::LISTOP",
"B::PMOP",
"B::SVOP",
"B::PADOP",
"B::PVOP",
"B::LOOP",
"B::COP",
"B::METHOP",
"B::UNOP_AUX"
};
SV *make_op_object(IV o_addr) {
const OP *o = INT2PTR(OP*, o_addr);
SV *opsv = newSV(0);
sv_setiv(newSVrv(opsv, opclassnames[op_class(o)]), o_addr);
return opsv;
}
__EOS__
Example use:
use Devel::Callsite qw( callsite );
my $site = sub { return callsite() };
my $addr = $site->();
my $op = make_op_object($addr);
say $op->name;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With