- MicroQuickJS(MQuickJS), gömülü sistemler için tasarlanmış ultra hafif bir JavaScript motoru olup yalnızca yaklaşık 10kB RAM ve 100kB ROM ile çalışabiliyor
- QuickJS'e benzer hızı korurken bellek kullanımını azaltmak için tracing garbage collector ve UTF-8 string depolama biçimi kullanıyor
- Desteklenen dil, ES5'e yakın sınırlı bir JavaScript alt kümesi ve yalnızca hata olasılığı yüksek sözdizimlerini yasaklayan strict mode destekleniyor
- REPL aracı
mqjs ile script çalıştırma, bytecode kaydetme ve bellek sınırı ayarlama yapılabiliyor; üretilen bytecode doğrudan ROM'dan çalıştırılabiliyor
- Tüm motor ve standart kütüphane ROM'da bulunduğundan hızlı başlatma ve düşük bellek tüketimi sağlanıyor; bu da gömülü ortamlarda JavaScript çalıştırma verimliliğini artırıyor
Giriş
- MicroQuickJS(MQuickJS), gömülü sistemleri hedefleyen bir JavaScript motoru olup 10kB RAM ve 100kB ROM'da (ARM Thumb-2 kodu dahil) çalışır
- Yalnızca ES5'e yakın bir alt kümeyi destekler ve verimsiz ya da hata olasılığı yüksek sözdizimlerini yasaklayan strict mode ile çalışır
- QuickJS ile kodun bir kısmını paylaşsa da iç yapısı bellekten tasarruf etmek için tamamen farklı tasarlanmıştır
- Tracing garbage collector, CPU stack kullanmama, UTF-8 string depolama yaklaşımı kullanılır
REPL
- REPL komutu
mqjs olup script çalıştırma, değerlendirme, interaktif mod, bellek sınırı ayarlama ve bytecode kaydetmeyi destekler
- Örnek:
./mqjs --memory-limit 10k tests/mandelbrot.js
-o seçeneği ile derlenmiş bytecode bir dosyaya kaydedilebilir
- Kaydedilen bytecode
./mqjs mandelbrot.bin ile çalıştırılabilir
- Bytecode, CPU'nun endianness ve word length (32/64-bit) değerlerine göre farklıdır;
-m32 seçeneği ile 32 bit için bytecode üretilebilir
--no-column seçeneği ile debug bilgisindeki sütun numaraları kaldırılabilir
Strict mode
- Yalnızca strict mode desteklenir;
with anahtar sözcüğü kullanılamaz ve global değişkenler mutlaka var ile tanımlanmalıdır
- Array hole kullanımına izin verilmez
- Örnek:
a[10] = 2 bir TypeError üretir
- Delikli array gerekiyorsa normal object kullanılmalıdır
- Yalnızca global eval desteklenir, local değişkenlere erişim yoktur
- Value boxing desteklenmez (
new Number(1) vb.)
JavaScript alt kümesi
- Strict mode temellidir ve ES5 uyumluluğuna odaklanır
- Array object'lerinde hole yoktur; aralık dışı indeks erişimi hatadır
for in, object'in yalnızca kendi özellikleri üzerinde dolaşır; for of yalnızca array'leri destekler
- Global object vardır ancak getter/setter desteklenmez; doğrudan oluşturulan özellikler global değişken olarak açığa çıkmaz
- Regexp yalnızca ASCII için büyük/küçük harf ayrımını işler;
/./ UTF-16 yerine Unicode code point bazında eşleşir
- String fonksiyonları yalnızca ASCII işler (
toLowerCase, toUpperCase)
- Date için yalnızca
Date.now() desteklenir
- Ek desteklenen özellikler:
for of, Typed arrays, \u{hex} string literal
- Math fonksiyonları:
imul, clz32, fround, trunc, log2, log10
- Üs alma operatörü, regexp flag'leri (s, y, u), string fonksiyonları(
replaceAll, trimStart, trimEnd), globalThis
C API
- C kütüphanesi bağımlılığı minimumdur,
malloc, free, printf kullanılmaz
- Bellek buffer'ı doğrudan sağlanmalıdır ve motor yalnızca bu buffer içinde bellek tahsisi yapar
- Örnek:
ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib)
- Garbage collection yaklaşımı nedeniyle
JS_FreeValue() çağrısı gerekmez
- Object adresleri her tahsiste taşınabilir olduğundan
JSValue pointer kullanımı önerilir
JS_PushGCRef() / JS_PopGCRef() ile güvenli referans yönetimi yapılır
- Standart kütüphane, ROM'da saklanabilen C yapıları olarak derlenir; böylece hızlı başlatma ve düşük RAM kullanımı sağlanır
- Bytecode çalıştırma ROM'dan mümkündür;
JS_RelocateBytecode() ile yeniden yerleştirildikten sonra JS_LoadBytecode() ve JS_Run() ile çalıştırılır
- Matematik kütüphanesi (libm.c) ve floating-point emulator dahildir
İç yapı ve QuickJS karşılaştırması
- Garbage collection: reference counting yerine tracing ve compacting GC kullanılır
- Bellek parçalanmasını önler, object boyutunu küçültür
- Değer gösterimi: CPU word boyutuna (32/64-bit) göre tasarlanmıştır
- 31 bit integer, Unicode code point, floating-point ve memory block pointer saklayabilir
- String'ler UTF-8 olarak saklanır, bu da QuickJS'in 8/16-bit array yaklaşımından daha verimlidir
- C fonksiyonları tek bir değer olarak saklanabilir, özellik eklenemez
- Standart kütüphane ROM'da yer alır ve minimum RAM object ile hızlı motor başlatma sağlar
- Bytecode stack tabanlıdır ve dolaylı referans tablosu üzerinden salt okunur şekilde işlenir
- Golomb code ile satır ve sütun numaraları sıkıştırılır
- Derleyici QuickJS'e benzer, ancak C stack kullanımını sınırlamak için özyinelemesiz parser kullanır
- Parse tree olmadan tek geçişte bytecode üretimi yapar
Test ve benchmark
- Temel testler:
make test
- QuickJS micro benchmark:
make microbench
- Octane benchmark için (strict mode'a uyarlanmış sürüm) ayrı indirme yapılabilir
Lisans
- MIT lisansı ile dağıtılır
- Kaynak kod telif hakkı Fabrice Bellard ve Charlie Gordon'a aittir
2 yorum
Fabrice Bellard hakkında bir tanıtım için daha önce yorum olarak yazdıklarıma bakabilirsiniz. Gerçekten hem istikrarlı hem de şaşırtıcı derecede olağanüstü biri..
https://news.hada.io/comment?id=51
Hacker News görüşleri
Eğer 2010'da böyle bir şey olsaydı, Redis'in betik dili muhtemelen Lua değil JavaScript olurdu
Lua, dilsel nedenlerle değil, uygulama tarafındaki kısıtlar yüzünden seçildi (küçük, hızlı ve ANSI-C tabanlı)
Lua'nın bazı fikirleri güzel, ama kişisel olarak Algol ailesi sözdiziminden uzaklaşmasını gereksiz buluyordum
SmallTalk ya da FORTH'ta olduğu gibi yeni soyut kavramlar öğrenmenin getirdiği kafa karışıklığına değiyorsa tamam, ama Lua'daki değişim bana o kadar gerekçeli gelmiyordu
Lua, tail call optimization (TCO) destekleyen tek hafif dil ve bu sayede döngü kullanmadan yalnızca özyineleme ile program yazabiliyorsunuz
JavaScript'te bu optimizasyon yok, dolayısıyla aynı yaklaşım mümkün değil
Lua ayrıca derleyici yazımı için özellikle uygun. Çünkü özyinelemeli yapı çok fazla
Redis scripting için JS daha uygun olabilir, ama Lua'yı küçümsemek üzücü
Brezilya'da C'den çok Pascal ve Ada daha yaygındı, dolayısıyla etkisi oradan geliyor
Ruby ve Perl de benzer dönemde çıktı ama çok daha radikal sözdizimi değişiklikleri denediler
Parser ve lexer'ı ayırıp
{}yerinethen/endgibi token'ları değiştirilebilir yapmak pek denenmiş bir şey değilİlgili tartışmalar: HN başlığı, Reddit tartışması
Bu motor, eskiden JSC üzerinde çalışırken benim istediğim şekilde JS'i kısıtlıyor
Web'de uyumluluk yüzünden böyle sınırlamalar mümkün değil, ama gömülü ortamlarda bu tür kısıtlar aksine keyif veren bir tasarım olabilir
Tarayıcıda doğrudan MicroQuickJS çalıştırabileceğiniz bir playground yaptım
MicroQuickJS WebAssembly sürümü
Referans için özgün QuickJS sürümü de var
QuickJS 2.28MB, MicroQuickJS ise 303KB; yani çok daha hafif
emcc -O3seçeneği ya da--closure 1eklenirse daha da küçülebilirQuickJS zaten optimize edilmiş, iyileştirme alanı daha çok MicroQuickJS tarafında var
Jeff Atwood'un meşhur sözüyle, "JavaScript ile yazılabilecek her uygulama eninde sonunda JavaScript ile yazılır"
Görünüşe göre artık bu söz gömülü sistemler için de geçerli
Jeff Atwood vikisi
JSLinux bağlantısı
Commit geçmişi olmadan yüklenmiş olması biraz hayal kırıklığı
Bu seviyedeki bir geliştiricinin projeyi ne kadar hızlı bitirdiğini görmek isterdim
Zaten QuickJS tabanlı olduğu için karşılaştırmanın çok da anlamlı olmayacağını düşünüyorum
Bunun yt-dlp'nin YouTube JS challenge sorununu çözmenin en hafif yolu olup olamayacağını merak ediyorum
Bkz. yt-dlp EJS belgeleri
QuickJS zaten destekleniyor
YouTube'un JS bulmacaları o kadar karmaşık ki, Python ile yazılmış bir JS emülatörü bile vazgeçmek zorunda kalmıştı
Gömülü sistemleri pek bilmiyorum ama böyle bir motorun ESP32 veya Arduino'yu JavaScript ile programlamayı mümkün kılıp kılamayacağını merak ediyorum
MicroPython gibi yani
MicroQuickJS yalnızca ES5'in bir kısmını uyguluyor ve ortam binding'leri de sunmuyor
JS kodunu Lua VM bytecode'una çevirip çalıştırıyordu; oldukça zekice bir yaklaşımdı
Yakın zamanda o eski Node 0.8 CLI'ını Rust ile yeniden yazmayı denedim ama cihaz sonunda yine çekmeceye döndü
Zamanlama gerçekten çok önemli. Dün gece paylaşıldığında hiç ilgi görmemişti
ABD sabah saatlerinde yeniden paylaşmak ya da düzenli aralıklarla tekrar göndermek gibi taktikler var
Fabrice Bellard, bugün yaşayan en üretken ve çok yönlü programcılardan biri
Başlıca işleri: FFmpeg, QEMU, JSLinux, TCC, QuickJS
Efsanevi bir isim
En az bağımlılık ve araçla eksiksiz programlar üretme yaklaşımı gerçekten etkileyici
Gerçekten tek kişiyse uyuması da gerekiyor sonuçta
ts_server, TextSynth
Kullanıcının parametreleri verip programın kendi başına tamamlanacak şekilde çalıştığı yapıları tercih ediyor gibi görünüyor
IOCCC kazananları listesi
"10kB RAM'de bile JS derleyip çalıştırabiliyor" olması etkileyici
RAM'in pahalandığı bu dönemde zamanlaması da manidar
Acaba bunu Chromium veya Electron'a koymak mümkün olur mu diye merak ediyorum