如何将操作地址转换为正确的B :: OP类型?

时间:2018-07-17 00:46:49

标签: perl perl-xs

在运行的Perl程序中,如果我有一个Op地址(通过B::ConciseDevel::Callsite或通过其他神秘方式),有一种简单的方法可以将其转换为正确的B: OP,还没有走一棵Opcode树?

为使这一点更清楚,下面是一些代码:

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();

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

$ 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的地址,并且根据this具有next()sibling()name()方法。如果说的地址是0x20aa870,那将是另外具有LISTOP方法的children()的地址。

我添加了B :: Concise只是为了说明正在发生的事情。在实践中,我不想走optree,因为我假设/希望地址实际上是该listop所在的地址。

所以也许有两部分,首先是我认为是父类的地址B::Op的地址,但是在那之后,我想知道我们使用哪种类型的操作(UNOP,BINOP,LISTOP)然后在谈论。

如果我可以完成转换部分,那么第二部分可能很简单:所有B::OP都有一个name()方法,因此我可以弄清楚我拥有OP的哪个子类。

编辑: ikegami的解决方案现在不是Devel :: Callsite版本1.0.1的一部分,尽管还不太正确。

1 个答案:

答案 0 :(得分:4)

这复制了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;