Pertanyaan Mengapa mengeksekusi kode Java dalam komentar dengan karakter Unicode tertentu diperbolehkan?


Kode berikut menghasilkan output "Hello World!" (tidak juga, coba saja).

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

Alasan untuk ini adalah bahwa compiler Java mem-parsing karakter Unicode \u000d sebagai garis baru dan diubah menjadi:

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

Sehingga menghasilkan komentar yang "dieksekusi".

Karena ini dapat digunakan untuk "menyembunyikan" kode berbahaya atau apa pun yang dapat dibuat oleh programmer jahat, mengapa diperbolehkan di komentar?

Mengapa ini diizinkan oleh spesifikasi Java?


1247
2018-06-09 09:02


asal


Jawaban:


Unicode decoding terjadi sebelum terjemahan leksikal lainnya. Manfaat utama dari ini adalah bahwa hal itu membuat sepele untuk bolak-balik antara ASCII dan pengkodean lainnya. Anda bahkan tidak perlu mencari tahu di mana komentar dimulai dan berakhir!

Sebagaimana dinyatakan dalam Bagian JLS 3.3 ini memungkinkan alat berbasis ASCII untuk memproses file sumber:

[...] Bahasa pemrograman Java menentukan cara standar mengubah program yang ditulis dalam Unicode ke ASCII yang mengubah program menjadi bentuk yang dapat diproses oleh alat berbasis ASCII. [...]

Ini memberikan jaminan mendasar bagi independensi platform (independensi set karakter yang didukung) yang selalu menjadi tujuan utama untuk platform Java.

Mampu menulis karakter Unicode di mana saja dalam file adalah fitur yang rapi, dan terutama penting dalam komentar, ketika mendokumentasikan kode dalam bahasa non-latin. Fakta bahwa itu dapat mengganggu semantik dengan cara halus seperti itu hanyalah efek samping (yang tidak menguntungkan).

Ada banyak gotchas pada tema ini dan Java Puzzlers oleh Joshua Bloch dan Neal Gafter termasuk varian berikut:

Apakah ini program Java legal? Jika ya, apa yang dicetak?

\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d

(Program ini ternyata adalah program "Hello World" biasa.)

Dalam solusi untuk yang kusut, mereka menunjukkan yang berikut:

Lebih serius lagi, teka-teki ini berfungsi untuk memperkuat pelajaran dari tiga sebelumnya: Pelolosan Unicode sangat penting ketika Anda perlu memasukkan karakter yang tidak dapat diwakili dengan cara lain ke dalam program Anda. Hindari mereka dalam semua kasus lain.


Sumber: Java: Melaksanakan kode dalam komentar ?!


687
2018-06-09 09:13



Karena ini belum diatasi, berikut penjelasannya, mengapa terjemahan pelarian Unicode terjadi sebelum pemrosesan kode sumber lain:

Gagasan di balik itu adalah bahwa hal itu memungkinkan penerjemahan kode sumber Java yang tidak sama antara pengkodean karakter yang berbeda. Saat ini, ada dukungan Unicode yang tersebar luas, dan ini tidak terlihat seperti masalah, tetapi pada waktu itu tidak mudah bagi pengembang dari negara barat untuk menerima beberapa kode sumber dari rekannya di Asia yang berisi karakter Asia, membuat beberapa perubahan ( termasuk menyusun dan mengujinya) dan mengirim hasilnya kembali, semua tanpa merusak sesuatu.

Jadi, kode sumber Java dapat ditulis dalam pengkodean apa saja dan memungkinkan berbagai karakter dalam pengenal, karakter dan Stringliteral dan komentar. Kemudian, untuk mentransfernya tanpa kehilangan, semua karakter yang tidak didukung oleh pengkodean target digantikan oleh pelarian Unicode mereka.

Ini adalah proses yang dapat dibalik dan poin yang menarik adalah terjemahan dapat dilakukan oleh alat yang tidak perlu mengetahui apa pun tentang sintaks kode sumber Java karena aturan terjemahan tidak bergantung padanya. Ini berfungsi sebagai terjemahan ke karakter Unicode aktual mereka di dalam kompiler yang terjadi secara terpisah ke sintaks kode sumber Java juga. Ini menyiratkan bahwa Anda dapat melakukan sejumlah langkah penerjemahan secara acak di kedua arah tanpa pernah mengubah arti dari kode sumber.

Ini adalah alasan untuk fitur aneh lain yang bahkan belum disebutkan: \uuuuuuxxxx sintaksis:

Saat alat terjemahan melepaskan karakter dan menemukan urutan yang sudah merupakan urutan yang lolos, itu harus memasukkan tambahan u ke dalam urutan, konversi \ucafe untuk \uucafe. Maknanya tidak berubah, tetapi ketika mengonversi ke arah lain, alat itu hanya harus menghapusnya u dan ganti hanya urutan yang berisi satu u oleh karakter Unicode mereka. Dengan cara itu, bahkan pelarian Unicode dipertahankan dalam bentuk aslinya ketika mengkonversi bolak-balik. Saya kira, tidak ada yang pernah menggunakan fitur itu ...


132
2018-06-09 17:59



Saya akan benar-benar tidak efektif dalam menambahkan poin, hanya karena saya tidak dapat menahan diri dan saya belum melihatnya, bahwa pertanyaan tersebut tidak valid karena mengandung premis tersembunyi yang salah, yaitu bahwa kode tersebut ada di dalamnya. komentar!

Di kode sumber Java \ u000d setara dalam setiap cara untuk karakter ASCII CR. Ini adalah akhiran garis, sederhana dan sederhana, di mana pun itu terjadi. Pemformatan dalam pertanyaan itu menyesatkan, apa yang benar-benar disamakan dengan karakter secara sintaksis adalah:

public static void main(String... args) {
   // The comment below is no typo. 
   // 
 System.out.println("Hello World!");
}

Oleh karena itu, jawaban IMHO yang paling benar adalah: kode dijalankan karena tidak dalam komentar; ada di baris berikutnya. "Melaksanakan kode dalam komentar" tidak diizinkan di Java, seperti yang Anda harapkan.

Sebagian besar kebingungan berasal dari fakta bahwa penyorot sintaks dan IDE tidak cukup canggih untuk memperhitungkan situasi ini. Mereka sama sekali tidak memproses unicode escapes, atau mereka melakukannya setelah mem-parsing kode daripada sebelumnya, seperti javac tidak.


97
2018-06-10 17:37



Itu \u000d melarikan diri mengakhiri komentar karena \u escapes secara seragam dikonversi ke karakter Unicode yang sesuai sebelum program ini diberi token. Anda juga bisa menggunakan \u0057\u0057 dari pada // untuk mulai komentar.

Ini adalah bug di IDE Anda, yang harus syntax-highlight garis untuk membuatnya jelas bahwa \u000d mengakhiri komentar.

Ini juga merupakan kesalahan desain dalam bahasa. Tidak dapat diperbaiki sekarang, karena itu akan merusak program yang bergantung padanya. \u escapes baik harus dikonversi ke karakter Unicode yang sesuai oleh kompiler hanya dalam konteks di mana itu "masuk akal" (string literal dan pengidentifikasi, dan mungkin tempat lain) atau mereka seharusnya dilarang untuk menghasilkan karakter dalam kisaran U + 0000-007F , atau keduanya. Salah satu dari semantik itu akan mencegah komentar itu dihentikan oleh \u000dmelarikan diri, tanpa mengganggu kasus-kasus di mana \u escapes berguna — perhatikan itu termasuk penggunaan \u lolos dalam komentar sebagai cara untuk mengkodekan komentar dalam skrip non-Latin, karena editor teks dapat mengambil pandangan yang lebih luas tentang di mana \u escapes signifikan daripada compiler. (Saya tidak mengetahui adanya editor atau IDE yang akan ditampilkan \u keluar sebagai karakter yang sesuai di apa saja konteks, meskipun.)

Ada kesalahan desain serupa di keluarga C,1 tempat backslash-newline diproses sebelum batas komentar ditentukan, jadi mis.

// this is a comment \
   this is still in the comment!

Saya mengemukakan hal ini untuk mengilustrasikan bahwa memang mudah untuk membuat kesalahan desain khusus ini, dan tidak menyadari bahwa ini adalah kesalahan sampai terlambat untuk memperbaikinya, jika Anda terbiasa memikirkan tentang tokenisasi dan menguraikan cara pemrogram kompiler berpikir tentang tokenization dan parsing. Pada dasarnya, jika Anda telah mendefinisikan tata bahasa formal Anda dan kemudian seseorang muncul dengan kasus khusus sintaksis - trigraphs, backslash-newline, encoding karakter Unicode sewenang-wenang dalam file sumber terbatas pada ASCII, apa pun - yang perlu dijepit, lebih mudah untuk tambahkan pass transformasi sebelum tokenizer daripada untuk mendefinisikan ulang tokenizer untuk memperhatikan di mana masuk akal untuk menggunakan kasus khusus itu.

