Voir le flux RSS

Rust, WebAssembly et plein d'autres trucs sympathiques.

[Actualité] Wasm-bindgen, qu'est-ce que c'est ?

Noter ce billet
par , 30/07/2018 à 08h00 (1412 Affichages)
WebAssembly est, avant tout, un standard pensé dans l’optique d’améliorer les performances de JS et d’établir un socle commun qui pourra, à l’avenir, être exploité par une variété de langages. Les premiers à communiquer avec wasm étant C, C++ ainsi que Rust.

Dans ce billet, je m’efforcerai de détailler les (chouettes) solutions mises en place pour faciliter cette intégration. C’est parti !

Un peu de contexte...

Actuellement, il existe trois principaux outils supportant la compilation, vers wasm, des langages dont je parlais plus haut :
  1. C et C++: Emscripten (site web, dépôt) et Binaryen (site web, dépôt). L’un est une importante toolchain qui n’a fait "que" ajouter un nouvel élément à son trousseau, alors que l’autre a été créé par l’équipe en charge du développement et de la démocratisation de WebAssembly. N’étant pas l’objet de ce billet, je vous invite vivement à consulter leur site web respectif, où vous pourrez y connaître les différences techniques des deux projets ;
  2. Rust: wasm-bindgen, qui est certainement le moins mature, pour le moment, des trois.


Notez que Emscripten et Binaryen sont axés sur C et C++. Ça ne signifie, cependant, pas qu’ils ne supporteront jamais d’autres langages et c’est d’ailleurs l’un des objectifs partagés par wasm-bindgen qui, à terme, ne devra plus être directement lié au langage Rust.

Sa structure

En réalité, le projet est composé de deux éléments:

  1. La crate wasm-bindgen. Elle nous apporte l’attribut modestement nommé #[wasm-bindgen] qui va servir d’interprète pour les deux partis (nous y reviendrons rapidement) ;
  2. L’outil wasm-bindgen-cli qui n’est ni plus ni moins que le programme qui va nous permettre de rendre nos métadonnées (générées par l’attribut cité plus haut) intelligibles en wasm.


Petit prix à payer en revanche, ces fonctionnalités ne sont disponibles qu’en compilant avec la nightly de Rust.

Son fonctionnement

Les présentations ainsi faites, passons à des choses plus intéressantes !

L’attribut wasm-bindgen

Du côté de notre crate, #[wasm-bindgen] servira à "marquer" les services, structures et implémentations (impl) puis préparera à l’exportation tout ce petit monde sous la forme d’un module ECMAScript, facilitant, ensuite, largement leur importation dans le code JavaScript censé les accueillir.

Nom : wasm-bindgen-architecture-current.png
Affichages : 1508
Taille : 21,0 Ko

Exporter des composants écrits en Rust vers JS

Prenons, dans un premier temps, les exemples originaux.

Côté Rust, dans lib.rs :
Code Rust : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
// Current prelude for using `wasm_bindgen`, and this'll get smaller over time!
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
 
// Here we're importing the `alert` function from the browser, using
// `#[wasm_bindgen]` to generate correct wrappers.
#[wasm_bindgen]
extern {
    fn alert(s: &str);
}
 
// Here we're exporting a function called `greet` which will display a greeting
// for `name` through a dialog.
#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

Côté JavaScript:

Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
const { greet } = wasm_bindgen;
 
function runApp() {
  greet('World');
}
 
// Load and instantiate the wasm file, and we specify the source of the wasm
// file here. Once the returned promise is resolved we're ready to go and
// use our imports.
wasm_bindgen('../out/main_bg.wasm').then(runApp).catch(console.error);

Vous pouvez remarquer à la ligne 9 que le bloc extern {}, couramment utilisé avec la FFI, dispose de la même fonction qu’en temps normal: déclarer les signatures des services que nous tentons d’utiliser à partir d’une ABI connue. Ici, nous souhaitons user de la méthode window.alert() que nous propose le navigateur.

Maintenant, nous pourrions toujours réadapter cet exemple pour illustrer ce qui a été dit juste avant: il nous est possible d’exporter des structures et l’implémentation de leurs services.

Côté Rust, dans lib.rs :

Code Rust : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#[wasm_bindgen]
extern {
    fn alert(s: &str);
}
 
#[wasm_bindgen]
pub struct Foo;
 
