【问题标题】:How does mro, goto, and set_subname interact?mro、goto 和 set_subname 如何交互?
【发布时间】:2019-11-03 02:49:56
【问题描述】:

这是一个关于mro.pm 的复杂问题,以及与set_subnamegoto 的相互作用

在解决问题时,我认为我误解的核心与mro.pm 的工作原理有关——尤其是关于set_subname

这三种构造有什么区别,

  1. 直接致电set_subname

    *Foo::bar = set_subname( 'Foo::bar', $codeRef );
    
  2. 包装set_subname的匿名子

    *Foo::bar = sub {
      my $codeRef2 = set_subname('Foo::bar', $codeRef);
      goto $codeRef2
    };
    
  3. 名称设置为set_subname的匿名子

    *Foo::bar = set_subname(
      'Foo::bar',
      sub { goto $codeRef }
    );
    

具体来说,Mojo 测试套件会因这些修改中的任何一个而失败,并且将匿名潜艇应用于 Mojo::Utils's monkey_patcht/mojo/websocket_proxy.t 运行上述两个变体,

  • 我有 2(第二个)选项

    *{"${class}::$k"} = sub {                                                                                                                          
      my $cr = set_subname("${class}::$k", $patch{$k});                                                                                                
      goto $cr;                                                                                                                                        
    }; 
    

    我得到了

    Mojo::Reactor::Poll: Timer failed: Can't locate object method "send" via package "Mojo::Transaction::HTTP" at t/mojo/websocket_proxy.t line 66.
    
  • 我有 3(第三个)选项,

    *{"${class}::$k"} = set_subname("${class}::$k", sub { goto $patch{$k} })
    

    我得到了

    No next::method 'new' found for Mojolicious::Routes at /usr/lib/x86_64-linux-gnu/perl/5.28/mro.pm line 30.
    

显然,第一个版本有效(它来自我链接的代码),问题是为什么其他两个变体给我不同的错误(尤其是第二个变体)以及那里发生了什么 - 为什么它们不工作?

【问题讨论】:

  • 我来这里是因为我未能找出问题所在。我想知道这三件事是如何相互作用的,这样我才能更好地理解这一点并隔离问题。这里的问题不是由我想要获得的效果驱动的。我试图解释为什么三件事的行为不同。

标签: perl goto method-resolution-order perlguts


【解决方案1】:

您的第二个选项不起作用,因为您用作包装器的 sub 与内部 sub 的原型不匹配。 monkey_patch 不仅用于方法,而且这改变了某些函数的解析方式。特别是,Mojo::Util::steady_time 有一个空原型,并且经常在不使用括号的情况下调用。

*{"${class}::$k"} = Sub::Util::set_prototype(
  Sub::Util::prototype( $patch{$k} ),
  Sub::Util::set_subname(
    "${class}::$k",
    sub {
      my $cr = Sub::Util::set_subname("${class}::$k", $patch{$k});
      goto $cr;
    }
  )
);

第三个构造不起作用,因为您正在使用 goto 从调用堆栈中删除重命名的包装子,只留下没有名称的内部子。这破坏了next::method 找到正确方法名称的能力。

【讨论】:

  • 哇!!所以原型把它扔掉了。无论如何,在包装原型子方面是否可以实现我想要的?
  • 啊,我没想到错误可能是由函数调用而不是方法引发的。然而,错误消息来自$tx->send('test1'),这是一个方法调用。原型在那里被忽略。 Evan,尝试在测试中使用 Carp::Always,当它死掉时你会得到一个完整的堆栈跟踪。
  • 出现问题是因为steady_time 上没有正确的原型,所有计时器都会立即解决。这会导致事务在有时间升级为 websocket 事务之前得到解决,这是->send 调用所必需的。
【解决方案2】:

这确实很复杂,但也许你过于复杂了?

请记住,MRO 只关心通过已定义的包名称顺序来定位一个方法,该方法只是 coderef 的符号表条目。内部子名仅与 caller() 报告 AFAIK 的内容有关。

来自:Mojo

*{"${class}::$_"} =          ## symbol table entry
set_subname("${class}::$_",  ## an internal name
   $patch{$_})               ## for a code ref
for keys %patch;

HTH

看到错误消息后编辑:

子程序没有被有效安装。我怀疑,由于在选项 2 和 3 中,您将调用 set_subname() 推迟到调用时间,因此 coderef $patch{$k} 永远不会分配子名称,并且会破坏 mro::_nextcan 链中的链接() 的 XS 魔法。特别是如果 $patch{$k} 调用 next::method。不过,这些关闭似乎是有效的。

虽然我必须说我的测试似乎表明选项 2 是有效的。

Enter command: my ($class, $k) = qw/W S/; my %patch = (S =>
sub {print "patch here\n"; decall;}); *{"${class}::$k"} = 
sub { print "goto'r here\n";  my $cr = set_subname("${class}::$k",
$patch{$k}); goto $cr;};


