Fabrice Bellard ने MicroQuickJS जारी किया
(github.com/bellard)- MicroQuickJS(MQuickJS) एम्बेडेड सिस्टम्स के लिए डिज़ाइन किया गया एक अति-हल्का JavaScript इंजन है, जो लगभग 10kB RAM और 100kB ROM पर चल सकता है
- QuickJS जैसी गति बनाए रखते हुए मेमोरी उपयोग कम करने के लिए इसमें tracing garbage collector और UTF-8 string storage अपनाया गया है
- समर्थित भाषा ES5 के क़रीब JavaScript का सीमित subset है, और केवल strict mode की अनुमति है, जो त्रुटि-प्रवण syntax को प्रतिबंधित करता है
- REPL टूल
mqjsके ज़रिए script execution, bytecode save करना, और memory limit सेट करना संभव है, तथा बना हुआ bytecode सीधे ROM से चलाया जा सकता है - पूरा इंजन और standard library ROM में resident रहते हैं, जिससे तेज़ initialization और कम memory consumption हासिल होता है और एम्बेडेड वातावरण में JavaScript execution efficiency बढ़ती है
परिचय
- MicroQuickJS(MQuickJS) एम्बेडेड सिस्टम्स को ध्यान में रखकर बनाया गया JavaScript इंजन है, जो 10kB RAM और 100kB ROM (ARM Thumb-2 code सहित) पर काम करता है
- इसकी गति QuickJS के समान है
- यह केवल ES5 के क़रीब subset को सपोर्ट करता है, और सिर्फ़ strict mode में चलता है, जो अप्रभावी या त्रुटि-प्रवण syntax को प्रतिबंधित करता है
- यह QuickJS के साथ कुछ code साझा करता है, लेकिन इसकी internal structure मेमोरी बचाने के लिए पूरी तरह अलग ढंग से डिज़ाइन की गई है
- इसमें tracing garbage collector, CPU stack का उपयोग न करना, और UTF-8 string storage का उपयोग किया गया है
REPL
- REPL command
mqjsहै, जो script execution, evaluation, interactive mode, memory limit setting, और bytecode save करने को सपोर्ट करता है- उदाहरण:
./mqjs --memory-limit 10k tests/mandelbrot.js
- उदाहरण:
-ooption के साथ compiled bytecode को फ़ाइल में सेव किया जा सकता है- सेव किया गया bytecode
./mqjs mandelbrot.binसे चलाया जा सकता है
- सेव किया गया bytecode
- bytecode CPU के endianness और word length (32/64-bit) के अनुसार अलग होता है, और
-m32option से 32-bit bytecode बनाया जा सकता है --no-columnoption से debug information के column number हटाए जा सकते हैं
strict mode
- केवल strict mode की अनुमति है;
withkeyword का उपयोग नहीं किया जा सकता, और global variables को अनिवार्य रूप सेvarसे declare करना होगा - array holes की अनुमति नहीं है
- उदाहरण:
a[10] = 2पर TypeError होगा - यदि holes वाला array चाहिए, तो सामान्य object का उपयोग करें
- उदाहरण:
- केवल global eval समर्थित है; local variables तक पहुँच नहीं है
- value boxing समर्थित नहीं है (
new Number(1)आदि)
JavaScript subset
- strict mode आधारित, ES5 compatibility पर केंद्रित
- Array object में holes नहीं होते, और range के बाहर index access करने पर error होता है
for inobject की own properties पर ही iterate करता है, औरfor ofकेवल arrays को सपोर्ट करता है- global object मौजूद है, लेकिन getter/setter की अनुमति नहीं है, और सीधे बनाई गई properties global variables के रूप में expose नहीं होतीं
- regular expressions (Regexp) में uppercase/lowercase matching केवल ASCII तक सीमित है, और
/./UTF-16 के बजाय Unicode code points के आधार पर match करता है - string functions केवल ASCII को संभालते हैं (
toLowerCase,toUpperCase) - Date में केवल
Date.now()समर्थित है - अतिरिक्त समर्थित सुविधाएँ:
for of, Typed arrays,\u{hex}string literals- Math functions:
imul,clz32,fround,trunc,log2,log10 - exponentiation operator, regexp flags (s, y, u), string functions (
replaceAll,trimStart,trimEnd), globalThis
C API
- C library dependencies न्यूनतम रखी गई हैं;
malloc,free,printfका उपयोग नहीं होता - memory buffer सीधे प्रदान करना होता है, और इंजन उसी buffer के भीतर memory allocate करता है
- उदाहरण:
ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib)
- उदाहरण:
- garbage collection method के कारण
JS_FreeValue()कॉल करने की आवश्यकता नहीं है - object address हर allocation पर बदल सकता है, इसलिए
JSValuepointer का उपयोग करने की सिफ़ारिश की जाती हैJS_PushGCRef()/JS_PopGCRef()से सुरक्षित reference management संभव है
- standard library को ऐसे C structures के रूप में compile किया जाता है जिन्हें ROM में रखा जा सकता है, जिससे तेज़ initialization और कम RAM usage मिलता है
- bytecode execution ROM से संभव है, और
JS_RelocateBytecode()से relocate करने के बादJS_LoadBytecode()औरJS_Run()से चलाया जा सकता है - math library (libm.c) और floating-point emulator अंतर्निहित हैं
आंतरिक संरचना और QuickJS से तुलना
- garbage collection: reference counting के बजाय tracing और compaction GC का उपयोग
- इससे memory fragmentation रुकती है और object size कम होता है
- value representation: CPU word size (32/64-bit) के अनुरूप डिज़ाइन
- इसमें 31-bit integers, Unicode code points, floating point values, और memory block pointers स्टोर किए जा सकते हैं
- strings UTF-8 में store की जाती हैं, जो QuickJS की 8/16-bit array approach से अधिक efficient है
- C functions को single value के रूप में store किया जा सकता है, लेकिन उनमें properties जोड़ी नहीं जा सकतीं
- standard library ROM में resident रहती है, और RAM objects को न्यूनतम रखकर तेज़ engine initialization संभव बनाती है
- bytecode stack-based है और indirect reference table के माध्यम से read-only बनाया जाता है
- Golomb code से line और column numbers compress किए जाते हैं
- compiler QuickJS जैसा है, लेकिन C stack usage सीमित रखने के लिए non-recursive parser का उपयोग करता है
- parse tree के बिना single-pass bytecode generation
टेस्ट और बेंचमार्क
- बेसिक टेस्ट:
make test - QuickJS micro benchmark:
make microbench - Octane benchmark (strict mode के लिए संशोधित संस्करण) अलग से डाउनलोड किया जा सकता है
- चलाने के लिए:
make octane
- चलाने के लिए:
लाइसेंस
- MIT लाइसेंस के तहत वितरित
- source code का copyright Fabrice Bellard और Charlie Gordon के पास है
2 टिप्पणियां
Hacker News की राय
अगर 2010 में यह मौजूद होता, तो लगता है Redis की scripting language Lua नहीं बल्कि JavaScript होती
Lua को भाषाई कारणों से नहीं, बल्कि implementation constraints (छोटी, तेज़, ANSI-C आधारित) की वजह से चुना गया था
Lua के कुछ ideas अच्छे हैं, लेकिन निजी तौर पर मुझे Algol-शैली के व्याकरण से हटना बेवजह लगा
SmallTalk या FORTH की तरह नए abstraction concepts सीखने की कीमत पर जो भ्रम पैदा होता है, वह सार्थक हो सकता है, लेकिन Lua के बदलाव के लिए उतनी ठोस वजह नहीं लगती
Lua इकलौती lightweight language है जो tail call optimization(TCO) सपोर्ट करती है, और इसकी वजह से loops के बिना सिर्फ recursion से program लिखे जा सकते हैं
JavaScript में यह optimization नहीं है, इसलिए उसी तरीके से यह संभव नहीं
Lua compiler writing के लिए भी खास तौर पर उपयुक्त है, क्योंकि उसमें recursive structures बहुत होते हैं
Redis scripting के लिए JS शायद ज़्यादा फिट हो, लेकिन Lua को कमतर बताना ठीक नहीं लगता
ब्राज़ील में C से ज़्यादा Pascal और Ada इस्तेमाल होते थे, इसलिए उसी का असर था
Ruby और Perl भी लगभग उसी दौर में आए, लेकिन उन्होंने syntax में कहीं ज़्यादा radical बदलाव किए
parser और lexer को अलग रखते हुए भी,
{}की जगहthen/endजैसे tokens बदलकर लगाने की कोशिशें लगभग नहीं हुई हैंसंबंधित चर्चा: HN thread, Reddit चर्चा
यह engine JS को उसी तरह restrict करता है जैसा मैं पहले JSC पर काम करते समय चाहता था
web पर compatibility की वजह से ऐसी सीमाएँ संभव नहीं, लेकिन embedded environments में यही सीमाएँ उल्टा खुशी देने वाला design बन सकती हैं
मैंने एक playground बनाया है जहाँ browser में सीधे MicroQuickJS चलाकर देखा जा सकता है
MicroQuickJS WebAssembly version
संदर्भ के लिए मूल QuickJS version भी है
QuickJS 2.28MB है, जबकि MicroQuickJS सिर्फ 303KB, यानी काफ़ी हल्का
emcc -O3option या--closure 1जोड़ने से शायद और कम हो सकता हैQuickJS पहले से optimized है, और सुधार की गुंजाइश सिर्फ MicroQuickJS में दिखती है
Jeff Atwood के मशहूर कथन की तरह, “हर वह app जो JavaScript में लिखा जा सकता है, अंततः JavaScript में लिखा जाएगा”
अब लगता है यह बात embedded systems पर भी लागू हो रही है
Jeff Atwood wiki
JSLinux लिंक
यह थोड़ा अफ़सोसजनक है कि इसे commit history के बिना upload किया गया
मैं देखना चाहता था कि इस स्तर का developer किसी project को कितनी तेज़ी से पूरा करता है
वैसे भी यह QuickJS आधारित है, इसलिए तुलना का ज़्यादा मतलब शायद नहीं
सोच रहा हूँ कि क्या यह yt-dlp की YouTube JS challenge को हल करने का सबसे हल्का तरीका हो सकता है
yt-dlp EJS docs देखें
QuickJS पहले से supported है
YouTube की JS puzzles इतनी जटिल हैं कि Python में बना JS emulator भी उन्हें संभालना छोड़ चुका है
embedded systems की मुझे ज़्यादा जानकारी नहीं, लेकिन सोच रहा हूँ कि क्या ऐसा engine ESP32 या Arduino को JavaScript से program करने लायक बना सकता है
कुछ MicroPython जैसा
MicroQuickJS में सिर्फ ES5 का कुछ हिस्सा implemented है, और यह environment bindings भी नहीं देता
वह JS code को Lua VM bytecode में बदलकर चलाता था, और यह काफ़ी चतुर तरीका था
हाल में मैंने उस पुराने Node 0.8 CLI को Rust में फिर से लिखने की कोशिश भी की, लेकिन आखिरकार hardware फिर दराज़ में लौट गया
timing सच में बहुत अहम है। कल रात इसे पोस्ट किया था तो कोई प्रतिक्रिया नहीं मिली
एक रणनीति यह है कि अमेरिकी सुबह के समय फिर से पोस्ट करो, या समय-समय पर दोबारा पोस्ट करते रहो
Fabrice Bellard मौजूदा समय के सबसे उत्पादक और बहुमुखी programmers में से एक हैं
प्रमुख काम: FFmpeg, QEMU, JSLinux, TCC, QuickJS
सचमुच एक दिग्गज हस्ती
बहुत कम dependencies और tools के साथ complete programs बनाने का उनका तरीका प्रभावशाली है
क्योंकि अगर वह सचमुच एक ही इंसान हैं, तो उन्हें सोना भी पड़ता होगा
ts_server, TextSynth
लगता है वे ऐसी संरचना पसंद करते हैं जहाँ user parameters सेट करे और program अपने आप पूरा चल जाए
IOCCC winners list
यह बात प्रभावशाली है कि “10kB RAM में भी JS को compile और run किया जा सकता है”
आजकल जब RAM फिर महँगी हो रही है, यह बिल्कुल सही समय पर आया लगता है
सोच रहा हूँ कि क्या इसे Chromium या Electron में डाला जा सकता है
Fabrice Bellard के बारे में परिचय के लिए मैंने पहले जो कमेंट लिखा था, उसे देखें। यह शख्स वाकई लगातार कमाल करने वाला अद्भुत दिग्गज है..
https://hi.news.hada.io/topic?id=59#cid51