这是一个关于 mro.pm
以及与 set_subname
和 goto
相互作用的复杂问题
在解决问题时,我认为我的误解的核心与 mro.pm
的工作方式有关 - 特别是关于 set_subname
。
这三种结构有什么区别,
简单调用
set_subname
*Foo::bar = set_subname( 'Foo::bar', $codeRef );
包装
set_subname
的匿名子*Foo::bar = sub { my $codeRef2 = set_subname('Foo::bar', $codeRef); goto $codeRef2 };
使用
set_subname
设置名称的匿名子*Foo::bar = set_subname( 'Foo::bar', sub { goto $codeRef } );
具体来说,Mojo 测试套件在将匿名子应用到 Mojo::Utils
's monkey_patch
时会因这些修改而失败。针对 t/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.
显然,第一个版本可以工作(来 self 链接的代码),问题是为什么其他两个变体给我带来不同的错误(尤其是第二个变体)以及那里发生了什么 - 为什么它们不工作?
最佳答案
您的第二个选项不起作用,因为您用作包装器的子组件与内部子组件的原型(prototype)不匹配。 monkey_patch
不仅用于方法,而且这改变了一些函数的解析方式。特别是,Mojo::Util::steady_time
具有空原型(prototype),并且通常在不使用括号的情况下调用。
*{"${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
查找正确方法名称的能力。
关于perl - mro、goto 和 set_subname 如何交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58677042/