Enter command: decall
0       "console"
1       "console.pl"
2       "114"
3       "(eval)"
4       "0"
5       0
6       "package W; decall"
7       ""
8       "256"
9       "\020\001\000\000\000P\004\000\000\000\000\000\000U\025U\005"
10      0

Enter command: S
goto'r here
patch here
0       "W"
1       "(eval 110)"
2       "1"
3       "W::S"
4       "1"
5       0
6       0
7       0
8       "256"
9       "\020\001\000\000\000P\004\000\000\000\000\000\000U\025U\005"
10      0

对于选项 2 的问题,您可能不得不开始寻找更远的地方。

【讨论】:

  • 我不确定这是否能回答问题。向我解释为什么这没有按照相同的逻辑通过 Mojo 测试套件,foreach my $k ( keys %patch ) { *{"${class}::$k"} = set_subname("${class}::$k", sub {goto $patch{$k}) }
  • 我已经用实际的错误消息更新了问题。
  • 我也很难隔离它。我只能说这似乎是通过用我的答案中的其他方法更改那一行(我第一次链接并且您复制到您的答案中)来发生的。为什么会发生是问题的基础。我无法隔离它。如果您能解释mro::_nextcan()'s 方法如何以不同的方式处理这三个示例,我会对答案感到满意并将其标记为已选择。这可能正是我不明白的。
【解决方案3】:

修改 Mojo/Util.pm 后使用

  foreach my $k (keys %patch) {
    *{"${class}::$k"} = sub {
      my $cr = set_subname("${class}::$k", $patch{$k});
      goto $cr;
    };
   }

并隔离测试用例,我得到:

$ perl -MCarp::Always t/mojo/websocket_proxy2.t
Mojo::Reactor::Poll: Timer failed: Can't locate object method "send" via package "Mojo::Transaction::HTTP" at t/mojo/websocket_proxy2.t line 59.
        main::__ANON__(Mojo::UserAgent=HASH(0x60280dad0), Mojo::Transaction::HTTP=HASH(0x6029ee7b0)) called at blib/lib/Mojo/UserAgent.pm line 252
        Mojo::UserAgent::_finish(Mojo::UserAgent=HASH(0x60280dad0), "927210c53042c6142eda3f4010c8b17c", 1) called at blib/lib/Mojo/UserAgent.pm line 220
        Mojo::UserAgent::_error(Mojo::UserAgent=HASH(0x60280dad0), "927210c53042c6142eda3f4010c8b17c", "Connect timeout") called at blib/lib/Mojo/UserAgent.pm line 128
        Mojo::UserAgent::__ANON__(Mojo::IOLoop=HASH(0x601f9abb8), "Connect timeout", undef) called at blib/lib/Mojo/IOLoop.pm line 63
        Mojo::IOLoop::__ANON__(Mojo::IOLoop::Client=HASH(0x601e34598)) called at blib/lib/Mojo/EventEmitter.pm line 15
        Mojo::EventEmitter::emit(Mojo::IOLoop::Client=HASH(0x601e34598), "error", "Connect timeout") called at blib/lib/Mojo/IOLoop/Client.pm line 39
        Mojo::IOLoop::Client::__ANON__(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 143
        eval {...} called at blib/lib/Mojo/Reactor/Poll.pm line 143
        Mojo::Reactor::Poll::_try(Mojo::Reactor::Poll=HASH(0x6001a8390), "Timer", CODE(0x601e24ca0)) called at blib/lib/Mojo/Reactor/Poll.pm line 81
        Mojo::Reactor::Poll::one_tick(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 99
        Mojo::Reactor::Poll::start(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/IOLoop.pm line 134
        Mojo::IOLoop::start("Mojo::IOLoop") called at t/mojo/websocket_proxy2.t line 62
 at blib/lib/Mojo/IOLoop.pm line 23.
        Mojo::IOLoop::__ANON__(Mojo::Reactor::Poll=HASH(0x6001a8390), "Timer failed: Can't locate object method \"send\" via package \"Mojo::Transaction::HTTP\" at t/mojo/websocket_proxy2.t line 59.\x{a}\x{9}main::__ANON__(Mojo::UserAgent=HASH(0x60280dad0), Mojo::Transaction::HTTP=HASH(0x6029ee7b0)) called at blib/lib/Mojo/UserAgent.pm line 252\x{a}\x{9}Mojo::UserAgent::_finish(Mojo::UserAgent=HASH(0x60280dad0), \"927210c53042c6142eda3f4010c8b17c\", 1) called at blib/lib/Mojo/UserAgent.pm line 220\x{a}\x{9}Mojo::UserAgent::_error(Mojo::UserAgent=HASH(0x60280dad0), \"927210c53042c6142eda3f4010c8b17c\", \"Connect timeout\") called at blib/lib/Mojo/UserAgent.pm line 128\x{a}\x{9}Mojo::UserAgent::__ANON__(Mojo::IOLoop=HASH(0x601f9abb8), \"Connect timeout\", undef) called at blib/lib/Mojo/IOLoop.pm line 63\x{a}\x{9}Mojo::IOLoop::__ANON__(Mojo::IOLoop::Client=HASH(0x601e34598)) called at blib/lib/Mojo/EventEmitter.pm line 15\x{a}\x{9}Mojo::EventEmitter::emit(Mojo::IOLoop::Client=HASH(0x601e34598), \"error\", \"Connect timeout\") called at blib/lib/Mojo/IOLoop/Client.pm line 39\x{a}\x{9}Mojo::IOLoop::Client::__ANON__(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 143\x{a}\x{9}eval {...} called at blib/lib/Mojo/Reactor/Poll.pm line 143\x{a}\x{9}Mojo::Reactor::Poll::_try(Mojo::Reactor::Poll=HASH(0x6001a8390), \"Timer\", CODE(0x601e24ca0)) called at blib/lib/Mojo/Reactor/Poll.pm line 81\x{a}\x{9}Mojo::Reactor::Poll::one_tick(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 99\x{a}\x{9}Mojo::Reactor::Poll::start(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/IOLoop.pm line 134\x{a}\x{9}Mojo::IOLoop::start(\"Mojo::IOLoop\") called at t/mojo/websocket_proxy2.t line 62\x{a}") called at blib/lib/Mojo/EventEmitter.pm line 15
        Mojo::EventEmitter::emit(Mojo::Reactor::Poll=HASH(0x6001a8390), "error", "Timer failed: Can't locate object method \"send\" via package \"Mojo::Transaction::HTTP\" at t/mojo/websocket_proxy2.t line 59.\x{a}\x{9}main::__ANON__(Mojo::UserAgent=HASH(0x60280dad0), Mojo::Transaction::HTTP=HASH(0x6029ee7b0)) called at blib/lib/Mojo/UserAgent.pm line 252\x{a}\x{9}Mojo::UserAgent::_finish(Mojo::UserAgent=HASH(0x60280dad0), \"927210c53042c6142eda3f4010c8b17c\", 1) called at blib/lib/Mojo/UserAgent.pm line 220\x{a}\x{9}Mojo::UserAgent::_error(Mojo::UserAgent=HASH(0x60280dad0), \"927210c53042c6142eda3f4010c8b17c\", \"Connect timeout\") called at blib/lib/Mojo/UserAgent.pm line 128\x{a}\x{9}Mojo::UserAgent::__ANON__(Mojo::IOLoop=HASH(0x601f9abb8), \"Connect timeout\", undef) called at blib/lib/Mojo/IOLoop.pm line 63\x{a}\x{9}Mojo::IOLoop::__ANON__(Mojo::IOLoop::Client=HASH(0x601e34598)) called at blib/lib/Mojo/EventEmitter.pm line 15\x{a}\x{9}Mojo::EventEmitter::emit(Mojo::IOLoop::Client=HASH(0x601e34598), \"error\", \"Connect timeout\") called at blib/lib/Mojo/IOLoop/Client.pm line 39\x{a}\x{9}Mojo::IOLoop::Client::__ANON__(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 143\x{a}\x{9}eval {...} called at blib/lib/Mojo/Reactor/Poll.pm line 143\x{a}\x{9}Mojo::Reactor::Poll::_try(Mojo::Reactor::Poll=HASH(0x6001a8390), \"Timer\", CODE(0x601e24ca0)) called at blib/lib/Mojo/Reactor/Poll.pm line 81\x{a}\x{9}Mojo::Reactor::Poll::one_tick(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 99\x{a}\x{9}Mojo::Reactor::Poll::start(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/IOLoop.pm line 134\x{a}\x{9}Mojo::IOLoop::start(\"Mojo::IOLoop\") called at t/mojo/websocket_proxy2.t line 62\x{a}") called at blib/lib/Mojo/Reactor/Poll.pm line 143
        Mojo::Reactor::Poll::_try(Mojo::Reactor::Poll=HASH(0x6001a8390), "Timer", CODE(0x601e24ca0)) called at blib/lib/Mojo/Reactor/Poll.pm line 81
        Mojo::Reactor::Poll::one_tick(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/Reactor/Poll.pm line 99
        Mojo::Reactor::Poll::start(Mojo::Reactor::Poll=HASH(0x6001a8390)) called at blib/lib/Mojo/IOLoop.pm line 134
        Mojo::IOLoop::start("Mojo::IOLoop") called at t/mojo/websocket_proxy2.t line 62

我也可以确认设置原型可以解决问题。

【讨论】:

    猜你喜欢
    • 2014-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 2011-11-28
    相关资源
    最近更新 更多