Introduzione a WebAssembly: inizia con WebAssembly

WebAssembly promette un nuovo tipo di web: prestazioni più veloci per gli utenti e maggiore flessibilità per gli sviluppatori. Invece di essere vincolato all'utilizzo di JavaScript come unico linguaggio per l'interazione web lato client, uno sviluppatore può scegliere tra un'ampia gamma di altri linguaggi: C, TypeScript, Rust, Ruby, Python e lavorare in quello che preferisce con.

In origine, l'unico modo per creare WebAssembly (o WASM in breve) era compilare il codice C / C ++ in WebAssembly utilizzando la toolchain Emscripten. Oggi, non solo gli sviluppatori hanno più opzioni di lingua, ma è diventato più facile compilare questi altri linguaggi direttamente in WebAssembly, con meno passaggi intermedi.

In questo articolo, esamineremo i passaggi necessari per implementare i componenti WebAssembly in un'app Web. Poiché WebAssembly è un work-in-progress, i passaggi dipendono in larga misura dalla lingua utilizzata e la toolchain probabilmente continuerà a cambiare per un po 'di tempo. Ma in questo momento è possibile scrivere e distribuire applicazioni WebAssembly utili, anche se minime, in diverse lingue.

Scegli una lingua supportata da WebAssembly

Il primo passo verso la distribuzione di un'applicazione WebAssembly è scegliere una lingua che può essere compilata in WebAssembly come destinazione. C'è una buona probabilità che almeno uno dei principali linguaggi che stai utilizzando in produzione possa essere convertito in WebAssembly, o abbia un compilatore che può emettere WebAssembly.

Ecco i primi classificati:

  • C. Ovviamente. Il modo tipico per trasformare il codice C in WebAssembly è tramite Emscripten, poiché C-to-Emscripten-to-WebAssembly è stata la prima toolchain WebAssembly a venire. Ma stanno emergendo altri strumenti. Un intero compilatore, Cheerp, è stato progettato specificamente per generare applicazioni WebAssembly da codice C / C ++. Cheerp può anche scegliere come target JavaScript, asm.js o qualsiasi combinazione di quanto sopra. È anche possibile utilizzare la toolchain Clang per creare payload WebAssembly, sebbene il processo richieda ancora una buona dose di sollevamento manuale. (Ecco un esempio.)
  • Ruggine. Il linguaggio di programmazione dei sistemi di Mozilla, progettato per essere sicuro e veloce, è uno dei principali candidati per il supporto nativo di WebAssembly. Le estensioni alla toolchain Rust ti consentono di compilare direttamente dal codice Rust a WebAssembly. È necessario utilizzare la nightlytoolchain di Rust per eseguire la compilazione di WebAssembly, quindi questa funzionalità dovrebbe essere considerata sperimentale per ora.
  • TypeScript . Per impostazione predefinita, TypeScript viene compilato in JavaScript, il che significa che a sua volta potrebbe essere compilato in WebAssembly. Il progetto AssemblyScript riduce il numero di passaggi coinvolti, consentendo la compilazione di TypeScript strettamente tipizzato in WebAssembly.

Diverse altre lingue stanno iniziando a indirizzare WebAssembly, ma sono nelle primissime fasi. I seguenti linguaggi possono essere utilizzati per creare componenti WebAssembly, ma solo in modi più limitati rispetto a C, Rust e TypeScript:

  • D . Il linguaggio D ha recentemente aggiunto il supporto per la compilazione e il collegamento direttamente a WebAssembly.
  • Java . Il bytecode Java può essere compilato in anticipo su WebAssembly tramite il progetto TeaVM. Ciò significa che qualsiasi linguaggio che emette bytecode Java può essere compilato in WebAssembly, ad esempio Kotlin, Scala o Clojure. Tuttavia, molte delle API Java che non possono essere implementate in modo efficiente in WebAssembly sono limitate, come le API di riflessione e risorse, quindi TeaVM, e quindi WebAssembly, è utile solo per un sottoinsieme di app basate su JVM. 
  • Lua . Il linguaggio di scripting Lua ha una lunga storia di utilizzo come linguaggio incorporato, proprio come JavaScript. Tuttavia, gli unici progetti per trasformare Lua in WebAssembly implicano l'utilizzo di un motore di esecuzione in-browser: wasm_lua incorpora un runtime Lua nel browser, mentre Luwa JIT-compila Lua in WebAssembly.
  • Kotlin / Native . I fan del linguaggio Kotlin, uno spinoff di Java, hanno atteso con impazienza il rilascio completo di Kotlin / Native, un back-end LLVM per il compilatore Kotlin in grado di produrre binari autonomi. Kotlin / Native 0.4 ha introdotto il supporto per WebAssembly come destinazione della compilazione, ma solo come prova del concetto.
  • .Net . I linguaggi .Net non hanno ancora il supporto completo per WebAssembly, ma alcuni esperimenti sono iniziati. Vedere Blazor, che può essere utilizzato per creare app Web a pagina singola in .Net tramite C # e la sintassi "Razor" di Microsoft.
  • Nim . Questo linguaggio emergente si compila in C, quindi in teoria si potrebbe compilare il C risultante in WebAssembly. Tuttavia, un back-end sperimentale per Nim chiamato nwasm è in fase di sviluppo.
  • Altri linguaggi basati su LLVM . In teoria, qualsiasi linguaggio che sfrutta il framework del compilatore LLVM può essere compilato in WebAssembly, poiché LLVM supporta WebAssembly come uno dei tanti obiettivi. Tuttavia, questo non significa necessariamente che qualsiasi linguaggio compilato con LLVM verrà eseguito così com'è in WebAssembly. Significa solo che LLVM semplifica il targeting di WebAssembly.

