
Halo, Habr! Nama saya Dima, saya seorang pengembang di tim "Arsitektur" di hh.ru. Di antara hal-hal lain, saya membuat segalanya lebih mudah bagi rekan kerja. Menjalankan kode dalam produksi adalah tugas yang khas. Karena itu, ketika saya mendengar ada masalah dengan ini, saya memutuskan untuk memperbaikinya.
Tidak selalu perubahan data dapat dibuat UPDATE / INSERT sederhana - kadang-kadang Anda perlu menggunakan validasi, bus acara, dan banyak lagi. Dalam kasus seperti itu, solusi paling optimal adalah dengan mengeksekusi kode arbitrer langsung dalam aplikasi. Kami memiliki Java, jadi ketika
JEP-222 muncul , saya langsung berpikir bahwa JShell mungkin dapat membuat scripting nyaman lagi. Sebuah mukjizat tidak terjadi, dan oleh karena itu di bawah potongan Anda akan menemukan perbandingan yang tidak terlalu mendalam dari penafsir Jawa yang paling terkenal (dan "dekat-Jawa") untuk jvm dengan contoh-contoh. Saya mengundang semua orang yang tertarik pada kucing.
Kami menggunakan
BeanShell untuk menjalankan skrip, dan untuk 2019 itu mengerikan: rilis terakhir dari
2016 , kurangnya dukungan untuk lambdas dan bahkan obat generik - semua ini memaksa kami untuk menulis kode yang tidak ada yang menulis sejak Java
1.4 .
Kriteria
Sebelum memulai perbandingan, kami merumuskan persyaratan untuk mesin skrip bawaan. Setelah menggaruk kepala, saya membuat daftar berikut:
- dukungan untuk sintaks java saat ini;
- kemampuan untuk menyampaikan konteks eksternal kepada juru bahasa;
- kemampuan untuk mengganggu eksekusi;
- kemampuan untuk mengarahkan ulang I / O;
- umpan balik informatif.
Semakin banyak bahasa di mana kita menulis skrip menyerupai bahasa yang kita kembangkan, semakin sedikit kesalahan - tangan ingat. Tetapi ketika kita membuat kesalahan yang diidentifikasi pada tahap kompilasi, mereka harus memperbolehkan pengembang untuk memperbaikinya - ini adalah indikasi nama-nama variabel yang hilang, garis, stacktracks dll.
Selanjutnya, skrip harus bekerja dalam konteks tertentu, dengan akses ke konteks Spring, ke logger yang akan melayani skrip. Tanpa kesempatan untuk mentransfer konteks, tanda terima berubah menjadi sebuah pencarian.
Jika kesalahan tetap bocor ke runtime, kemudian memulai ulang seluruh instance untuk menghentikan eksekusi adalah ide yang buruk, jadi Anda hanya perlu dapat cukup mengganggu eksekusi skrip kapan saja.
Dan yang terakhir - semua pesan ke output sistem selama skrip bekerja hanya masuk akal dalam konteks skrip ini. Dalam log sistem, kesimpulan seperti itu tidak banyak berguna. Oleh karena itu, saya ingin dapat mengarahkan pesan-pesan ini sebagai tanggapan.
Jadi ayo pergi
- dukungan untuk sintaks java saat ini - ya
- kemampuan untuk menyampaikan konteks - tidak
- kemampuan untuk mengganggu eksekusi - ya
- kemampuan untuk mengarahkan ulang I / O - no
- umpan balik informatif - ya
Saya
harus segera mengatakan bahwa
JEP-222 tidak bertujuan untuk membuat juru bahasa yang disematkan - tujuannya adalah
REPL , yaitu, kemampuan untuk dengan cepat membuat prototipe kode. Ini penuh dengan sejumlah konsekuensi.
Pertama, hidup tidak mempersiapkan kompiler Java untuk fakta bahwa Anda dapat mendeklarasikan metode di luar kelas, dan menggunakan variabel yang belum dideklarasikan di tubuh metode. Oleh karena itu, eksekusi itu sendiri tersembunyi di balik lapisan abstraksi yang mengesankan.
Kedua, REPL mungkin dijalankan tidak secara lokal, tetapi di suatu tempat pada mesin jarak jauh, sehingga API dibuat dengan fitur-fitur seperti itu dalam pikiran. Saya pikir ini adalah alasan utama mengapa API tidak memiliki kemampuan untuk mengirimkan konteks eksternal ke penerjemah dan mengarahkan I / O.
Selain itu, ada berbagai mode peluncuran -
jarak jauh , ketika shell terhubung ke mesin melalui JDI, dan
lokal . Karena tidak ada kemungkinan untuk menyampaikan konteks secara terprogram, tetapi kami masih sangat ingin, harapan tetap hanya dalam mode lokal dan bahwa kita dapat menggunakan
pembuatan kodeTapi, sayangnya, mode lokal jelas tidak dikonsepsikan sebagai yang utama -
skrip semacam ini menyebut
kebuntuan pada kompiler. Terlepas dari kenyataan bahwa kode yang sama dalam mode JDI bekerja tanpa masalah.
Jadi, saya harus meninggalkan penggunaan JShell, meskipun secara umum API itu aneh, tetapi dapat dimengerti - kami memberikan script ke input, kami mendapatkan aliran acara, untuk masing-masing dari kami dapat memeriksa status, mendapatkan kesalahan dan informasi tagihan. Kesalahan memungkinkan kami mengidentifikasi ekspresi yang diizinkan:
PEMBARUAN: Berkumpul ScriptEngine dari JShell . Tetapi untuk ini saya harus menulis
mode peluncuran saya
- dukungan untuk sintaks java saat ini - no
- kemampuan untuk menyampaikan konteks - ya
- kemampuan untuk mengganggu eksekusi - ya
- kemampuan untuk mengalihkan I / O - ya (tetapi membutuhkan penggunaan metode khusus)
- umpan balik informatif - ya
Kegagalan membuat kami memperhatikan apa yang kami gunakan sekarang. Setelah istirahat panjang, proyek ini tampak hidup kembali, dan dilihat dari
roadmapnya , proyek ini dengan percaya diri bergerak menuju rilis yang akan menyelesaikan semua masalah kita - banyak fitur yang seharusnya bekerja sekarang.
Pada saat penulisan ini, beanhell memang mendukung obat generik, tetapi lambda masih tidak berfungsi. Mungkin dengan keluarnya situasi situasinya akan berubah.
Namun dalam hal integrasi, mesinnya cukup ramah - dukungan untuk javax.scripting standar, kesalahan runtime cukup jelas:

