3

This is a complex question with regard to mro.pm, and the interplay with set_subname, and goto

In troubleshooting a problem, I think the core of my misunderstanding relates to how mro.pm works -- especially with regard to set_subname.

What is the difference between these three constructs,

  1. Plain call to set_subname

    *Foo::bar = set_subname( 'Foo::bar', $codeRef );
    
  2. Anon sub which wraps a set_subname

    *Foo::bar = sub {
      my $codeRef2 = set_subname('Foo::bar', $codeRef);
      goto $codeRef2
    };
    
  3. Anon sub which has its name set with set_subname

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

Specifically, the Mojo test suits fails with either of these modifications with anon subs applied to Mojo::Utils's monkey_patch Running the two variants above against t/mojo/websocket_proxy.t,

  • With the 2 (second) option I have

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

    And I get

    Mojo::Reactor::Poll: Timer failed: Can't locate object method "send" via package "Mojo::Transaction::HTTP" at t/mojo/websocket_proxy.t line 66.
    
  • With the 3 (third) option I have,

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

    And I get

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

Obviously, the first version works (it's from the code I linked), the question is why are the other two variants giving me different errors (especially the second variant) and what's happening there -- why don't they work?

4

3 回答 3

2

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

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

出自:莫乔

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

高温高压

看到错误消息后编辑:

子例程没有被有效安装。我怀疑,由于在选项 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 的问题。

于 2019-11-03T03:19:16.817 回答
2

您的第二个选项不起作用,因为您用作包装器的子与内部子的原型不匹配。 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了找到正确方法名称的能力。

于 2019-11-03T21:28:00.063 回答
0

修改 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

我还可以确认设置原型可以修复它。

于 2019-11-04T05:37:47.293 回答