Maison > développement back-end > Tutoriel Python > Construire pour WebAssembly

Construire pour WebAssembly

Mary-Kate Olsen
Libérer: 2024-12-02 01:26:09
original
321 Les gens l'ont consulté

Building for WebAssembly

J'explore actuellement deux sujets intéressants pour Memphis, mon interpréteur Python dans Rust : la construction pour WebAssembly et l'intégration de CPython. En l’absence d’étapes majeures à signaler cette semaine, j’ai pensé partager quelques réflexions en cours. Pour moi, Memphis est un projet visant à élargir ma compréhension conceptuelle à travers des expériences pratiques. J'espère que cet article pourra faire la même chose pour vous alors que nous passons en revue certaines des décisions de conception que j'explore.

Python dans le navigateur

Compiler Memphis vers une cible WebAssembly me trottait dans la tête depuis un certain temps, et il y a deux samedis, j'ai finalement essayé. Avec une tasse tiède de café filtre sur mon dessous de verre, je me suis cassé les jointures et j'ai commencé.

WebAssembly est un environnement d'exécution en bac à sable dans les navigateurs Web modernes qui complète l'environnement JavaScript traditionnel. L'environnement Wasm est plus proche du code natif et peut être utilisé pour des tâches bénéficiant d'un contexte CPU plus performant ; pensez aux calculs de chiffres ou aux boucles idiotes et chargées. Cela m’intéressait moins du point de vue de la performance que parce que c’était possible. L’un des arguments de vente de Rust (sur littéralement des bajillions) est qu’il peut cibler Wasm. Comment faire, pourrait-on se demander ? Cela est possible car Rust utilise LLVM comme backend de compilateur. L'interface du compilateur Rust produit du code de représentation intermédiaire (IR) LLVM et LLVM peut le compiler en code natif pour des dizaines de cibles.

C'est un avantage assez énorme et j'étais curieux de savoir si cela fonctionnerait simplement pour Memphis. Je n'avais littéralement aucunement pensé à exécuter Python dans le navigateur auparavant, cela me semblait donc être l'occasion idéale de tester la courbe d'apprentissage de Wasm.

Configuration de wasm-pack et construction pour WebAssembly

J'ai lancé mon assistant IA et demandé la séquence de lancement. Ça a fait bip boop bip boop. Vous trouverez ci-dessous les étapes annotées de mes apprentissages tout au long du parcours.

# wasm-pack helps compile our Rust code to WebAssembly and bundle it
# with JavaScript bindings we can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via
# rustup for us. If for whatever reason it does not, you can use this: 
# rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is
# behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
Copier après la connexion
Copier après la connexion
Copier après la connexion

Le build a réussi dès mon premier essai ! Cependant, comme nous n’avons marqué aucune fonction dans notre binaire Rust comme pouvant être appelée depuis WebAssembly, cela ne fait pas grand-chose.

Nous pouvons installer la caisse wasm-bindgen pour ce faire, que j'ai placée derrière un indicateur de fonctionnalité. J'ai ajouté ceci à mon Cargo.toml.

[dependencies]
wasm-bindgen = { version = "0.2", optional = true }

[features]
wasm = ["wasm-bindgen"]
Copier après la connexion
Copier après la connexion

Voici un petit morceau de code que j'ai ajouté à mon fichier src/lib.rs, derrière l'indicateur de fonctionnalité wasm. La fonction greet est décorée de #[wasm_bindgen] pour rendre ce symbole disponible en JavaScript.

# wasm-pack helps compile our Rust code to WebAssembly and bundle it
# with JavaScript bindings we can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via
# rustup for us. If for whatever reason it does not, you can use this: 
# rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is
# behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
Copier après la connexion
Copier après la connexion
Copier après la connexion

Création d'une interface JavaScript

J'ai également demandé à mon assistant IA le plus petit morceau de JavaScript possible que je pourrais utiliser pour tester mon interface Wasm. Lorsque nous appelons init(), le navigateur charge le fichier .wasm, effectue une étape de compilation JIT pour convertir le binaire WebAssembly portable en code natif et initialise la mémoire pour le runtime WebAssembly.

[dependencies]
wasm-bindgen = { version = "0.2", optional = true }

[features]
wasm = ["wasm-bindgen"]
Copier après la connexion
Copier après la connexion

Comme un miracle parmi les miracles, ça a juste fonctionné. Certes, je n'exécutais aucun code Python dans le navigateur, mais l'interface avec mon binaire était une étape ÉNORME que le jeune moi qui pouvait à peine installer Java ne voulait pas sous-évaluer.

L'étape suivante consistait à lui donner une expression Python définie en JavaScript et à laisser le binaire Wasm analyser les chiffres. Comme je l'ai mentionné dans mon article REPL, chaque point d'entrée dans un projet logiciel est une opportunité d'améliorer mes abstractions, et ce serait certainement encore le cas ici. En feuilletant mon dépôt Memphis, j'ai réalisé Wow, je devrais vraiment avoir une meilleure interface pour transmettre une chaîne et l'évaluer en tant que Python. Comme je l'ai dit, j'ADORE les nouveaux points d'entrée.