#[wasm_bindgen]
impl Foo {
  pub fn new() -> Foo {
    Foo
  }
  pub fn greet(&self, name: &str) {
      alert(&format!("Hello, {}!", name));
  }
}

Côté JavaScript:

Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
/*
On récupère la structure exactement comme
on pourrait le faire avec un module ECMAScript
écrit en JavaScript.
*/
const { Foo } = wasm_bindgen;
 
function runApp() {
  Foo.new().greet('World');
}
 
wasm_bindgen('../out/main_bg.wasm').then(runApp).catch(console.error);

Exporter des composants écrits en JS vers Rust

#[wasm_bindgen] dispose de trois paramètres (dont un variable) :

  1. module: renseigne le chemin à partir duquel le compilateur est censé charger le module ECMAScript ;
  2. constructor: précise que la signature, suivie par ce paramètre, représente le constructeur du prototype ;
  3. method: précise que la signature, suivie par ce paramètre, représente une méthode du prototype.


Ce trio vous permet d’importer des structures initialement écrites en JS.

Côté Rust :

Code Rust : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// On renseigne le nom du module.
#[wasm_bindgen(module = "main")]
extern {
    fn alert(s: &str);
    // Ici, on se sert du mot-clé `type`
    // pour préciser que l'on souhaite importer
    // la structure `MyJavaScriptObject`.
    type MyJavaScriptObject;
    // On définit la signature du constructeur.
    #[wasm_bindgen(constructor)]
    fn new() -> MyJavaScriptObject;
    // On définit l'identificateur et la signature
    // de la méthode d'instance.
    #[wasm_bindgen(method)]
    fn say_hello(this: &MyJavaScriptObject) -> ();
}
 
#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
    MyJavaScriptObject::new().say_hello();
}

Côté JavaScript :

Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
export class MyJavaScriptObject {
  constructor() {
    this.greetings = "Hi, I'm a JavaScript object!";
  }
 
  say_hello() {
    console.log(this.greetings);
  }
}




Mon avis

J’aimerais faire un retour qui servira de conclusion à ce petit billet, si ça peut aider certains développeurs à situer WASM/Rust en termes de maturité.

Nous avons une vision basique de ce qu’un développeur utilisant wasm est censé devoir faire pour lier JS à un module wasm. Rien de très compliqué, en somme, mais les possibilités me semblent encore limitées.

Ajoutons à cela que la compréhension même du langage n’est pas encore très bonne. Les lifetimes ne sont pas supportées, les constantes ne peuvent être exportées et les wrappers tels que Option et Result ne sont pas disponibles. En bref, y’a du boulot.
Source


Voir aussi

Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Viadeo Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Twitter Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Google Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Facebook Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Digg Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Delicious Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog MySpace Envoyer le billet « Wasm-bindgen, qu'est-ce que c'est ? » dans le blog Yahoo

Mis à jour 30/07/2018 à 12h29 par Malick

Tags: javascript, rust, wasm
Catégories
Javascript , Développement Web , Programmation

Commentaires

  1. Avatar de gb_68
    • |
    • permalink
    Merci pour ces informations.

    Juste une petite précision sur Emscripten et Binaryen ; ce sont deux outils complémentaires : Emscripten utilise Binaryen comme back-end pour générer du wasm - même si un back-end LLVM wasm est aussi prévu (le but de Binaryen est, d'après ce que j'ai compris, essentiellement d'être un ensemble d'outils/bibliothèques pour aider à réaliser des compilateurs/outils supportant wasm). C'est d'ailleurs une bonne chose que des projets modulaires pouvant se réutiliser les uns les autres apparaissent plutôt qu'une prolifération d'outils monolithiques dont les rôles se chevauchent ; car même si les acteurs majeurs du Web sont favorables à WebAssembly, c'est un projet ambitieux qui nécessitera d'importantes ressources pour arriver à terme (autant ne pas trop les gaspiller).

    Emscripten
    Citation Envoyé par Emscripten
    Emscripten’s WebAssembly support depends on Binaryen
    Binaryen
    Citation Envoyé par Binaryen
    Integrate with Emscripten in order to provide a complete compiler toolchain from C and C++ to WebAssembly.
  2. Avatar de Songbird
    • |
    • permalink
    Bonjour,

    Effectivement, y'a eu une petite méprise. Je corrigerai ça dans la journée, merci !

    Maintenant que tu le dis, ça coule de source en effet.

    En te souhaitant une bonne journée !