mro,goto和set_subname如何交互?

问题描述 投票:3回答:3

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

[解决问题时,我认为我的误解的核心与mro.pm的工作方式有关,尤其是与set_subname有关。

这三种结构之间有什么区别,

  1. set_subname的普通电话

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

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

    *Foo::bar = set_subname(
      'Foo::bar',
      sub { goto $codeRef }
    );
    
  4. 特别是,Mojo测试服由于上述修改之一而失败,并且在Mojo::Utils's monkey_patch上应用了一个匿名子,对Mojo::Utils运行了以上两个变体,

  • 我有2(第二个)选项

    monkey_patch

    我得到

    t/mojo/websocket_proxy.t
  • 我有3(第三个)选项,

    *{"${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.

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

关于mro.pm以及与set_subname和goto的相互作用这是一个复杂的问题,在解决问题时,我认为我的误解的核心与mro.pm的工作原理有关-...

您的第二个选项不起作用,因为您要用作包装器的子容器与内部子容器的原型不匹配。 *{"${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. 具有一个空原型,并且经常在不使用括号的情况下被调用。

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; } ) ); 查找正确方法名称的能力。

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

[请记住,MRO只关心通过包名称的定义顺序来定位方法,该方法只是代码引用的符号表项。内部子名称仅与caller()报告AFAIK的内容有关。

来自:Mojo

goto

HTH

看到错误消息后进行编辑:

尚未正确安装子例程。我怀疑由于在选项2和3中您将对set_subname()的调用推迟到调用时间,因此coderef $ patch {$ k}从未分配有子名称,并且中断了mro :: _ nextcan链中的链接()的XS魔术。特别是如果$ patch {$ k}调用next :: method。闭包似乎是有效的。

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

next::method

您可能必须开始更远地寻找选项2的问题。

用修改Mojo / Util.pm后

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

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

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

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

perl goto method-resolution-order perlguts
3个回答
2
投票

您的第二个选项不起作用,因为您要用作包装器的子容器与内部子容器的原型不匹配。 *{"${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. 具有一个空原型,并且经常在不使用括号的情况下被调用。


2
投票

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

[请记住,MRO只关心通过包名称的定义顺序来定位方法,该方法只是代码引用的符号表项。内部子名称仅与caller()报告AFAIK的内容有关。


0
投票

用修改Mojo / Util.pm后

*{"${class}::$_"} =          ## symbol table entry
set_subname("${class}::$_",  ## an internal name
   $patch{$_})               ## for a code ref
for keys %patch;
© www.soinside.com 2019 - 2024. All rights reserved.