Pour le moment, j'utiliserais mon adaptateur crosscheck. Crosscheck est mon framework de test en cours pour valider que l'interpréteur Treewalk et la VM bytecode produisent le même comportement pour une entrée Python donnée. Il porte le nom de ce que font les agents de bord.

Voici mon code Rust mis à jour.

#[cfg(feature = "wasm")]
mod wasm {
    use wasm_bindgen::prelude::wasm_bindgen;

    // Export a function to JavaScript
    #[wasm_bindgen]
    pub fn greet() -> String {
        "Hello from WebAssembly!".to_string()
    }
}
Copier après la connexion

Voici mon code JavaScript mis à jour, qui invoque la nouvelle fonction d'évaluation de Rust.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Wasm Test</title>
</head>
<body>
    <script type="module">
        import init, { greet } from './pkg/memphis.js';

        async function run() {
            await init();
            console.log(greet());
        }

        run();
    </script>
</body>
</html>
Copier après la connexion

Débogage des erreurs WebAssembly

Maintenant, lorsque je l'ai exécuté, j'ai eu……… une erreur de console. Il s'est écrasé avec une erreur non implémentée.

J'ai fouillé un peu et je ne savais pas exactement ce qui causait cela. Vous pouvez cliquer sur la source mais pour une version Wasm qui n'est qu'un bloc d'assemblage sans référence aux fonctions Rust d'origine.

J'ai discuté avec l'IA/recherché sur Google et j'ai trouvé deux approches utiles. L'un d'eux est console_log à utiliser dans les builds Wasm, qui affiche les instructions de journal de votre code Rust dans la console de votre navigateur. Cela en a aidé certains, mais ce que je cherchais vraiment, c'était une trace de pile. Entrez console_error_panic_hook. Cela m'a immédiatement donné la trace de la pile Rust, qui était CLUTCH. Si vous réalisez votre propre build Wasm, arrêtez de lire ceci maintenant et ajoutez cette caisse. Cela ne me dérange même pas si vous ne finissez jamais de lire cet article. Ferris voudrait que vous utilisiez cette caisse ?. Voici comment je l'ai ajouté à mon interface Wasm.

#[cfg(feature = "wasm")]
mod wasm {
    use wasm_bindgen::prelude::wasm_bindgen;

    use crosscheck::{InterpreterTest, TreewalkAdapter};

    // Export a function to JavaScript
    #[wasm_bindgen]
    pub fn greet() -> String {
        "Hello from WebAssembly!".to_string()
    }

    #[wasm_bindgen]
    pub fn evaluate(code: String) -> String {
        let result = TreewalkAdapter.execute(&code);
        format!("{}", result)
    }
}
Copier après la connexion

Ma trace de pile m'a indiqué mon coupable : j'utilisais std::env pour demander certaines ressources du système d'exploitation, qui ne sont pas autorisées dans un runtime Wasm (c'est la partie en bac à sable). J'ai placé ces appels derrière un indicateur de fonctionnalité (ils sont liés à la façon dont je détermine de manière hackée l'emplacement de la bibliothèque standard Python sur la machine hôte) et j'ai relancé ma construction. Après quelques petits échecs liés au bon affichage de mes types de retours….

ÇA A MARCHE. Voici ce que je vois maintenant dans la console de mon navigateur.

# wasm-pack helps compile our Rust code to WebAssembly and bundle it
# with JavaScript bindings we can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via
# rustup for us. If for whatever reason it does not, you can use this: 
# rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is
# behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
Copier après la connexion
Copier après la connexion
Copier après la connexion

tldr Je peux exécuter Python dans le navigateur. (À leur honneur, RustPython fait cela aussi : https://rustpython.github.io/demo/. Je n'ai pas examiné en profondeur leur projet mais il semble complet.) La compréhension de la liste Python est définie en JavaScript sous forme de chaîne et la liste de réponses est évaluée par le code Rust compilé dans Wasm et reconvertie en une chaîne pouvant être affichée par JavaScript.

Cette configuration ne prend en charge que les expressions pour le moment. Pour évaluer les déclarations (et relire plus tard leurs résultats), je devrai conserver l'état du côté Rust. Je rêve aussi de créer un REPL JavaScript. Cela ressemble à un problème pour mon futur (et à un rêve ennuyeux, à vrai dire).

La fin

J'ai parlé assez longtemps, donc je vais attendre lundi prochain pour discuter de Python embarqué.

Désolé pour l'appât et le changement. Le calendrier de contenu n'attend personne.

Pour être clair, par Python intégré, j'entends intégrer un interpréteur CPython à l'intérieur de Memphis, et non exécuter Python dans un environnement de « systèmes embarqués ». Ce serait difficile sans raison. Contrairement à Memphis, qui est difficile pour FUN.


Si vous souhaitez recevoir plus de messages comme celui-ci directement dans votre boîte de réception, vous pouvez vous abonner ici !

Autre part

En plus d'encadrer des ingénieurs logiciels, j'écris également sur mon expérience en tant que personne autiste diagnostiquée chez l'adulte. Moins de code et le même nombre de blagues.

  • Pourquoi ai-je soif de reconnaissance ? - Depuis l'organisation Scratch dot

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal