我可以用Perl更改已加载模块中的代码行吗?
当我使用该模块从多个FLV文件中提取元数据或合并多个FLV文件时,我经常收到“标记大小太小”错误,然后该模块将拒绝工作。有人在三年前发布了一个bug报告,但似乎没有解决方案 最近,我发现如果我只是在Tag.pm中注释掉以下代码行,我可以用Perl更改已加载模块中的代码行吗?,perl,module,Perl,Module,当我使用该模块从多个FLV文件中提取元数据或合并多个FLV文件时,我经常收到“标记大小太小”错误,然后该模块将拒绝工作。有人在三年前发布了一个bug报告,但似乎没有解决方案 最近,我发现如果我只是在Tag.pm中注释掉以下代码行,FLV::Info的依赖模块之一,如下所示: =pod if ($datasize < 11) { die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10)
FLV::Info
的依赖模块之一,如下所示:
=pod
if ($datasize < 11)
{
die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10);
}
=cut
这种“从模具到非模具”的方法同样有效
BEGIN {
*CORE::GLOBAL::die = sub { return CORE::die(@_) };
}
use FLV::Info;
{
local *CORE::GLOBAL::die = sub {
return if $_[0] =~ /^Tag size is too small/;
return CORE::die(@_);
};
my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();
}
然而,“重新定义”方法并没有像我预期的那样有效。
我复制并粘贴了原始的FLV::Tag::parse子例程,并按照修改原始Tag.pm文件的方式注释了代码行,如下所示:
use FLV::Info;
no warnings 'redefine';
*FLV::Tag::parse = sub {
...
...
=pod
if ($datasize < 11)
{
die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10);
}
=cut
...
...
};
my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();
好吧,即使复制并粘贴了完全相同的解析子例程,而没有对我的重新定义进行任何修改,我也会收到“未知标记类型”错误,而不是“标记大小太小”
真奇怪
作为参考,“重新评估”和“将模具覆盖到非模具”方法将提供以下信息:
1992 video frames
File name sample.flv
File size 5767831 bytes
Duration about 79.6 seconds
Video 1992 frames
codec AVC
type interframe/keyframe
Audio 1712 packets
format AAC
rate 44100 Hz
size 16 bit
type stereo
Meta 1 event
audiocodecid 10
audiosamplerate 22050
audiosamplesize 16
audiosize 342817
creationdate unknown
datasize 805
duration 79.6
filesize 5767869
framerate 25
height 300
keyframes {
>>> 'filepositions' => [
>>> '780',
>>> '865',
>>> '1324122',
>>> '2348913',
>>> '2978630',
>>> '3479001',
>>> '3973756',
>>> '4476281',
>>> '4997226',
>>> '5391890'
>>> ],
>>> 'times' => [
>>> '0',
>>> '0',
>>> '9.6',
>>> '19.2',
>>> '28.8',
>>> '38.4',
>>> '46.32',
>>> '55.92',
>>> '64.88',
>>> '73.88'
>>> ]
>>> }
lastkeyframetimestamp 73.88
lasttimestamp 79.6
metadatacreator Manitu Group FLV MetaData Injector 2
metadatadate 1281964633858
stereo 1
videocodecid 7
videosize 5424234
width 400
最终更新
我已经弄明白了为什么“重新定义”方法通过启用strict和warnings pragma而失败。感谢@Schwern的提醒:)
首先添加以下代码行(从FLV::Util模块复制),然后重新定义FLV::Tag::parse子例程
Readonly::Hash our %TAG_CLASSES => (
8 => 'FLV::AudioTag',
9 => 'FLV::VideoTag',
18 => 'FLV::MetaTag',
);
简单?不,但是你可以做一些疯狂的事情。这里有一些坏主意 其中一个更明显的方法是将一个被黑客攻击的.pm文件的副本放在你的项目中,这样它就可以在系统版本之前看到 另一种类似的方法是将整个例程剪切粘贴到代码中,并在加载原始例程后注入
use FLV::Tag;
no warnings 'redefine';
*FLV::Tag::parse = sub {
...copy of FLV::Tag::parse with your edits...
};
您可以覆盖die
,使其在看到该消息时不会死亡
BEGIN {
# In order to override die() later, you must override it at compile time.
*CORE::GLOBAL::die = sub { return CORE::die(@_) };
}
{
local *CORE::GLOBAL::die = sub {
return if $_[0] =~ /^Tag size too small/;
return CORE::die(@_);
}
...do your thing...
}
您可以将该子例程的内容转储回Perl中,对代码执行字符串替换,然后重新计算它
use Data::Dump::Streamer;
my $original = FLV::Tag->can("parse");
my $code = Dump($original)->Out;
$code =~ s{\Qif ($datasize < 11)\E}{if( 0 )};
no warnings 'redefine';
*FLV::Tag::parse = eval $code;
使用数据::转储::拖缆;
my$original=FLV::Tag->can(“解析”);
my$code=转储($original)->输出;
$code=~s{\Qif($datasize<11)\E}{if(0)};
没有“重新定义”的警告;
*FLV::Tag::parse=eval$code;
或者您可以遍历该子例程的操作码树并更改条件,我将其作为练习留给手头有更多时间的人
这些都是坏主意。你最好只是修改代码,然后再次联系作者,让他们知道有新的信息。好的,Schwern的回答非常彻底,但有一种方法不会那么“坏” 使用他的第二种方法(将整个例程剪切并粘贴到代码中,并在加载原始代码后注入它)。。。但是将其置于特定版本的
FLV::Info
(或FLV::Tag
)上
这样,您仍然有您的monkey补丁(从技术上来说,这是一个monkey补丁吗?),但您删除了这种方法被认为是“不好”的最大原因之一,即,对模块的任何升级都可能与您的自定义补丁子例程冲突。如果您通过版本检查来保护覆盖,您就消除了这种担忧。这是一个很好的答案,并附有一条很好的免责声明:)@Schwern,谢谢您与我分享这些“坏主意”!非常有帮助:)答案应该放在书签中,以便下次讨论Perl的“动态”属性的确切含义。@Schwern,我已经更新了我的文章,提供了更多细节。奇怪的是,如果我用与原始解析子例程完全相同的副本重新定义解析子例程,我将收到不同的错误消息,“未知标记类型”错误而不是“标记太小”错误。@Schwern,我已更新了我的帖子。问题解决了。谢谢你的提示:)这很有帮助!在掌握Perl时,我将深入介绍其中的一些内容。
BEGIN {
# In order to override die() later, you must override it at compile time.
*CORE::GLOBAL::die = sub { return CORE::die(@_) };
}
{
local *CORE::GLOBAL::die = sub {
return if $_[0] =~ /^Tag size too small/;
return CORE::die(@_);
}
...do your thing...
}
use Data::Dump::Streamer;
my $original = FLV::Tag->can("parse");
my $code = Dump($original)->Out;
$code =~ s{\Qif ($datasize < 11)\E}{if( 0 )};
no warnings 'redefine';
*FLV::Tag::parse = eval $code;