Tutti i progetti di cui sopra convertono il programma originale o il bytecode generato in WebAssembly. Ma per linguaggi interpretati come Ruby o Python, c'è un altro approccio: invece di convertire le app stesse, si converte il runtime del linguaggio  in WebAssembly. I programmi vengono quindi eseguiti così come sono nel runtime convertito. Poiché molti runtime di linguaggio (inclusi Ruby e Python) sono scritti in C / C ++, il processo di conversione è fondamentalmente lo stesso di qualsiasi altra applicazione C / C ++.

Ovviamente questo significa che il runtime convertito deve essere scaricato nel browser prima che qualsiasi applicazione possa essere eseguita con esso, rallentando il caricamento e i tempi di analisi. Una versione "pura" di WebAssembly di un'app è più leggera. Pertanto, la conversione in runtime è nella migliore delle ipotesi una misura provvisoria finché più lingue non supportano WebAssembly come destinazione di esportazione o compilazione.

Integra WebAssembly con JavaScript

Il passo successivo è scrivere il codice nella lingua che hai scelto, con una certa attenzione a come quel codice interagirà con l'ambiente WebAssembly, quindi compilarlo in un modulo WebAssembly (un binario WASM) e infine integrare quel modulo con un esistente Applicazione JavaScript.

I passaggi esatti su come esportare il codice in WebAssembly variano enormemente a seconda della toolchain. Devieranno anche un po 'dal modo in cui i normali binari nativi sono costruiti per quella lingua. Ad esempio, in Rust, dovrai seguire diversi passaggi:

  1. Configura la nightly build per Rust, con la wasm32-unknown-unknowntoolchain.
  2. Scrivi il tuo codice Rust con funzioni esterne dichiarate come #[no-mangle].
  3. Crea il codice utilizzando la toolchain sopra.

(Per un riepilogo dettagliato dei passaggi precedenti, vedere The Rust and WebAssembly Book su GitHub.)

Vale la pena notare che qualunque lingua tu stia utilizzando, dovrai avere almeno un livello minimo di competenza in JavaScript per integrare il codice con un front-end HTML. Se gli snippet JavaScript in-page in questo esempio da The Rust and WebAssembly Book ti sembrano greci, dedica un po 'di tempo per imparare almeno JavaScript sufficiente per capire cosa sta succedendo lì.

L'integrazione di WebAssembly e JavaScript viene eseguita utilizzando l' WebAssemblyoggetto in JavaScript per creare un ponte al codice WebAssembly. Mozilla ha la documentazione su come farlo. Ecco un esempio di WebAssembly separato per Rust, ed ecco un esempio di WebAssembly per Node.js.

Al momento, l'integrazione tra il back-end WebAssembly e il front-end JavaScript / HTML è ancora la parte più complicata e manuale dell'intero processo. Ad esempio, con Rust, i bridge a JavaScript devono ancora essere creati manualmente, tramite puntatori di dati grezzi.

Tuttavia, più parti della toolchain stanno iniziando ad affrontare questo problema. Il framework Cheerp consente ai programmatori C ++ di parlare con le API del browser tramite uno spazio dei nomi dedicato. E Rust offre wasm-bindgen, che funge da ponte a due vie tra JavaScript e Rust e tra JavaScript e WebAssembly.

Inoltre, è allo studio una proposta di alto livello su come gestire i binding all'host. Una volta finalizzato, fornirà un modo standard per i linguaggi che si compilano in WebAssembly per interagire con gli host. La strategia a lungo termine con questa proposta comprende anche i collegamenti a host che non sono browser, ma i collegamenti del browser sono il caso d'uso immediato e a breve termine.

Debug e profilazione delle app WebAssembly

Un'area in cui gli strumenti WebAssembly sono ancora nelle prime fasi è il supporto per il debug e la creazione di profili. 

Fino a quando non sono arrivate le mappe sorgente JavaScript, i linguaggi compilati in JavaScript erano spesso difficili da eseguire il debug perché il codice originale e quello compilato non potevano essere correlati facilmente. WebAssembly ha alcuni degli stessi problemi: se scrivi codice in C e lo compili in WASM, è difficile tracciare correlazioni tra il codice sorgente e il codice compilato.

Le mappe sorgente JavaScript indicano quali righe nel codice sorgente corrispondono a quali regioni del codice compilato. Alcuni strumenti WebAssembly, come Emscripten, possono anche emettere mappe sorgente JavaScript per codice compilato. Uno dei piani a lungo termine per WebAssembly è un sistema di mappe di origine che va oltre ciò che è disponibile in JavaScript, ma è ancora solo in fase di proposta.

Al momento, il modo più diretto per eseguire il debug del codice WASM in natura è utilizzare la console di debug del browser web. Questo articolo su WebAssemblyCode mostra come generare codice WASM con una mappa di origine, renderlo disponibile per gli strumenti di debug del browser e scorrere il codice. Notare che i passaggi descritti dipendono dall'utilizzo dello emccstrumento per emettere il WASM. Potrebbe essere necessario modificare i passaggi a seconda della specifica toolchain.