- Jawsm Rust में लिखा गया JavaScript→WebAssembly compiler है, एक experimental tool जो interpreter के बिना चलने वाली standalone WASM binaries बनाता है
- porffor की तरह यह standalone WASM बनाता है, लेकिन implementation approach अलग है; यह आधुनिक WASM GC, exception handling, और tail call optimization proposal के instructions का उपयोग करके JavaScript syntax को WASM instructions में बदलता है
- अभी यह test262 test suite के लगभग 25% tests pass करता है, और viability की पुष्टि के लिए महत्वपूर्ण माने गए scopes/closures, try/catch, async/await, generators implement हो चुके हैं
- यह अभी production-ready नहीं है; कई language features और built-in types गायब या अधूरे हैं, और RegExp, ज़्यादातर builtins, BigInt arithmetic भी मौजूद नहीं हैं
- generated binaries की runtime portability कम है, क्योंकि वे नए WASM proposals पर निर्भर हैं; फिलहाल इन्हें V8-based Chromium या Node में WASIp2 polyfill के साथ चलाया जाता है
Jawsm का लक्ष्य और स्थिति
- Jawsm एक JavaScript to WebAssembly compiler है, जिसका उच्चारण “awesome” जैसा है
- यह Rust में लिखा गया है, और इसका लक्ष्य JavaScript code को interpreter के बिना चलने वाली standalone WASM binary में बदलना है
- यह porffor जैसे output बनाता है, लेकिन implementation approach अलग है
- अभी यह एक experimental tool है और production में इस्तेमाल के लिए तैयार नहीं है
- कई JavaScript language features गायब या अधूरे हैं
- कई built-in types और methods भी गायब या अधूरे हैं
- long-term goal JavaScript language features का 100% support देना है
Jawsm क्यों बनाया गया
- यह project WebAssembly scenarios चलाने वाले stress test tool Crows पर काम करते समय शुरू हुआ
- Crows फिलहाल केवल Rust से WASM में compile किए गए code को support करता है
- छोटे tests अक्सर interpreted language में लिखना आसान होता है, लेकिन WASM पर scripting language चलाने का मौजूदा तरीका ideal नहीं है
- interpreter शामिल करने पर binary size कम से कम कुछ MB हो जाता है और memory usage भी बढ़ जाता है
- या TinyGo, AssemblyScript जैसे target language के variant का उपयोग करना पड़ता है
- आधुनिक WASM proposals का उपयोग करके, compiled interpreter के बिना भी JavaScript features का 100% implementation संभव हो सकता है—यही लक्ष्य है
- यह मानकर चला गया है कि WASM runtime खुद पहले से ही एक interpreter है
अभी काम करने वाले features
- Jawsm फिलहाल test262 test suite के लगभग 25% tests pass करता है
- project viability की पुष्टि के लिए 4 core features सभी implement हो चुके हैं
- scopes/closures
- try/catch
- async/await
- generators
- इसके अलावा जिन features के काम करने की उम्मीद है, वे हैं
var, let, const declarations और assignment
do..while, while, for, for..in, for..of loops
switch statement
break, continue का limited support
- string literals और string literal concatenation
- numbers और basic operators
+, -, *, /
- boolean और basic boolean operators
- arrays और Array से जुड़े ज़्यादातर functions
- object literals
new keyword
async, await
Promise API का limited support
- generator functions
try/catch
- बहुत basic BigInt support
अभी missing features
- फिलहाल missing मुख्य items ये हैं
- ज़्यादातर builtins
- मौजूदा builtins के ज़्यादातर methods
- RegExp expressions
- BigInt arithmetic
- अगले चरण की योजना इन features के implementation पर केंद्रित है
- basic regexp support
- RegExp literals
- RegExp object के बहुत basic functions
- BigInt literals और basic BigInt support
- equality checks या कई operators का उपयोग करते समय बेहतर automatic casting
- arrays, strings आदि basic builtins के और functions
execution environment और constraints
- Jawsm कुछ अपेक्षाकृत नए WASM proposals का उपयोग करता है, इसलिए generated binaries अभी runtimes के बीच ज्यादा portable नहीं हैं
- target implementation WASIp2 को ध्यान में रखकर है
- Wasmtime ऐसा runtime है जो components और WASIp2 चला सकता है, लेकिन Jawsm जिन चीज़ों का उपयोग करता है, जैसे WASM GC के कुछ हिस्से या exception handling, उन्हें support नहीं करता
- runtimes के standardized proposals के साथ catch up करने तक development आसान रखने के लिए V8 का उपयोग किया जाता है
- V8 को Chromium या Node के जरिए उपयोग किया जाता है
- जरूरी WASIp2 features को JavaScript polyfill से supplement किया जाता है
- repository में Jawsm द्वारा generated binaries चलाने के लिए
run.js script है
- अंततः लक्ष्य यह है कि यह WASM GC, exception handling, और WASIp2 API implement करने वाले सभी runtimes में चल सके
- या WASIp2 polyfill का उपयोग करने वाला तरीका भी इसमें शामिल है
उपयोग का तरीका
- अगर उद्देश्य contribution नहीं है, तो फिलहाल इसके उपयोग की सिफारिश नहीं की जाती
- repository clone करने के बाद
execute.sh को इस तरह इस्तेमाल किया जा सकता है
./execute.sh --cargo-run path/to/script.js
- यह command WAT file बनाता है, उसे binary में compile करता है, और फिर Node.js से चलाता है
- जरूरी tools ये हैं
- Rust का
cargo
wasm-tools का अपेक्षाकृत नया version
- Node.js v23.0.0 या उससे ऊपर
--cargo-run option देने पर पहले cargo run से project compile किया जाता है, फिर execute किया जाता है
--cargo-run के बिना चलाने पर यह release build run करने की कोशिश करता है, इसलिए पहले cargo build --release चलाना होगा
internal working
- Jawsm JavaScript syntax को WASM instructions में बदलता है
- conversion process में इन WASM proposals के instructions का उपयोग होता है
-
WASM GC
- exception handling
- tail call optimizations
- Rust code script को transform करता है, और JavaScript semantics को WASM में ले जाने के लिए types और functions का एक set साथ में उपयोग होता है
- ज़्यादातर WASM instructions tarnik का उपयोग करके generate किए जाते हैं
- tarnik एक Rust macro है जो Rust-like syntax के आधार पर WASM instructions generate करता है
scope और closure handling का example
- WASM function references, structs, arrays को support करता है, लेकिन JavaScript की scope semantics सीधे provide नहीं करता
- Jawsm JavaScript scope behavior की नकल करने के लिए extra WASM code generate करता है
- example JavaScript code इस प्रकार है
let a = "foo";
function bar() {
console.log(a);
}
bar();
- JavaScript में function definition अपने define हुए scope को inherit करती है, इसलिए
bar() को variable a access कर पाना चाहिए
- conversion flow मोटे तौर पर इस प्रकार है
- बिना parent वाला global scope बनाता है
- current scope में variable
a को "foo" के रूप में declare करता है
bar function object बनाते समय, function जिस scope में define हुआ था उसका reference भी साथ store करता है
- function के अंदर execute करते समय नया scope बनाता है, लेकिन parentScope reference बनाए रखता है
retrieve(scope, "a") current scope और सभी parent scopes में a को search करता है
- current scope से
bar लेकर call करता है
license
- code Apache 2.0 license के तहत distribute किया गया है
1 टिप्पणियां
Hacker News की राय
WASM GC proposal का वाकई बहुत चतुर इस्तेमाल किया गया है
अब तक JS→WASM compilers असल में पूरा JS engine साथ लोड करने वाले तरीके अपनाते थे, लेकिन JS structures को सीधे WASM के primitive features पर map करने की कोशिश पहली बार देखी है
Jaws से तुलना करना दिलचस्प होगा
पहले मैंने TypeScript के काफ़ी करीब एक भाषा को embedded ARM compiler के रूप में बनाया था
वह AssemblyScript की तुलना में TypeScript के कहीं ज़्यादा करीब थी, और तब इस्तेमाल की गई कुछ techniques मददगार हो सकती हैं
https://www.microsoft.com/en-us/research/uploads/prod/2019/09/static-typescript-draft2.pdf
“Rust इस्तेमाल करना मुझे वाकई पसंद है, लेकिन मैं यह भी जानता हूं कि यह व्यापक रूप से लोकप्रिय भाषा नहीं है” — क्या यह बात सही है?
Rust को बहुत ज़्यादा hype किया जा रहा है और आजकल यह हर जगह इस्तेमाल होती दिखती है
मेरे देश Estonia में, लोग जिस local job board का इस्तेमाल करते हैं उसके आधार पर local jobs 0 हैं, इसलिए असल मायने में इसे बिल्कुल भी लोकप्रिय कहना मुश्किल है
यह इस पर निर्भर करता है कि “व्यापक” को कैसे define करते हैं, लेकिन StackOverflow, PyPL जैसे indices और GitHub stats देखें तो Rust का usage JavaScript या Python का करीब 5~10% लगता है
अगर “आखिरकार JavaScript spec का 100% cover कर पाएंगे, इसका काफ़ी भरोसा है,” तो क्या test262_runner.rb results हैं?
Porffor के लेखक की presentation से test262 के बारे में पता चला, और README में https://github.com/CanadaHonk/porffor?tab=readme-ov-file#test262 जैसी progress indicator हो तो अच्छा होगा
अच्छा project है
अभी tests के करीब 12% pass हो रहे हैं, लेकिन implement करने में आसान बहुत से हिस्से अभी बचे हैं
खासकर यह देखते हुए कि project शुरू हुए सिर्फ़ 2 हफ्ते हुए हैं; और हां, इसका मतलब यह नहीं कि 100% तक progress linear होगी
built-in types और functions की लंबी tail बची है, लेकिन “मुश्किल हिस्सों” से implementation शुरू करने की वजह से कुछ आसान हिस्से अभी नहीं किए गए हैं
उदाहरण के लिए, test262 harness चलाने के लिए जितनी ज़रूरत थी उतनी ही syntax implement की है—conditional statements और while loops—और for, for in, for of, do while तथा switch जैसी condition expressions अभी implement नहीं हुई हैं
इन्हें मौजूदा if/else और while implementation की तरह ही काफ़ी आसानी से जोड़ा जा सकता है
awaitऔर generators की implementation खत्म कर लेने पर, आखिरी मुश्किल semantic concepts भी हल हो जाएंगे, इसलिए उसके बाद ऐसे आसान हिस्सों को implement करने का इरादा हैcoverage कितनी बढ़ेगी कहना मुश्किल है, लेकिन उदाहरण के लिए अभी
object["foo"]syntax implement नहीं है, इसलिए 1200 tests fail हो रहे हैंobject.fooकाम करता है, लेकिनobject["foo"]नहीं चलताइसका मतलब यह नहीं कि वे 1200 अपने-आप सभी pass हो जाएंगे, लेकिन इस तरह की अपेक्षाकृत simple syntax omissions की वजह से अक्सर सैकड़ों tests fail होते हैं
Porffor जैसा शानदार graph भी ज़रूर जोड़ना चाहता हूं
project README.md पढ़ने के बाद भी अभी साफ़ नहीं है कि expected usage pattern क्या है?
generated WASM code किस runtime के साथ और कैसे interact करता है, यह जानना चाहता हूं
क्या यह browser और दूसरे WASM runtimes दोनों के साथ compatible tool है, या केवल project से linked runtime में ही चलता है?
इससे जुड़ा सवाल: JavaScript code में web APIs या किसी specific environment में ही defined global identifiers—जैसे modern browser या Node.js के global identifiers—मिलें तो यह कैसे react करता है?
अगर ऐसे environment को target नहीं कर रहे, तो I/O कैसे करना चाहिए?
अच्छे सवाल हैं, और README में और detail जोड़ूंगा
यह project मुख्य रूप से server पर WebAssembly usage को target करता है
JavaScript के अंदर WebAssembly से JavaScript चलाने का ज़्यादा मतलब नहीं दिखता, लेकिन समय के साथ यह frontend plugin sandboxing में उपयोगी हो सकता है
चाहे browser में चलाएं या WasmTime/WasmEdge जैसे backend runtime में, फिलहाल WebAssembly के अंदर JavaScript execute करना ideal नहीं है
या तो V8/SpiderMonkey जैसे JS engines को WASM में compile करके उनके ऊपर scripts चलानी पड़ती हैं, या AssemblyScript जैसी “लगभग JavaScript” language से संतुष्ट होना पड़ता है
यही server workloads चलाने में limiting factor बनता है
उदाहरण के लिए Fastly WASM workers में SpiderMonkey इस्तेमाल करता है, और सिर्फ़ hello world भी हर instance पर 5~10MB memory लेता है
इसके उलट Shopify store की server-side customization के लिए WASM इस्तेमाल करता है और WASM binary को 250KB से कम तक limit करता है; इस size में कोई भी interpreter डालना मुश्किल है
इसलिए “recommended” language AssemblyScript बनी, और वजह यहां समझाई गई है: https://shopify.engineering/shopify-webassembly
यह स्थिति ऐतिहासिक रूप से इसलिए रही क्योंकि WASM बहुत simple runtime था
C code को machine code में compile करने की तरह WASM में compile करना अपेक्षाकृत आसान था, लेकिन WebAssembly खुद एक तरह का interpreter होने के बावजूद, उसके ऊपर higher-level languages interpret करना आसान नहीं था
अब garbage collection support और exception handling support जैसे नए proposals standardize हो रहे हैं, जिससे WebAssembly structs, arrays और function references जैसी capabilities वाला कहीं ज़्यादा powerful interpreter बन रहा है
Jaws इसी बात का फायदा उठाकर JS कोड को WASM कोड में बदलता है, और SpiderMonkey जैसे JS engine के बिना WASM से ही बने हुए code को interpret करवाता है
असल में Jaws से बना binary शायद 50KB से कम हो सकता है, जबकि SpiderMonkey को WASM में compile करके उसके ऊपर script चलाने वाले तरीके में यह 10MB के करीब होता है
memory usage भी काफी कम हो जाएगा
Fastly जैसी कंपनियों के लिए इसका मतलब memory usage और server cost को कई गुना घटा पाना है, और Shopify जैसी कंपनियों के लिए इसका मतलब मौजूदा JavaScript code, जैसे NPM packages और JavaScript ecosystem, को backend plugin authors के लिए उपलब्ध करा पाना है
“browser runtime के बिना JS चलाना” करीब आता हुआ लग रहा है
Porffor, Jaws, या किसी और project में से कोई न कोई आखिरकार सफल होगा लगता है
मुझे यह approach सच में पसंद है
binary सीधे generate करने की कोशिश करने के बजाय सीधे WASM को target करके build करने पर, WASM GC और WASI 0.3 में शामिल होने वाली लग रही async support पर निर्भर किया जा सकता है
string encoding के फर्क और related utilities को कैसे handle करते हैं?
मेरी धुंधली समझ के हिसाब से WASM UTF-8 support करता है और JS संभावित रूप से टूटे हुए UTF-16 को भी support करता है
CPU instruction set की तरह, WASM abstract machine में string या encoding की कोई concept नहीं है
यह linear memory के अंदर बस bytes हैं, और जो encoding चाहिए उसे खुद implement किया जा सकता है
WASM file format में names की encoding के लिए UTF-8 specify करता है, लेकिन इसका runtime virtual machine से कोई संबंध नहीं है
कुछ लोग उसे compiler भी कहते हैं
खैर, अच्छा बनाया है
क्या यह उसी code को JS में सीधे चलाने से तेज है, या यह दूसरे languages के साथ interop के लिए है?
modern JavaScript compilers JIT के जरिए frequently executed paths को काफी अच्छे से optimize करते हैं
इस project का use case JavaScript को WebAssembly sandbox environment में चलाने लायक बनाना है
उदाहरण के लिए Shopify WebAssembly से backend code extend करने देता है, लेकिन binary size को 250KB तक limit करता है
उस size में अभी JavaScript इस्तेमाल करना मुश्किल है, क्योंकि QuickJS जैसा simple interpreter भी WASM में compile करने पर कुछ MB का हो जाता है