Mari kita ambil sesuatu yang sangat sederhana,
# Foo.pm
package Foo {
my $baz = bar();
sub bar { 42 }; ## Overwrite this
print $baz; ## Before this is executed
}
Apakah ada yang saya dapat dari test.pl
menjalankan kode yang mengubah apa $baz
yang diatur dan menyebabkan Foo.pm
untuk mencetak sesuatu yang lain ke layar?
# maybe something here.
use Foo;
# maybe something here
Apakah mungkin dengan fase kompiler untuk memaksa yang di atas untuk mencetak 7
?
perl
compilation
Evan Carroll
sumber
sumber
Foo::bar
, tetapiuse Foo
akan menjalankan fase kompilasi (mendefinisikan ulang bar jika ada yang sebelumnya didefinisikan di sana) dan fase runtime dari Foo. Satu-satunya hal yang dapat saya pikirkan adalah@INC
kait yang sangat dalam untuk memodifikasi bagaimana Foo dimuat.require
(dan karenanyause
) mengkompilasi dan mengeksekusi modul sebelum kembali. Sama berlaku untukeval
.eval
tidak dapat digunakan untuk mengkompilasi kode tanpa juga menjalankannya.Jawaban:
Peretasan diperlukan karena
require
(dan karenanyause
) mengkompilasi dan mengeksekusi modul sebelum kembali.Sama berlaku untuk
eval
.eval
tidak dapat digunakan untuk mengkompilasi kode tanpa juga menjalankannya.Solusi paling tidak mengganggu yang saya temukan adalah menimpanya
DB::postponed
. Ini disebut sebelum mengevaluasi file yang diperlukan dikompilasi. Sayangnya, ini hanya dipanggil saat debugging (perl -d
).Solusi lain adalah dengan membaca file, memodifikasinya, dan mengevaluasi file yang dimodifikasi, seperti yang berikut ini:
Di atas tidak diatur dengan benar
%INC
, itu mengacaukan nama file yang digunakan oleh peringatan dan semacamnya, itu tidak memanggilDB::postponed
, dll. Berikut ini adalah solusi yang lebih kuat:Saya menggunakan
UNITCHECK
(yang dipanggil setelah kompilasi tetapi sebelum eksekusi) karena saya mendahulukan override (menggunakanunread
) daripada membaca di seluruh file dan menambahkan definisi baru. Jika Anda ingin menggunakan pendekatan itu, Anda bisa mendapatkan pegangan file untuk kembali menggunakanKudos to @ Grinnz karena menyebutkan
@INC
kait.sumber
Karena satu-satunya opsi di sini akan sangat terjal, apa yang sebenarnya kita inginkan di sini adalah menjalankan kode setelah subrutin ditambahkan ke
%Foo::
simpanan:sumber
Ini akan memancarkan beberapa peringatan, tetapi mencetak 7:
Pertama, kami mendefinisikan
Foo::bar
. Nilainya akan didefinisikan ulang oleh deklarasi di Foo.pm, tetapi peringatan "Subroutine Foo :: bar redefined" akan dipicu, yang akan memanggil pengendali sinyal yang mendefinisikan kembali subrutin untuk mengembalikan 7.sumber
perl -w
.Berikut ini adalah solusi yang menggabungkan mengaitkan proses pemuatan modul dengan kemampuan membuat-baca dari modul Readonly:
sumber
Saya telah merevisi solusi saya di sini, sehingga tidak lagi bergantung pada
Readonly.pm
, setelah mengetahui bahwa saya telah melewatkan alternatif yang sangat sederhana, berdasarkan jawaban m-conrad , yang telah saya ulang ke pendekatan modular yang saya mulai di sini.Foo.pm ( Sama seperti pada posting pembuka )
OverrideSubs.pm Diperbarui
test-run.pl
Jalankan dan hasilkan:
sumber
Jika bagian
sub bar
dalamFoo.pm
memiliki prototipe yang berbeda dariFoo::bar
fungsi yang ada , Perl tidak akan menimpanya? Sepertinya itulah masalahnya, dan membuat solusinya cukup sederhana:atau sejenisnya
Pembaruan: tidak, alasan ini berhasil adalah Perl tidak akan mendefinisikan ulang subrutin "konstan" (dengan prototipe
()
), jadi ini hanya solusi yang layak jika fungsi tiruan Anda konstan.sumber
BEGIN { *Foo::bar = sub () { 7 } }
lebih baik ditulis sebagaisub Foo::bar() { 7 }
sub bar { 42 } my $baz = bar();
bukanmy $baz = bar(); sub bar { 42 }
, itu tidak akan berhasil.Prototype mismatch: sub Foo::bar () vs none at Foo.pm line 5.
danConstant subroutine bar redefined at Foo.pm line 5.
)Ayo ikuti kontes Golf!
Ini hanya mengawali kode modul dengan penggantian metode, yang akan menjadi baris kode pertama yang berjalan setelah fase kompilasi dan sebelum fase eksekusi.
Kemudian, isi
%INC
entri tersebut agar nantinyause Foo
tidak ada yang menarik yang asli.sumber