Weźmy coś bardzo prostego,
# Foo.pm
package Foo {
my $baz = bar();
sub bar { 42 }; ## Overwrite this
print $baz; ## Before this is executed
}
Czy w każdym razie mogę test.pl
uruchomić kod, który zmienia $baz
ustawienia i powoduje Foo.pm
wydrukowanie czegoś innego na ekranie?
# maybe something here.
use Foo;
# maybe something here
Czy jest możliwe, aby fazy kompilatora wymusiły wydrukowanie powyższego 7
?
perl
compilation
Evan Carroll
źródło
źródło
Foo::bar
, aleuse Foo
uruchomi zarówno fazę kompilacji (przedefiniowanie paska, jeśli coś tam wcześniej zdefiniowano), jak i fazę działania Foo. Jedyne, co mogę wymyślić, to głęboko zhakowany@INC
hak do modyfikacji sposobu ładowania Foo.require
(a zatemuse
) zarówno kompiluje, jak i wykonuje moduł przed zwróceniem. To samo dotyczyeval
.eval
nie można go użyć do kompilacji kodu bez jego wykonania.Odpowiedzi:
Wymagany jest hack, ponieważ
require
(a zatemuse
) zarówno kompiluje, jak i wykonuje moduł przed zwróceniem.To samo dotyczy
eval
.eval
nie można go użyć do kompilacji kodu bez jego wykonania.Najtrudniejszym rozwiązaniem, jakie znalazłem, byłoby zastąpienie
DB::postponed
. Jest to wywoływane przed oceną skompilowanego wymaganego pliku. Niestety jest wywoływany tylko podczas debugowania (perl -d
).Innym rozwiązaniem byłoby odczytanie pliku, zmodyfikowanie go i ocena zmodyfikowanego pliku, podobnie jak to robi:
Powyższe nie ustawia się poprawnie
%INC
, miesza nazwę pliku używaną przez ostrzeżenia i takie, nie wywołujeDB::postponed
itp. Poniżej przedstawiono bardziej niezawodne rozwiązanie:Użyłem
UNITCHECK
(który jest wywoływany po kompilacji, ale przed wykonaniem), ponieważ wstawiłem zastąpienie (używanieunread
) zamiast wczytywania całego pliku i dołączania nowej definicji. Jeśli chcesz zastosować to podejście, możesz uzyskać uchwyt pliku, z którego będziesz mógł powrócićUznanie dla @Grinnz za wzmianki o
@INC
hakach.źródło
Ponieważ jedyne opcje tutaj będą głęboko zhackowane, tak naprawdę chcemy tutaj uruchomić kod po dodaniu podprogramu do
%Foo::
skrytki:źródło
Spowoduje to wygenerowanie niektórych ostrzeżeń, ale wyświetli 7:
Najpierw definiujemy
Foo::bar
. Jego wartość zostanie przedefiniowana przez deklarację w Foo.pm, ale zostanie uruchomione ostrzeżenie „Subroutine Foo :: bar redefined”, które wywoła procedurę obsługi sygnału, która ponownie zdefiniuje podprogram, aby zwrócić 7.źródło
perl -w
.Oto rozwiązanie, które łączy przechwytywanie procesu ładowania modułu z możliwościami tylko do odczytu modułu Readonly:
źródło
Zmieniłem tutaj moje rozwiązanie, aby nie
Readonly.pm
opierało się już na tym , po tym, jak dowiedziałem się, że przegapiłem bardzo prostą alternatywę, opartą na odpowiedzi m-conrada , którą przerobiłem na podejście modułowe, które zacząłem tutaj.Foo.pm ( Taki sam jak w poście otwierającym )
OverrideSubs.pm Zaktualizowano
test-run.pl
Uruchom i wyjdź:
źródło
Jeśli
sub bar
wnętrzeFoo.pm
ma inny prototyp niż istniejącaFoo::bar
funkcja, Perl go nie zastąpi? Wydaje się, że tak jest i sprawia, że rozwiązanie jest dość proste:lub coś w tym rodzaju
Aktualizacja: nie, powodem tego jest to, że Perl nie przedefiniuje podprogramu „stałego” (z prototypem
()
), więc jest to realne rozwiązanie tylko wtedy, gdy twoja próbna funkcja jest stała.źródło
BEGIN { *Foo::bar = sub () { 7 } }
lepiej napisać jakosub Foo::bar() { 7 }
sub bar { 42 } my $baz = bar();
zamiastmy $baz = bar(); sub bar { 42 }
, nie zadziałałoby.Prototype mismatch: sub Foo::bar () vs none at Foo.pm line 5.
iConstant subroutine bar redefined at Foo.pm line 5.
)Zróbmy zawody golfowe!
To tylko poprzedza kod modułu zastąpieniem metody, która będzie pierwszą linią kodu, która działa po fazie kompilacji i przed fazą wykonania.
Następnie wypełnij
%INC
wpis, aby przyszłe ładunkiuse Foo
nie pobierały oryginału.źródło