Определить процедуру и заменить ее другой процедурой

Чего я хочу добиться:

###############CODE########
old_procedure(arg1, arg2);
#############CODE_END######

У меня есть огромный код, в котором есть старая процедура. Я хочу, чтобы вызов этого old_procedure переходил к вызову новой процедуры (new_procedure(arg1, arg2)) с теми же аргументами. Теперь я знаю, вопрос кажется довольно глупым, но дело в том, что мне не разрешено менять код или bad_function. Поэтому единственное, что я могу сделать, это создать процедуру извне, которая читает поток кода или что-то еще, а затем, когда он находит bad_function, он заменяет ее на new_function. Они имеют тип void, поэтому не нужно беспокоиться о возвращаемых значениях. Я использую Perl. Если кто-то знает, как минимум начать в этом направлении... пожалуйста, прокомментируйте или ответьте. Было бы неплохо, если бы новый код мог быть написан на Perl или C, но и другие известные языки тоже хороши. C++, Java.

РЕДАКТИРОВАТЬ: код написан в сценарии оболочки и Perl. Я не могу редактировать код, и у меня нет местоположения old_function, я имею в виду, что могу найти его... но это действительно сложно. Так что я могу использовать указанную выше вещь пакета, но если есть способ обойти это... чтобы я мог проанализировать поток с этой функцией и заменить вызовы функций. Пожалуйста, не удаляйте теги, так как мне нужны предложения от экспертов по Java, C++.

РЕДАКТИРОВАТЬ: @mirod Так что я попробовал это, и ваш ответ сделал новую подпрограмму, и теперь нет никакого способа получить доступ к старой. Я создал переменную, которая проверяет значение, чтобы решить, какой путь ( old_sub или new_sub)... есть ли способ добавить переменную в новый код... который отправляет элемент управления обратно в old_function, если он не установлен... лайк:

use BadPackage; # sub is defined there
BEGIN
{ package BapPackage;
  no warnings; # to avoid the "Subroutine bad_sub redefined" message
# check for the variable and send to old_sub if the var is not set
  sub bad_sub
    { # good code
    }
}
# Thanks @mirod

4 ответа

Решение

Это проще сделать в Perl, чем во многих других языках, но это не значит, что это легко, и я не знаю, хотите ли вы это услышать. Вот подтверждение концепции:

Давайте возьмем неработающий код:

# file name: Some/Package.pm
package Some::Package;
use base 'Exporter';
our @EXPORT = qw(forty_two nineteen);
sub forty_two { 19 }
sub nineteen { 19 }
1;

# file name: main.pl
use Some::Package;
print "forty-two plus nineteen is ", forty_two() + nineteen();

Запуск программы perl main.pl производит вывод:

forty-two plus nineteen is 38

Сдается что файлы Some/Package.pm а также main.pl сломаны и неизменны. Как мы можем исправить их поведение?

Один из способов, которым мы можем вставить произвольный код в perl Команда с -M переключатель командной строки. Сделаем ремонтный модуль:

# file: MyRepairs.pm
CHECK {
    no warnings 'redefine';  
    *forty_two = *Some::Package::forty_two = sub { 42 };
};
1;

Сейчас запускаю программу perl -MMyRepairs main.pl производит:

forty-two plus nineteen is 61

Наш ремонтный модуль использует CHECK блок для выполнения кода между фазой компиляции и фазой выполнения. Мы хотим, чтобы наш код был последним, выполняемым во время компиляции, поэтому он будет перезаписывать некоторые функции, которые уже были загружены. -M переключатель командной строки сначала запустит наш код, поэтому CHECK блок задерживает выполнение наших исправлений до тех пор, пока не будет запущен весь другой код времени компиляции. Увидеть perlmod Больше подробностей.

Это решение хрупкое. Он не может ничего делать с модулями, загруженными во время выполнения (с require ... или же eval "use ..." (это общие) или подпрограммы, определенные в других CHECK блоки (это редкость).

Если мы предположим, что скрипт оболочки работает main.pl также является неизменным (то есть мы не можем изменить perl main.pl в perl -MMyRepairs main.pl), затем мы продвигаемся на один уровень вверх и проходим -MMyRepairs в PERL5OPT переменная окружения:

PERL5OPT="-I/path/to/MyRepairs -MMyRepairs" bash the_immutable_script_that_calls_main_pl.sh

Они называются инструментами автоматического рефакторинга и являются общими для других языков. Хотя для Perl вы можете быть в очень плохом состоянии, потому что анализ Perl, чтобы найти все ссылки, будет практически невозможен.

Где определена старая процедура?

Если он определен в пакете, вы можете переключиться на пакет после того, как он был used и переопределить подпункт:

use BadPackage; # sub is defined there
BEGIN
{ package BapPackage;
  no warnings; # to avoid the "Subroutine bad_sub redefined" message
  sub bad_sub
    { # good code
    }
}

Если код находится в том же пакете, но в другом файле (загружается через require), вы можете сделать то же самое, не переключая пакет.

если весь код находится в одном файле, измените его.

Кодовый файл sed -i 's/old_procedure/new_procedure/g

Это то, что вы имеете в виду?

Другие вопросы по тегам