Mengapa nilai tipe null berubah di dalam loop?

109

Menjalankan cuplikan ini di konsol Chrome:

function foo() {
    return typeof null === 'undefined';
}
for(var i = 0; i < 1000; i++) console.log(foo());

harus mencetak 1000 kali false, tetapi pada beberapa mesin akan mencetak falseuntuk beberapa iterasi, kemudian truesisanya.

masukkan deskripsi gambar di sini

Mengapa ini terjadi? Apakah ini hanya bug?

Agos
sumber
4
Ini mengembalikan 1000 kali benar bagi saya ...
Hoàng Long
2
saya pikir itu bug, saya punya 262 salah / 738 benar
Jax Teller
1
itu sesuatu yang aneh dengan konsol chrome: jika Anda mendorong ke sebuah array dan mencatat array, itu saja false. sebagaimana adanya, jumlah trues berfluktuasi di chrome.
dandavis
1
@ HoàngLong seperti yang saya katakan dalam pertanyaan, ini hanya terjadi pada beberapa mesin. Mungkin juga hal itu terjadi hanya pada beberapa versi Chrome
Agos
2
@ HoàngLong pastikan Anda menjalankannya di Chrome
Nobita

Jawaban:

74

Ada bug kromium yang terbuka untuk ini:

Masalah 604033 - kompiler JIT tidak mempertahankan perilaku metode

Jadi ya, itu hanya bug!

Tidur 86
sumber
5
"Hanya"? Tidak bisakah ini merusak aplikasi web sembarangan di seluruh dunia?
jpmc26
6
"Hanya" hanya untuk mengatakan itu bukanlah fitur atau sesuatu yang aneh. Merupakan bug kritis, tapi hanya bug!
Slumber86
37

Ini sebenarnya adalah mesin JavaScript V8 ( Wiki bug ).

Mesin ini digunakan di Chromium, Maxthron, Android OS, Node.js dll.

Deskripsi bug yang relatif sederhana dapat Anda temukan di topik Reddit ini :

Mesin JavaScript modern mengkompilasi kode JS menjadi kode mesin yang dioptimalkan ketika dijalankan (kompilasi Just In Time) untuk membuatnya berjalan lebih cepat. Namun, langkah pengoptimalan memiliki beberapa biaya performa awal sebagai gantinya untuk percepatan jangka panjang, sehingga mesin secara dinamis memutuskan apakah suatu metode layak dilakukan tergantung pada seberapa umum metode tersebut digunakan.

Dalam kasus ini, tampaknya hanya ada bug di jalur yang dioptimalkan, sedangkan jalur yang tidak dioptimalkan berfungsi dengan baik. Jadi pada awalnya metode ini berfungsi sebagaimana mestinya, tetapi jika dipanggil dalam loop cukup sering pada titik tertentu, mesin akan memutuskan untuk mengoptimalkannya dan menggantinya dengan versi buggy.

Bug ini tampaknya telah diperbaiki di V8 itu sendiri ( commit ), juga di Chromium ( laporan bug ) dan NodeJS ( commit ).

Sergey Novikov
sumber
Saya mengonfirmasi bahwa bug tersebut masih ada di Node.js 6.2.2 yang membuat saya khawatir.
Michael Shopsin
Itu diperbaiki di mesin V8 hari ini (21.06), saya yakin perangkat lunak terkait akan segera diperbarui.
Sergey Novikov
Melakukan backport perbaikan v8 ke Node.js 6.2.x sedang berlangsung karena masalah # 7348 dimiliki oleh TheAlphaNerd .
Michael Shopsin
18

Untuk menjawab pertanyaan langsung mengapa itu berubah, bug tersebut ada dalam rutinitas pengoptimalan "JIT" dari mesin V8 JS yang digunakan oleh Chrome. Pada awalnya, kode dijalankan persis seperti yang tertulis, tetapi semakin sering Anda menjalankannya, semakin besar potensi manfaat pengoptimalan yang melebihi biaya analisis.

Dalam kasus ini, setelah eksekusi berulang dalam loop, kompilator JIT menganalisis fungsi tersebut, dan menggantinya dengan versi yang dioptimalkan. Sayangnya, analisis membuat asumsi yang salah, dan versi yang dioptimalkan sebenarnya tidak memberikan hasil yang benar.

Secara khusus, pengguna Reddit RainHappens menyarankan bahwa ini adalah kesalahan dalam jenis propagasi :

Itu juga melakukan beberapa jenis propagasi (seperti dalam tipe apa variabel dll). Ada tipe khusus "tidak terdeteksi" ketika variabel tidak terdefinisi atau null. Dalam hal ini pengoptimal mengatakan "null tidak terdeteksi, sehingga dapat diganti dengan string" tidak ditentukan "untuk perbandingan.

Ini adalah salah satu masalah sulit dengan pengoptimalan kode: bagaimana menjamin bahwa kode yang telah diatur ulang untuk kinerja akan tetap memiliki efek yang sama seperti aslinya.

IMSoP
sumber