Fabrice Bellard merilis MicroQuickJS
(github.com/bellard)- MicroQuickJS (MQuickJS) adalah engine JavaScript ultra-ringan yang dirancang untuk sistem embedded, dan dapat berjalan hanya dengan sekitar 10kB RAM dan 100kB ROM
- Untuk mempertahankan kecepatan yang mirip dengan QuickJS sambil mengurangi penggunaan memori, engine ini mengadopsi tracing garbage collector dan penyimpanan string UTF-8
- Bahasa yang didukung adalah subhimpunan JavaScript terbatas yang mendekati ES5, dan hanya mengizinkan strict mode yang melarang sintaks yang rawan kesalahan
- Melalui alat REPL
mqjs, pengguna dapat menjalankan skrip, menyimpan bytecode, dan menetapkan batas memori; bytecode yang dihasilkan juga dapat dijalankan langsung dari ROM - Seluruh engine dan standard library tinggal di ROM untuk mewujudkan inisialisasi cepat dan konsumsi memori rendah, sehingga meningkatkan efisiensi eksekusi JavaScript di lingkungan embedded
Pengenalan
- MicroQuickJS (MQuickJS) adalah engine JavaScript yang menargetkan sistem embedded, dan berjalan dengan 10kB RAM serta 100kB ROM (termasuk kode ARM Thumb-2)
- Kecepatannya mirip dengan QuickJS
- Hanya mendukung subhimpunan yang mendekati ES5, dan hanya berjalan dalam strict mode yang melarang sintaks yang tidak efisien atau rawan kesalahan
- Meskipun berbagi sebagian kode dengan QuickJS, struktur internalnya dirancang sepenuhnya berbeda untuk menghemat memori
- Menggunakan tracing garbage collector, tanpa memakai stack CPU, dan penyimpanan string UTF-8
REPL
- Perintah REPL adalah
mqjs, dan mendukung eksekusi skrip, evaluasi, mode interaktif, pengaturan batas memori, serta penyimpanan bytecode- Contoh:
./mqjs --memory-limit 10k tests/mandelbrot.js
- Contoh:
- Dengan opsi
-o, bytecode hasil kompilasi dapat disimpan ke file- Bytecode yang disimpan dapat dijalankan dengan
./mqjs mandelbrot.bin
- Bytecode yang disimpan dapat dijalankan dengan
- Bytecode berbeda tergantung endianness dan panjang word CPU (32/64-bit), dan opsi
-m32dapat digunakan untuk membuat bytecode 32-bit - Dengan opsi
--no-column, nomor kolom pada informasi debug dapat dihapus
Strict mode
- Hanya strict mode yang diizinkan; penggunaan kata kunci
withtidak didukung, dan variabel global wajib dideklarasikan denganvar - Hole pada array tidak diizinkan
- Contoh:
a[10] = 2akan menimbulkan TypeError - Jika membutuhkan array dengan hole, gunakan objek biasa
- Contoh:
- Hanya global eval yang didukung, tanpa akses ke variabel lokal
- Value boxing tidak didukung (
new Number(1)dan sejenisnya)
Subhimpunan JavaScript
- Berbasis strict mode, dengan fokus pada kompatibilitas ES5
- Objek Array tidak memiliki hole, dan akses indeks di luar rentang akan menghasilkan error
for inhanya mengiterasi properti milik objek itu sendiri, sedangkanfor ofhanya didukung untuk array- Global object ada, tetapi getter/setter tidak didukung, dan properti yang dibuat langsung tidak diekspos sebagai variabel global
- Regular expression (Regexp) hanya membedakan huruf besar/kecil untuk ASCII, dan
/./mencocokkan berdasarkan code point Unicode, bukan UTF-16 - Fungsi string hanya memproses ASCII (
toLowerCase,toUpperCase) - Date hanya mendukung
Date.now() - Fitur tambahan yang didukung:
for of, Typed arrays, literal string\u{hex}- Fungsi Math:
imul,clz32,fround,trunc,log2,log10 - operator eksponen, flag Regexp (s, y, u), fungsi string (
replaceAll,trimStart,trimEnd), globalThis
C API
- Ketergantungan pada library C diminimalkan, tanpa penggunaan
malloc,free, atauprintf - Buffer memori harus disediakan secara langsung, dan engine hanya mengalokasikan memori di dalam buffer tersebut
- Contoh:
ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib)
- Contoh:
- Karena mekanisme garbage collection yang digunakan, pemanggilan
JS_FreeValue()tidak diperlukan - Karena alamat objek dapat berpindah setiap kali alokasi, penggunaan pointer
JSValuedirekomendasikanJS_PushGCRef()/JS_PopGCRef()untuk manajemen referensi yang aman
- Standard library dikompilasi sebagai struktur C yang dapat disimpan di ROM, sehingga mencapai inisialisasi cepat dan penggunaan RAM rendah
- Eksekusi bytecode dapat dilakukan dari ROM, lalu direlokasi dengan
JS_RelocateBytecode()dan dijalankan denganJS_LoadBytecode()sertaJS_Run() - Library matematika (
libm.c) dan emulator floating-point disertakan
Struktur internal dan perbandingan dengan QuickJS
- Garbage collection: menggunakan tracing GC terkompresi alih-alih reference counting
- Mencegah fragmentasi memori dan mengecilkan ukuran objek
- Representasi nilai: dirancang sesuai ukuran word CPU (32/64-bit)
- Dapat menyimpan integer 31-bit, code point Unicode, floating-point, dan pointer blok memori
- String disimpan dalam UTF-8, lebih efisien dibanding pendekatan array 8/16-bit milik QuickJS
- Fungsi C dapat disimpan sebagai nilai tunggal, tetapi tidak bisa ditambahi properti
- Standard library tinggal di ROM, dan objek RAM diminimalkan untuk memungkinkan inisialisasi engine yang cepat
- Bytecode berbasis stack, dan diperlakukan read-only melalui tabel referensi tidak langsung
- Nomor baris dan kolom dikompresi dengan kode Golomb
- Compiler mirip dengan QuickJS tetapi menggunakan parser non-rekursif untuk membatasi penggunaan stack C
- Menghasilkan bytecode dalam single pass tanpa parse tree
Pengujian dan benchmark
- Pengujian dasar:
make test - Microbenchmark QuickJS:
make microbench - Benchmark Octane (versi modifikasi untuk strict mode) dapat diunduh terpisah
- Jalankan:
make octane
- Jalankan:
Lisensi
- Didistribusikan di bawah lisensi MIT
- Hak cipta source code dimiliki oleh Fabrice Bellard dan Charlie Gordon
2 komentar
Komentar Hacker News
Kalau ada ini pada 2010, sepertinya bahasa scripting Redis akan jadi JavaScript, bukan Lua
Lua dipilih bukan karena alasan kebahasaan, melainkan karena keterbatasan implementasi (kecil, cepat, dan berbasis ANSI-C)
Beberapa ide di Lua memang bagus, tetapi secara pribadi saya merasa penyimpangannya dari sintaks keluarga Algol itu tidak perlu
Kebingungan yang muncul karena harus mempelajari konsep abstraksi baru seperti di SmallTalk atau FORTH memang sepadan, tetapi menurut saya perubahan di Lua tidak punya alasan sekuat itu
Lua adalah satu-satunya bahasa ringan yang mendukung tail call optimization (TCO), sehingga program bisa ditulis murni rekursif tanpa loop
JavaScript tidak punya optimisasi ini, jadi pendekatan seperti itu tidak memungkinkan
Lua juga sangat cocok untuk penulisan compiler. Alasannya karena strukturnya banyak yang rekursif
Untuk scripting Redis, JS mungkin memang lebih cocok, tetapi sayang kalau Lua diremehkan
Di Brasil, Pascal dan Ada lebih umum dipakai daripada C, jadi pengaruhnya datang dari sana
Ruby dan Perl juga muncul pada era serupa, tetapi mencoba perubahan sintaks yang jauh lebih radikal
Hampir tidak ada yang mencoba memisahkan parser dan lexer sambil menukar token seperti
{}denganthen/endDiskusi terkait: thread HN, diskusi Reddit
Engine ini membatasi JS dengan cara yang dulu saya inginkan saat mengerjakan JSC
Di web, pembatasan seperti ini tidak mungkin dilakukan karena kompatibilitas, tetapi di lingkungan embedded justru bisa jadi desain yang menyenangkan
Saya membuat playground untuk langsung menjalankan MicroQuickJS di browser
Versi WebAssembly MicroQuickJS
Sebagai referensi, ada juga versi QuickJS asli
QuickJS berukuran 2.28MB, sedangkan MicroQuickJS hanya 303KB, jadi jauh lebih ringan
Opsi
emcc -O3atau tambahan--closure 1sepertinya bisa mengecilkannya lagiQuickJS sudah dioptimalkan, dan hanya MicroQuickJS yang masih punya ruang untuk diperbaiki
Seperti kutipan terkenal Jeff Atwood, “setiap aplikasi yang bisa ditulis dengan JavaScript pada akhirnya akan ditulis dengan JavaScript”
Sekarang tampaknya itu juga berlaku untuk sistem embedded
Wiki Jeff Atwood
Tautan JSLinux
Sayang sekali proyek ini diunggah tanpa riwayat commit
Saya ingin melihat seberapa cepat pengembang level ini bisa menyelesaikan proyek
Toh karena basisnya QuickJS, perbandingan seperti itu mungkin juga tidak terlalu berarti
Saya penasaran apakah ini bisa jadi cara paling ringan untuk menyelesaikan tantangan JS YouTube di yt-dlp
Lihat dokumentasi EJS yt-dlp
QuickJS sudah didukung
Teka-teki JS dari YouTube terlalu rumit, sampai emulator JS yang dibuat dengan Python saja akhirnya menyerah
Saya tidak terlalu paham sistem embedded, tetapi saya penasaran apakah engine seperti ini bisa memungkinkan ESP32 atau Arduino diprogram dengan JavaScript
Seperti MicroPython
MicroQuickJS hanya mengimplementasikan sebagian ES5 dan tidak menyediakan binding lingkungan
Ia menjalankan kode JS dengan mengubahnya menjadi bytecode VM Lua, dan itu pendekatan yang cukup cerdas
Belakangan saya sempat menulis ulang CLI Node 0.8 lama itu dalam Rust, tetapi akhirnya perangkatnya kembali masuk laci
malloc(). Itu yang membuka kemungkinan iniTiming memang sangat penting. Waktu diposting tadi malam, tidak ada reaksi sama sekali
Ada strategi untuk memposting ulang pada jam pagi di AS, atau memposting ulang secara berkala
Fabrice Bellard adalah salah satu programmer paling produktif dan paling serbabisa yang masih hidup
Karya terkenalnya: FFmpeg, QEMU, JSLinux, TCC, QuickJS
Sosok yang legendaris
Pendekatannya dalam membuat program lengkap dengan dependensi dan alat seminimal mungkin terasa sangat mengesankan
Soalnya kalau benar satu orang, dia pasti perlu tidur juga
ts_server, TextSynth
Sepertinya dia lebih suka struktur di mana pengguna mengatur parameter lalu program berjalan tuntas dengan sendirinya
Daftar pemenang IOCCC
Sangat mengesankan bahwa “JS bisa dikompilasi dan dijalankan bahkan di RAM 10kB”
Ini terasa pas sekali untuk masa sekarang, ketika RAM kembali mahal
Saya penasaran apakah ini bisa dimasukkan ke Chromium atau Electron
Untuk pengenalan tentang Fabrice Bellard, silakan lihat yang dulu pernah saya tulis di komentar. Orang ini benar-benar konsisten sekaligus monster yang mengagumkan..
https://id.news.hada.io/topic?id=59#cid51