1 Untuk pedants: Saya sadar bahwa aspek C ini 100% disengaja, dengan dasar pemikiran - saya tidak mengada-ada - bahwa itu akan memungkinkan Anda untuk secara mekanis memaksakan kode dengan garis-garis panjang yang sewenang-wenang ke kartu berlubang. Itu masih keputusan desain yang salah.


63
2018-06-09 15:16



Ini adalah pilihan desain yang disengaja yang berjalan kembali ke desain asli Java.

Kepada orang-orang yang bertanya "siapa yang ingin Unicode lolos dalam komentar?", Saya menganggap mereka adalah orang-orang yang bahasa ibunya menggunakan karakter Latin. Dengan kata lain, itu melekat dalam desain asli Java yang orang bisa menggunakan karakter Unicode sewenang-wenang dimanapun legal dalam program Java, paling biasanya dalam komentar dan string.

Ini bisa dibilang kekurangan dalam program (seperti IDE) yang digunakan untuk melihat teks sumber bahwa program-program tersebut tidak dapat menafsirkan Unicode lolos dan menampilkan mesin terbang yang sesuai.


21
2018-06-09 18:45



Saya setuju dengan @zwol bahwa ini adalah kesalahan desain; tapi aku bahkan lebih kritis.

\u melarikan diri berguna dalam string dan literal char; dan itu satu-satunya tempat yang seharusnya ada. Ini harus ditangani dengan cara yang sama seperti pelarian lain seperti \n; dan "\u000A"  harus berarti persis "\n".

Sama sekali tidak ada gunanya \uxxxx dalam komentar - tidak ada yang bisa membaca itu.

Demikian pula, tidak ada gunanya menggunakan \uxxxx di bagian lain dari program. Satu-satunya pengecualian mungkin di API publik yang dipaksa untuk memuat beberapa karakter non-ascii - apa yang terakhir kali kita lihat?

Para perancang memiliki alasan mereka pada tahun 1995, tetapi 20 tahun kemudian, ini tampaknya menjadi pilihan yang salah.

(pertanyaan untuk pembaca - mengapa pertanyaan ini terus mendapatkan suara baru? apakah pertanyaan ini terkait dari suatu tempat yang populer?)


21
2018-06-09 16:47



Satu-satunya orang yang dapat menjawab mengapa pelarian Unicode diimplementasikan karena mereka adalah orang-orang yang menulis spesifikasi.

Alasan yang masuk akal untuk ini adalah bahwa ada keinginan untuk mengizinkan seluruh BMP sebagai karakter yang mungkin dari kode sumber Java. Ini menyajikan masalah meskipun:

  • Anda ingin dapat menggunakan karakter BMP apa pun.
  • Anda ingin dapat memasukkan karater BMP apa pun dengan cukup mudah. Cara untuk melakukan ini adalah dengan pelarian Unicode.
  • Anda ingin menjaga spesifikasi leksikal mudah bagi manusia untuk membaca dan menulis, dan cukup mudah untuk diterapkan juga.

Ini sangat sulit ketika Unicode lolos memasuki keributan: itu menciptakan seluruh beban aturan lexer baru.

Cara mudahnya adalah melakukan lexing dalam dua langkah: pertama cari dan ganti semua Unicode lolos dengan karakter yang diwakilinya, lalu parse dokumen yang dihasilkan seolah-olah Unicode lolos tidak ada.

Keuntungannya adalah mudah untuk ditentukan, sehingga membuat spesifikasi lebih sederhana, dan mudah diterapkan.

Kelemahannya adalah, ya, teladan Anda.


11
2018-06-12 11:59



Compiler tidak hanya menerjemahkan Unicode lolos ke karakter yang mereka wakili sebelum mem-parsing program menjadi token, tetapi ia melakukannya sebelum membuang komentar dan ruang putih.

Program ini berisi pelarian Unicode tunggal (\ u000d), yang terletak di satu-satunya komentarnya. Seperti komentar memberitahu Anda, pelarian ini mewakili karakter linefeed, dan compiler sepatutnya menerjemahkannya sebelum membuang komentar.

Ini platform-dependent. Pada platform tertentu, seperti UNIX, ia akan berfungsi; pada yang lain, seperti Windows, tidak akan. Meskipun output mungkin terlihat sama dengan mata telanjang, itu bisa dengan mudah menimbulkan masalah jika disimpan dalam file atau disalurkan ke program lain untuk diproses selanjutnya.


1
2017-11-02 13:01