如何将操作地址转换为正确的B::op



在运行的Perl程序中,如果我有一个Op地址(通过B::Concise、Devel::Callsite或通过神秘的其他方式(,除了遍历Opcode树之外,有没有一种简单的方法可以将其转换为正确的B::Op?

为了让这一点更清楚,这里有一些代码:

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%xn", $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();

运行时,您会看到类似以下内容:

$ 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]
^^^^^^^^^^ 
....

因此0x2026940B::OP的地址,并且根据该地址具有next()sibling()name()方法。如果地址是0x20aa870,则它将是另外具有children()方法的LISTOP的地址。

我添加了B::简洁只是为了显示正在发生的事情。在实践中,我不想走optree,因为我假设/希望地址实际上就是listop所在的地方。

因此,可能有两个部分,首先向B::Op投射一个地址,我认为它是父类,但在那之后,我想知道我们谈论的是哪种Op(UNOP、BINOP、LISTOP(。

如果我能完成强制转换部分,那么第二部分可能很容易:所有的B::OP都有一个name()方法,这样我就可以弄清楚我有什么OP子类。

EDIT:ikegami的解决方案现在是Devel::Callsite 1.0.1版本的一部分,尽管它并不完全正确。

这复制了B的内部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__

示例用法:

use Devel::Callsite qw( callsite );
my $site = sub { return callsite() };
my $addr = $site->();
my $op = make_op_object($addr);
say $op->name;

相关内容

  • 没有找到相关文章

最新更新