Namun, menggunakan aliran bebas lambda adalah neraka yang membakar di neraka. Bahkan mungkin lebih mudah untuk menulis dalam bahasa lain. Jadi saya memutuskan untuk melihat lebih dekat pada segmen "near-java". Dan kandidat pertama untuk peran juru bahasa skrip di sini, tentu saja
- dukungan untuk sintaks java saat ini - no
- kemampuan untuk menyampaikan konteks - tidak
- kemampuan untuk mengganggu eksekusi - ya
- kemampuan untuk mengarahkan ulang I / O - no
- umpan balik informatif - ya
Kode Java, dengan banyak keberuntungan, akan menjadi kode kotlin yang valid. Tapi saya tidak berhasil meluncurkan apa pun yang setidaknya cukup memadai di java di kotlin, tapi tetap saja mari kita coba.
Kotlin telah
mengumumkan dukungan skrip javax untuk beberapa tahun.
Masalah pertama yang harus kita tangani adalah ketergantungan-neraka.
Kotlin-compiler menyertakan kelas org.jdom yang mulai berkelahi dengan org.jdom dalam aplikasi dan membungkusnya ... Jadi, kami memiliki kotlin-compiler-embeddable, di mana semua kelas ini dimasukkan ke dalam paket khusus.
Namun, setelah konfigurasi, ternyata transfer konteks eksternal tidak berfungsi. Dan sekarang ini adalah masalah serius, sampai solusinya tidak masuk akal untuk menggali lebih dalam. Jika Anda tahu apa masalahnya dan bagaimana cara memperbaikinya - tulis di komentar.
Kesalahan juga cukup bertele-tele:

- dukungan untuk sintaks java saat ini - tidak, tetapi ada analog
- kemampuan untuk menyampaikan konteks - ya
- kemampuan untuk mengganggu eksekusi - ya
- kemampuan untuk mengalihkan I / O - ya
- umpan balik informatif - ya
Alur, selain mendukung javax.scripting, menyediakan APInya sendiri yang lebih canggih untuk integrasi juru bahasa. Misalnya, dimungkinkan untuk melewati transformasi AST, yang memungkinkan Anda untuk menambahkan
interupsi bersyarat setelah setiap ekspresi . Masalahnya sangat kuat sehingga sudah
menakutkan .
Selain itu, kode Java (dan terutama beanhell) bisa menjadi kode alur yang cukup valid.
Integrasi dan uji operasi berhasil, dengan pengecualian inisialisasi sheet dan sintaks lambda (harus dibungkus dengan kurung kurawal), skrip binshell yang ada bekerja tanpa masalah. Kesalahan lebih dari sekadar bertele-tele:

Mungkin hari ini satu-satunya penerjemah yang, di satu sisi, memungkinkan Anda untuk menulis kode untuk sampel pada tahun 2019, dan di sisi lain, memenuhi semua persyaratan yang masuk akal untuk disampaikan kepada penerjemah.
Kesimpulan apa yang bisa kita tarik?
Pertama dan terpenting: REPL bukan mesin scripting. Ini mungkin tampak seperti satu langkah dari baris perintah ke integrasi ke dalam aplikasi, tetapi setelah diperiksa lebih dekat ternyata alat-alat ini memiliki tugas yang berbeda, kadang-kadang saling bertentangan.
Yang kedua - hari ini tidak ada alat tunggal untuk mengeksekusi skrip di Jawa, jadi jika Anda memerlukan alat seperti itu, bersiaplah untuk mempelajari sintaks baru.