关于mro.pm
,以及与set_subname
和goto
的相互作用,这是一个复杂的问题>
[解决问题时,我认为我的误解的核心与mro.pm
的工作方式有关,尤其是与set_subname
有关。
这三种结构之间有什么区别,
对set_subname
的普通电话
*Foo::bar = set_subname( 'Foo::bar', $codeRef );
包装set_subname
的Anon子>
*Foo::bar = sub {
my $codeRef2 = set_subname('Foo::bar', $codeRef);
goto $codeRef2
};
其名称设置为set_subname
的Anon子
*Foo::bar = set_subname(
'Foo::bar',
sub { goto $codeRef }
);
特别是,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
我也可以确认设置原型可以解决此问题。