WebAssembly - Szybki przewodnik
WebAssembly to nowy język programowania komputerów w sieci. Kod WebAssembly jest formatem binarnym niskiego poziomu, który jest kompatybilny z siecią i można go łatwo uruchomić w nowoczesnych przeglądarkach internetowych. Wygenerowany plik jest mały i ładuje się i wykonuje szybciej. Możesz teraz kompilować języki takie jak C, C ++, Rust itp. Do formatu binarnego i może działać w Internecie tak jak javascript.
Definicja WebAssembly
Zgodnie z oficjalną stroną internetową WebAssembly, która jest dostępna pod adresem https://webassembly.org/, jest zdefiniowany jako WebAssembly (w skrócie Wasm) to binarny format instrukcji dla maszyny wirtualnej opartej na stosie. Wasm został zaprojektowany jako przenośny cel kompilacji języków wysokiego poziomu, takich jak C / C ++ / Rust, umożliwiając wdrażanie w Internecie dla aplikacji klienckich i serwerowych.
Web Assembly nie jest czymś, co programista będzie musiał napisać, ale kod jest napisany w językach takich jak C, C ++, Rust i można go skompilować do WebAssembly (wasm). Ten sam kod można uruchomić w przeglądarkach internetowych.
Web Assembly to nowy język, kod jest niskopoziomowym językiem asemblera, ale dzięki funkcji formatu tekstowego kod jest czytelny i możliwe jest debugowanie, jeśli to konieczne.
Cele WebAssembly
Otwarte standardy dla WebAssembly są rozwijane w grupie społeczności W3C, która obejmuje przedstawicieli wszystkich głównych przeglądarek, a także grupę roboczą W3C.
Główne cele WebAssembly są wymienione poniżej -
Faster, Efficient and Portable - Kod WebAssembly ma działać szybciej na różnych platformach, wykorzystując dostępny sprzęt.
Easy to read and debug - WebAssembly, będący językiem asemblera niskiego poziomu, obsługuje format tekstowy, który umożliwia debugowanie kodu pod kątem wszelkich problemów, a także przepisanie kodu, jeśli to konieczne.
Security - WebAssembly jest bezpieczna do uruchomienia w przeglądarkach internetowych, ponieważ dba o uprawnienia i zasady tego samego pochodzenia.
Zalety WebAssembly
Oto zalety WebAssembly -
Run is Modern Browsers - WebAssembly może działać bez żadnych problemów w nowoczesnych przeglądarkach internetowych, które są dostępne.
Multiple Language support- Języki takie jak C, C ++, Rust, Go mogą teraz skompilować kod do WebAssembly i uruchomić to samo w przeglądarkach internetowych. Tak więc języki, które nie były w stanie działać w przeglądarce, będą teraz mogły to robić.
Faster, Efficient and Portable - Ze względu na mały rozmiar kodu ładuje się i wykonuje szybciej.
Easy to understand- Programiści nie muszą przykładać wiele wysiłku do zrozumienia kodowania WebAssembly, ponieważ nie muszą pisać kodu w WebAssembly. Zamiast tego skompiluj kod w WebAssembly i wykonaj to samo w sieci Web.
Easy to Debug - Chociaż końcowy kod jest w języku asemblera niskiego poziomu, można go również pobrać w formacie tekstowym, który jest łatwy do odczytania i debugowania.
Wady WebAssembly
Oto wady WebAssembly -
Wciąż trwają prace nad WebAssembly i jest za wcześnie, aby decydować o jego przyszłości.
WebAssembly jest zależna od javascript do interakcji z Document Object Model (DOM).
WebAssembly jest również nazywana WASM, która została wprowadzona po raz pierwszy w roku 2017. Wielkie firmy technologiczne stojące za powstaniem WebAssembly to Google, Apple, Microsoft, Mozilla i W3C.
Brzęczenie polega na tym, że WebAssembly zastąpi JavaScript ze względu na jego szybsze wykonanie, ale tak nie jest. WebAssembly i Javascript mają współpracować przy rozwiązywaniu złożonych problemów.
Potrzeba WebAssembly
Jak dotąd mamy tylko Javascript, który może z powodzeniem działać w przeglądarce. Istnieją bardzo ciężkie zadania, które są trudne do wykonania w przeglądarkach za pomocą javascript.
Są to między innymi: rozpoznawanie obrazu, aplikacje do projektowania wspomaganego komputerowo (CAD), powiększanie wideo na żywo, rzeczywistość wirtualna i rozszerzona, aplikacje muzyczne, wizualizacja i symulacja naukowa, gry, edycja obrazu / wideo itp.
WebAssembly to nowy język z instrukcjami binarnymi, które mogą ładować się i wykonywać szybciej. Zadanie opisane powyżej można łatwo wykonać w językach wysokiego poziomu, takich jak C, C ++, Rust itp. Potrzebujemy sposobu, aby kod, który mamy w C, C ++, Rust mógł być kompilowany i używać go w przeglądarkach internetowych. To samo można osiągnąć za pomocą WebAssembly.
Gdy kod WebAssembly jest ładowany w przeglądarce. Następnie przeglądarka zajmuje się konwersją do formatu maszynowego, który jest zrozumiały dla procesorów.
W przypadku javascript kod należy pobrać, przeanalizować i przekonwertować do formatu maszynowego. Zajmuje to dużo czasu, a ciężkie zadania, takie jak wspomniane wcześniej, mogą być bardzo powolne.
Działanie WebAssembly
Języki wysokiego poziomu, takie jak C, C ++ i Rust, są kompilowane do formatu binarnego, to znaczy .wasm i format tekstu .wat.
Kod źródłowy napisany w C, C ++ i Rust jest kompilowany do .wasmza pomocą kompilatora. Możesz skorzystać z Emscripten SDK do kompilowania C / C ++ do.wasm.
Przepływ jest następujący -
Kod C / C ++ można skompilować do .wasmużywając Emscripten SDK. Później.wasm kodu można użyć za pomocą javascript w pliku html, aby wyświetlić dane wyjściowe.
Kluczowe pojęcia dotyczące WebAssembly
Kluczowe pojęcia są wyjaśnione poniżej -
Moduł
Moduł to obiekt, który jest kompilowany przez przeglądarkę do wykonywalnego kodu maszynowego. Mówi się, że moduł jest bezstanowy i może być współużytkowany między systemem Windows i pracownikami sieciowymi.
Pamięć
Pamięć w WebAssembly to plik arraybufferktóry przechowuje dane. Pamięć można przydzielić za pomocą JavaScript API WebAssembly.memory ().
Stół
Tabela w WebAssembly to tablica o typie strukturalnym, która znajduje się poza pamięcią WebAssembly i najczęściej zawiera odniesienie do funkcji. Przechowuje adres pamięci funkcji.
Instancja
Instancja to obiekt, który będzie miał wszystkie wyeksportowane funkcje, które można wywołać z javascript w celu wykonania w przeglądarce.
WebAssembly jest również nazywana wasm, co jest ulepszeniem Javascript. Został zaprojektowany do działania w przeglądarkach, tak jak javascript, a także z nodejs. Zdarza się, że otrzymujesz wyjście wasm, gdy kompilowany jest dowolny język wysokiego poziomu, taki jak C, C ++, Rust.
Rozważmy następujący program w C -
int factorial(int n) {
if (n == 0)
return 1;
else
return n * factorial(n-1);
}
Skorzystaj z programu WasmExplorer, który jest dostępny pod adresemhttps://mbebenita.github.io/WasmExplorer/ aby uzyskać skompilowany kod, jak pokazano poniżej -
Format tekstowy WebAssembly dla programu silnia jest taki, jak podano poniżej -
(module
(table 0 anyfunc)
(memory $0 1) (export "memory" (memory $0)) (export "factorial" (func $factorial)) (func $factorial (; 0 ;) (param $0 i32) (result i32) (local $1 i32)
(local $2 i32) (block $label$0 (br_if $label$0 (i32.eqz (get_local $0)
)
)
(set_local $2 (i32.const 1) ) (loop $label$1 (set_local $2
(i32.mul
(get_local $0) (get_local $2)
)
)
(set_local $0 (tee_local $1 (i32.add
(get_local $0) (i32.const -1) ) ) ) (br_if $label$1 (get_local $1)
)
)
(return
(get_local $2)
)
)
(i32.const 1)
)
)
Korzystając z narzędzia Wat2Wasm, możesz wyświetlić kod WASM, tak jak wspomniano poniżej -
Programiści nie powinni pisać kodu w wasm ani uczyć się go kodować, ponieważ jest on generowany głównie podczas kompilacji języków wysokiego poziomu.
Model maszyny stosowej
W WASM wszystkie instrukcje są umieszczane na stosie. Argumenty są usuwane, a wynik jest odkładany z powrotem na stos.
Rozważmy następujący format tekstowy WebAssembly, który dodaje 2 liczby -
(module
(func $add (param $a i32) (param $b i32) (result i32)
get_local $a get_local $b
i32.add
)
(export "add" (func $add))
)
Nazwa funkcji to $addprzyjmuje 2 parametry $a and $b. Wynikiem jest 32-bitowa liczba całkowita typu. Dostęp do zmiennych lokalnych uzyskuje się za pomocą funkcji get_local, a operacja dodawania jest wykonywana za pomocą i32.add.
Reprezentacja stosu do dodania 2 liczb podczas wykonywania będzie następująca -
W step 1 - Wykonanie get_local $a instruction, the first parameters i.e., $a jest odkładany na stosie.
W step 2 - Podczas wykonywania get_local $b instruction, the second parameters i.e., $b jest umieszczany na stosie.
W step 3- Wykonanie i32.add zdejmuje elementy ze stosu i odkłada wynik z powrotem na stos. Wartość, która pozostaje na końcu wewnątrz stosu, jest wynikiem działania funkcji $ add.
W tym rozdziale dowiesz się, jak zainstalować pakiet Emscripten SDK, aby skompilować C / C ++. Emscripten to maszyna wirtualna niskiego poziomu (LLVM), która pobiera kod bajtowy wygenerowany z C / C ++ i kompiluje go do JavaScript, który można łatwo wykonać w przeglądarce.
Aby skompilować C / C ++ do WebAssembly, musimy najpierw zainstalować pakiet Emscripten sdk.
Zainstaluj pakiet Emscripten sdk
Kroki instalacji Emscripten sdk są następujące -
Step 1 - Sklonuj repozytorium emsdk: klon git https://github.com/emscripten-core/emsdk.git.
E:\wa>git clone https://github.com/emscripten-core/emsdk.git
Cloning into 'emsdk'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 1823 (delta 4), reused 4 (delta 2), pack-reused 1809 receiving obje
cts: 99% (1819/1823), 924.01 KiB | 257.00 KiB/s
Receiving objects: 100% (1823/1823), 1.01 MiB | 257.00 KiB/s, done.
Resolving deltas: 100% (1152/1152), done.
Step 2 - Wejdź do katalogu emsdk.
cd emsdk
Step 3 - W przypadku systemu Windows: wykonaj następujące polecenie.
emsdk install latest
For linux, to polecenie zajmie trochę czasu, aby zainstalować niezbędne narzędzia, takie jak java, python itp. Postępuj zgodnie z poniższym kodem -
./emsdk install latest
Step 4 - Aby aktywować najnowszy SDK, wykonaj następujące polecenie w terminalu.
For windows, wykonaj następujące polecenie -
emsdk activate latest
For linux, wykonaj poniższe polecenie -
./emsdk activate latest
Step 5 - Aby aktywować PATH i inne zmienne środowiskowe, uruchom następujące polecenie w swoim terminalu.
For windows, wykonaj polecenie -
emsdk_env.bat
For linux, wykonaj następujące polecenie -
source ./emsdk_env.sh
Skończyliśmy instalować emsdk i możemy teraz skompilować kod w C lub C ++. Kompilacja C / C ++ zostanie wykonana w następnych rozdziałach.
Aby skompilować dowolny kod w C lub C ++, wykonaj polecenie -
emcc source.c or source.cpp -s WASM=1 -o source.html
W wyniku otrzymasz plik source.html, pliki source.js i source.wasm. Js będzie miał interfejs API, który pobierze plik source.wasm, a wyjście będzie można zobaczyć po naciśnięciu source.html w przeglądarce.
Aby po prostu pobrać plik wasm, możesz użyć następującego polecenia. To polecenie da ci tylko plik source.wasm.
emcc source.c or source.cpp -s STANDALONE_WASM
W tym rozdziale omówimy kilka łatwych w użyciu narzędzi, które są bardzo pomocne podczas pracy z WebAssembly. Zacznijmy od poznania narzędzia WebAssembly.studio.
WebAssembly.studio
To narzędzie pozwala skompilować C, Rust, Wat to Wasm itp.
Aby rozpocząć, możesz kliknąć Empty C Project, Empty Rust Project, Empty Wat Project, aby skompilować C i Rust do WASM. 5.
Ma Build, Run, aby zbudować kod i sprawdzić dane wyjściowe. Przycisk pobierania umożliwia pobieranie.wasmplik, który można wykorzystać do przetestowania w przeglądarce. To narzędzie jest bardzo pomocne w kompilowaniu kodu C i Rusta oraz sprawdzaniu wyników.
Eksplorator WebAssembly
Eksplorator WebAssembly umożliwia kompilację kodu w językach C i C ++. Skorzystaj z linkuhttps://mbebenita.github.io/WasmExplorer/po więcej szczegółów. Ekran, który pojawi się po kliknięciu linku, jest pokazany poniżej -
Możesz wybrać wersję C i C ++. Kod źródłowy C lub C ++ jest napisany tutaj -
Po kliknięciu przycisku Kompiluj, daje on format tekstowy WebAssembly (WAT) i kod zespołu Firefox x86 w blokach poniżej -
Możesz pobrać .wasm kod, aby przetestować go w przeglądarce.
WASMFiddle
Wasmfiddle pomaga skompilować kod C do WebAssembly, a także przetestować wynik. Po kliknięciu w linkhttps://wasdk.github.io/WasmFiddle/, zobaczysz następującą stronę -
Kliknij Build, aby skompilować kod. Możesz pobrać kod Wat and Wasm, klikając Wat and Wasm. Aby przetestować wyjście, kliknij przycisk Uruchom.
WASM do WAT
Narzędzie wat2wasmpoda kod WAS, gdy wprowadzisz format tekstowy WebAssembly. Możesz kliknąć łączehttps://webassembly.github.io/wabt/demo/wat2wasm/ dla wersji demonstracyjnej, a ekran, który się pojawi, znajduje się poniżej -
Możesz użyć przycisku przesyłania, aby przesłać plik .wasm, a obszar tekstowy wyświetli format tekstowy.
WAT na WASM
Narzędzie wat2wasm poda kod WASM po wprowadzeniu formatu tekstowego WebAssembly. Możesz kliknąć łączehttps://webassembly.github.io/wabt/demo/wat2wasm/ dla wersji demonstracyjnej, a ekran, który się pojawi, znajduje się poniżej -
To narzędzie jest bardzo pomocne, ponieważ pomaga przetestować również wyniki. Możesz wprowadzić kod WAT i spojrzeć na kod .wasm, a także wykonać kod, aby zobaczyć dane wyjściowe.
WebAssembly, zwany również WASM, to niskopoziomowy kod binarny opracowany do wykonywania w przeglądarkach w najbardziej efektywny sposób. Kod WebAssembly jest zbudowany z następujących pojęć -
- Values
- Types
- Instructions
Poznajmy je teraz szczegółowo.
Wartości
Wartości w WebAssembly służą do przechowywania złożonych danych, takich jak tekst, ciągi znaków i wektory. WebAssembly obsługuje następujące -
- Bytes
- Integers
- Punkt zmiennoprzecinkowy
- Names
Bajty
Bajty to najprostsza forma wartości obsługiwana w WebAssembly. Wartość jest w formacie szesnastkowym.
For exampleBajty reprezentowane jako b mogą również przyjmować liczby naturalne n, gdzie n <256.
byte ::= 0x00| .... |0xFF
Liczby całkowite
W WebAssembly obsługiwane liczby całkowite są takie, jak podano poniżej -
- i32: 32-bitowa liczba całkowita
- i64: 64-bitowa liczba całkowita
Punkt zmiennoprzecinkowy
W programie WebAssembly obsługiwane liczby zmiennoprzecinkowe są następujące -
- f32: 32-bitowy zmiennoprzecinkowy
- f64: 64-bitowy zmiennoprzecinkowy
Nazwy
Nazwy to sekwencje znaków, z wartościami skalarnymi zdefiniowanymi przez Unicode, który jest dostępny pod linkiem http://www.unicode.org/versions/Unicode12.1.0/ podane w niniejszym dokumencie.
Rodzaje
Jednostki w WebAssembly są klasyfikowane jako typy. Obsługiwane typy są takie, jak podano poniżej -
- Typy wartości
- Typy wyników
- Typy funkcji
- Limits
- Rodzaje pamięci
- Typy tabel
- Typy globalne
- Typy zewnętrzne
Przeanalizujmy je jeden po drugim.
Typy wartości
Typ wartości obsługiwany przez WebAssembly jest taki, jak wspomniano poniżej -
- i32: 32-bitowa liczba całkowita
- i64: 64-bitowa liczba całkowita
- f32: 32-bitowy zmiennoprzecinkowy
- f64: 64-bitowy zmiennoprzecinkowy
valtype ::= i32|i64|f32|f64
Typy wyników
Wartości zapisane w nawiasach są wykonywane i przechowywane w typach wyników. Typ wyniku to wynik wykonania bloku kodu składającego się z wartości.
resulttype::=[valtype?]
Typy funkcji
Typ funkcji przyjmuje wektor parametrów i zwraca wektor wyników.
functype::=[vec(valtype)]--> [vec(valtype)]
Limity
Limity to zakres pamięci związany z pamięcią i typami tabel.
limits ::= {min u32, max u32}
Rodzaje pamięci
Typy pamięci dotyczą pamięci liniowych i zakresu rozmiarów.
memtype ::= limits
Typy tabel
Typy tabel są klasyfikowane według przypisanego typu elementu.
tabletype ::= limits elemtype
elemtype ::= funcref
Typ tabeli zależy od limitu minimalnego i maksymalnego rozmiaru przypisanego do niej.
Typy globalne
Typ globalny zawiera zmienne globalne, które mają wartość, która może się zmienić lub pozostać taka sama.
globaltype ::= mut valtype
mut ::= const|var
Typy zewnętrzne
Typy zewnętrzne dotyczą importu i wartości zewnętrznych.
externtype ::= func functype | table tabletype | mem memtype | global globaltype
Instrukcje
Kod WebAssembly to sekwencja instrukcji zgodnych z modelem maszyny stosowej. Ponieważ WebAssembly jest zgodna z modelem maszyny stosu, instrukcje są umieszczane na stosie.
Na przykład wartości argumentów funkcji są zdejmowane ze stosu, a wynik jest odkładany z powrotem na stos. Ostatecznie na stosie będzie tylko jedna wartość i to jest wynik.
Niektóre z powszechnie używanych instrukcji są następujące -
- Instrukcje numeryczne
- Zmienne instrukcje
Instrukcje numeryczne
Instrukcje numeryczne to operacje, które są wykonywane na wartości liczbowej.
For examplenn, mm ::= 32|64
ibinop ::= add|sub|mul|div_sx|rem_sx|and|or|xor
irelop ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx
frelop ::= eq | ne | lt | gt | le | ge
Zmienne instrukcje
Instrukcje dotyczące zmiennych dotyczą dostępu do zmiennych lokalnych i globalnych.
For example
Aby uzyskać dostęp do zmiennych lokalnych -
get_local $a
get_local $b
Do set zmienne lokalne -
set_local $a
set_local $b
Do access zmienne globalne -
get_global $a
get_global $b
Do set zmienne globalne -
set_global $a
set_global $b
Ten rozdział zawiera porównanie między WebAssembly i Javascriptem.
Javascript to język, z którego często korzystaliśmy w przeglądarce. Teraz, dzięki wydaniu WebAssembly możemy również używać WebAssembly wewnątrz przeglądarki.
Powodem powstania WebAssembly nie jest zastąpienie javascript, ale zajęcie się pewnymi rzeczami, które są trudne do obsłużenia przy pomocy javascript.
For example
Zadania takie jak rozpoznawanie obrazu, aplikacje CAD, powiększanie wideo na żywo, rzeczywistość wirtualna i rozszerzona rzeczywistość, aplikacje muzyczne, wizualizacja i symulacja naukowa, gry, edycja obrazu / wideo itp. Są trudne do wykonania za pomocą javascript.
Używając języków wysokiego poziomu, takich jak C / C ++, Rust, które teraz można skompilować do WebAssembly, łatwo jest wykonać powyższe zadanie. WebAssembly generuje kod binarny, który można łatwo wykonać w przeglądarce.
Oto lista porównań wykonanych między Javascriptem a WebAssembly.
Parametry | Javascript | WebAssembly |
---|---|---|
Kodowanie |
Możesz łatwo pisać kod w JavaScript. Napisany kod jest czytelny dla człowieka i zapisany jako .js. W przypadku korzystania z przeglądarki należy użyć tagu <script>. |
Kod można zapisać w formacie tekstowym w WebAssembly i zapisany jako .wat. Trudno jest napisać kod w formacie .wat. Najlepiej jest skompilować kod z innego języka wysokiego poziomu zamiast pisać od początku w formacie .wat. Nie możesz uruchomić pliku .wat w przeglądarce i musisz przekonwertować go do .wasm za pomocą kompilatorów lub dostępnych narzędzi online. |
Wykonanie |
Kod napisany w javascript używany w przeglądarce musi zostać pobrany, przeanalizowany, skompilowany i zoptymalizowany. |
Mamy już skompilowany kod WebAssembly w formacie .wasm w formacie binarnym. |
Zarządzanie pamięcią |
Javascript przypisuje pamięć, gdy tworzone są zmienne, a pamięć jest zwalniana, gdy nie jest używana i jest dodawana do czyszczenia pamięci. |
Pamięć w WebAssembly to bufor tablicy, który przechowuje dane. Pamięć można przydzielić za pomocą interfejsu API JavaScript WebAssembly.memory (). Pamięć WebAssembly jest przechowywana w formacie tablicowym, czyli płaskim modelu pamięci, który jest łatwy do zrozumienia i wykonania. Wadą modelu pamięci w WebAssembly jest -
|
Czas ładowania i wydajność |
W przypadku javascript, wywołanego wewnątrz przeglądarki, plik javascript musi zostać pobrany i przeanalizowany. Później parser konwertuje kod źródłowy na kod bajtowy, w którym silnik javascript wykonuje kod w przeglądarce. Silnik Javascript jest bardzo potężny, dzięki czemu czas ładowania i wydajność javascript są bardzo szybkie w porównaniu z WebAssembly. |
Najważniejszym celem WebAssembly jest szybsze działanie niż JavaScript, kod generowany z języków wysokiego poziomu ma mniejszy rozmiar, a co za tym idzie, czas ładowania jest szybszy. Jednak języki takie jak GO po skompilowaniu do wasm generują duży rozmiar pliku dla małego fragmentu kodu. WebAssembly został zaprojektowany w taki sposób, aby był szybszy w kompilacji i mógł działać we wszystkich głównych przeglądarkach. WebAssembly wciąż musi dodać wiele ulepszeń w zakresie wydajności w porównaniu do javascript. |
Debugowanie |
JavaScript jest czytelny dla człowieka i można go łatwo debugować. Dodanie punktów przerwania do kodu javascript w przeglądarce umożliwia łatwe debugowanie kodu. |
WebAssembly dostarcza kod w formacie tekstowym, który jest czytelny, ale nadal bardzo trudny do debugowania. Firefox umożliwia przeglądanie kodu WAS w formacie .wat w przeglądarce. Nie możesz dodawać punktów przerwania w .wat i jest to coś, co będzie dostępne w przyszłości. |
Wsparcie przeglądarki |
JavaScript działa dobrze we wszystkich przeglądarkach. |
Wszystkie główne przeglądarki internetowe obsługują WebAssembly. |
W tym rozdziale zrozumiemy, jak załadować kod wasm i uruchomić go w przeglądarce za pomocą JavaScript webassembly API.
Oto kilka ważnych interfejsów API, które zamierzamy wykorzystać w tym samouczku do wykonania kodu wasm.
- fetch () Interfejs API przeglądarki
- WebAssembly.compile
- WebAssembly.instance
- WebAssembly.instantiate
- WebAssembly.instantiateStreaming
Zanim omówimy API javascript WebAssembly, aby przetestować API i dane wyjściowe, użyjemy następującego programu w C i kodu .wasm wygenerowanego z programu c przy użyciu eksploratora wasm.
Przykład programu w języku C jest następujący -
#include<stdio.h>
int square(int n) {
return n*n;
}
Skorzystamy z eksploratora WASM, aby uzyskać kod WASM -
Pobierz kod WASM i użyj go do przetestowania API.
fetch () Interfejs API przeglądarki
Funkcja fetch () służy do ładowania zasobu sieciowego .wasm.
<script>
var result = fetch("findsquare.wasm");
console.log(result);
</script>
Zwraca obietnicę, jak pokazano poniżej -
Możesz również skorzystać z metody XMLHttpRequest, aby pobrać zasób sieciowy wasm.
WebAssembly.compile ()
Obowiązkiem API jest kompilacja szczegółów modułu, które są pobierane z .wasm.
Składnia
Składnia jest taka, jak podano poniżej -
WebAssembly.compile(buffer);
Parametry
Buffer - Ten kod z .wasm musi zostać przekonwertowany na tablicę o typie strukturalnym lub bufor tablicy przed podaniem jako dane wejściowe do kompilacji.
Wartość zwracana
Zwróci obietnicę, która będzie miała skompilowany moduł.
Przykład
Zobaczmy jeden przykład, który przedstawia dane wyjściowe jako skompilowany moduł przy użyciu metody webAssembly.compile ().
<script>
fetch("findsquare.wasm") .then(bytes => bytes.arrayBuffer())
.then(mod => {
var compiledmod = WebAssembly.compile(mod);
compiledmod.then(test=> {
console.log(test);
})
})
</script>
Wynik
Console.log po zaznaczeniu w przeglądarce poda skompilowane dane modułu -
Moduł ma obiekt konstruktora z importami, eksportami i niestandardowymi sekcjami. Zobaczmy następne API, aby uzyskać więcej informacji o skompilowanym module.
WebAssembly.instance
Korzystając z WebAssembly.instance, interfejs API zapewni wykonywalne wystąpienie skompilowanego modułu, które można dalej wykonać w celu uzyskania danych wyjściowych.
Składnia
Składnia jest taka, jak podano poniżej -
new WebAssembly.Instance(compiled module)
Wartość zwracana
Wartość zwracana będzie obiektem z tablicą funkcji eksportu, która może zostać wykonana.
Przykład
<script>
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)).then(module => {
let instance = new WebAssembly.Instance(module);
console.log(instance);
})
</script>
Wynik
Wynik da nam tablicę funkcji eksportu, jak pokazano poniżej -
Możesz zobaczyć funkcję square, którą otrzymaliśmy z kodu C, który został skompilowany.
Aby wykonać funkcję square, możesz wykonać następujące czynności -
<script>
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {
let instance = new WebAssembly.Instance(module);
console.log(instance.exports.square(15));
})
</script>
Wynik będzie -
225
WebAssembly.instantiate
To API zajmuje się kompilacją i tworzeniem instancji modułu razem.
Składnia
Składnia jest następująca -
WebAssembly.instantiate(arraybuffer, importObject)
Parametry
arraybuffer - Kod z .wasm musi zostać przekonwertowany na tablicę o typie strukturalnym lub bufor tablicy przed podaniem jako dane wejściowe do utworzenia instancji.
importObject- Obiekt importu musi mieć szczegóły pamięci, zaimportowane funkcje, które mają być użyte wewnątrz modułu. Może to być pusty obiekt modułu, na wypadek gdyby nie było nic do udostępnienia.
Wartość zwracana
Zwróci obietnicę, która będzie zawierać szczegóły modułu i instancji.
Przykład
<script type="text/javascript">
const importObj = {
module: {}
};
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode); console.log(finalcode.instance.exports.square(25));
});
</script>
Wynik
Po wykonaniu kodu otrzymasz poniższe dane wyjściowe.
WebAssembly.instantiateStreaming
Ten interfejs API zajmuje się kompilacją, a także tworzeniem wystąpienia modułu WebAssembly z podanego kodu .wasm.
Składnia
Składnia jest taka, jak podano poniżej -
WebAssembly.instantiateStreaming(wasmcode, importObject);
Parametry
wasmcode - Odpowiedź z pobierania lub innego interfejsu API, która podaje kod WAS i zwraca obietnicę.
importObject- Obiekt importu musi mieć szczegóły pamięci, zaimportowane funkcje, które mają być użyte wewnątrz modułu. Może to być pusty obiekt modułu na wypadek, gdyby nie było nic do udostępnienia.
Wartość zwracana
Zwróci obietnicę, która będzie zawierać szczegóły modułu i instancji.
Przykład
Przykład omówiono poniżej -
<script type="text/javascript">
const importObj = {
module: {}
};
WebAssembly.instantiateStreaming(fetch("findsquare.wasm"), importObj).then(obj => {
console.log(obj);
});
</script>
Podczas testowania w przeglądarce zobaczysz błąd -
Aby działał na końcu serwera, będziesz musiał dodać aplikację / wasm typu MIME lub skorzystać z WebAssembly.instantiate (arraybuffer, importObject).
Obsługa WebAssembly została dodana do wszystkich najnowszych przeglądarek dostępnych obecnie u Ciebie, takich jak Chrome, Firefox. Firefox od wersji 54+ oferuje specjalną funkcję do debugowania kodu WASM.
Aby to zrobić, wykonaj swój kod w przeglądarkach Firefox, które wywołują wasm. Na przykład rozważmy następujący kod w C, który znajduje kwadrat liczby.
Przykład programu C jest następujący -
#include<stdio.h>
int square(int n) {
return n*n;
}
Skorzystamy z eksploratora WASM, aby uzyskać kod WASM -
Pobierz kod WASM i użyj go, aby zobaczyć wynik w przeglądarce.
Plik HTML, który ładuje wasm, jest następujący -
!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Square function</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
let square;
fetch("findsquare.wasm").then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
square = instance.exports.square(13);
console.log("The square of 13 = " +square);
document.getElementById("textcontent").innerHTML = "The square of 13 = " +square;
});
</script>
</body>
</html>
Otwórz przeglądarkę Firefox i załaduj powyższy plik html i otwórz narzędzie debugera.
Powinieneś zobaczyć wpis wasm: // w narzędziu debuggera. Kliknij wasm: // i wyświetli się kod wasm przekonwertowany do formatu .wat, jak pokazano powyżej.
Możesz spojrzeć na kod wyeksportowanej funkcji i zdebugować kod, jeśli pojawi się jakikolwiek problem. Firefox zamierza również dodać punkty przerwania, aby można było debugować kod i sprawdzić przepływ wykonywania.
W tym rozdziale napiszemy prosty program w C i przekonwertujemy go do .wasm i uruchomimy to samo w przeglądarce, aby otrzymać tekst „Hello World”.
Wykorzysta narzędzie wasm explorer, które przekonwertuje program w C do .wasm i użyje .wasm w naszym pliku .html.
Narzędzie eksploratora Wasm dostępne pod adresem https://mbebenita.github.io/WasmExplorer/ looks as follows −
Kod C, którego będziemy używać, jest następujący -
#include <stdio.h>
char *c_hello() {
return "Hello World";
}
Zaktualizuj pierwszy blok w programie Wasm Explorer za pomocą kodu C, jak pokazano poniżej -
Kliknij przycisk KOMPILUJ, aby skompilować do WASM i WAT oraz zestawu internetowego Firefox x86, jak pokazano poniżej -
Użyj DOWNLOAD, aby pobrać plik .wasm i zapisz go jako firstprog.wasm.
Utwórz plik .html o nazwie firstprog.html, jak pokazano poniżej -
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Hello World</title>
</head>
<body>
<div id="textcontent"></div>
<script type="text/javascript">
//Your code from webassembly here
</script>
</body>
</html>
Użyjmy teraz firstprog.wasm do odczytania helloworld z funkcji c_hello () w języku C.
Krok 1
Użyj api fetch (), aby odczytać kod firstprog.wasm.
Krok 2
Kod .wasm należy przekonwertować na bufor tablicy przy użyciu ArrayBuffer. Obiekt ArrayBuffer zwróci binarny bufor danych o stałej długości.
Jak dotąd kod będzie wyglądał następująco -
<script type="text/javascript">
fetch("firstprog.wasm") .then(bytes => bytes.arrayBuffer())
</script>
Krok 3
Bajty z ArrayBuffer muszą zostać skompilowane do modułu przy użyciu WebAssembly.compile(buffer) funkcjonować.
Kod będzie wyglądał jak poniżej -
<script type="text/javascript">
fetch("firstprog.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
</script>
Krok 4
Aby pobrać moduł, musimy wywołać konstruktor webassembly.instance, jak pokazano poniżej -
<script type="text/javascript">
fetch("firstprog.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
</script>
Krok 5
Uspokójmy teraz instancję, aby zobaczyć szczegóły w przeglądarce.
<script type="text/javascript">
fetch("firstprog.wasm") .then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)) .then(module => {
return new WebAssembly.Instance(module)
})
.then(instance => {
console.log(instance);
});
</script>
Szczegóły console.log są pokazane poniżej -
Aby uzyskać ciąg „Hello World” z funkcji c_hello (), musimy dodać kod w javascript.
Najpierw pobierz szczegóły bufora pamięci, jak pokazano poniżej -
let buffer = instance.exports.memory.buffer;;
Wartość bufora należy przekonwertować na tablicę o typie strukturalnym, abyśmy mogli odczytać z niej wartości. Bufor zawiera ciąg Hello World.
Aby przekonwertować na wpisane, wywołaj konstruktor Uint8Array, jak pokazano poniżej -
let buffer = new Uint8Array(instance.exports.memory.buffer);
Teraz możemy odczytać wartość z bufora w pętli for -.
Uzyskajmy teraz punkt początkowy do odczytu bufora, wywołując funkcję, którą napisaliśmy, jak pokazano poniżej -
let test = instance.exports.c_hello();
Teraz zmienna testowa ma punkt początkowy, aby odczytać nasz ciąg. WebAssembly nie ma nic dla wartości łańcuchowych, wszystko jest przechowywane jako liczby całkowite.
Kiedy więc odczytujemy wartość z bufora, będzie to wartość całkowita i musimy ją przekonwertować na łańcuch za pomocą funkcji fromCharCode () w javascript.
Kod jest następujący -
let mytext = "";
for (let i=test; buffer[i]; i++){
mytext += String.fromCharCode(buffer[i]);
}
Teraz, kiedy konsolujesz mytext, powinieneś zobaczyć napis „Hello World”.
Przykład
Kompletny kod wygląda następująco -
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Add Function</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
fetch("firstprog.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module)})
.then(instance => {
console.log(instance);
let buffer = new Uint8Array(instance.exports.memory.buffer);
let test = instance.exports.c_hello();
let mytext = "";
for (let i=test; buffer[i]; i++) {
mytext += String.fromCharCode(buffer[i]);
}
console.log(mytext); document.getElementById("textcontent").innerHTML = mytext;
});
</script>
</body>
</html>
Dodaliśmy element div, a zawartość jest dodawana do elementu div, więc ciąg jest wyświetlany w przeglądarce.
Wynik
Dane wyjściowe są wymienione poniżej -
Widzieliśmy, jak uzyskać plik .wasm z kodu C / C ++. W tym rozdziale przekonwertujemy wasm do modułu WebAssembly i wykonamy to samo w przeglądarce.
Użyjmy kodu czynnikowego C ++, jak pokazano poniżej -
int fact(int n) {
if ((n==0)||(n==1))
return 1;
else
return n*fact(n-1);
}
Otwórz program Wasm Explorer, który jest dostępny pod adresem https://mbebenita.github.io/WasmExplorer/ as shown below −
Pierwsza kolumna ma funkcję silni C ++, druga kolumna ma format tekstowy WebAssembly, a ostatnia kolumna zawiera kod asemblera x86.
Format tekstu WebAssembly -
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0)) (export "_Z4facti" (func $_Z4facti))
(func $_Z4facti (; 0 ;) (param $0 i32) (result i32)
(local $1 i32) (set_local $1
(i32.const 1)
)
(block $label$0
(br_if $label$0
(i32.eq
(i32.or
(get_local $0) (i32.const 1) ) (i32.const 1) ) ) (set_local $1
(i32.const 1)
)
(loop $label$1
(set_local $1 (i32.mul (get_local $0)
(get_local $1) ) ) (br_if $label$1 (i32.ne (i32.or (tee_local $0
(i32.add
(get_local $0) (i32.const -1) ) ) (i32.const 1) ) (i32.const 1) ) ) ) ) (get_local $1)
)
)
Funkcja C ++ fact został wyeksportowany jako „_Z4facti”W formacie tekstowym WebAssembly.
Kliknij przycisk pobierania, aby pobrać kod wasm i zapisać plik jako factorial.wasm.
Teraz, aby przekonwertować kod .wasm na moduł, musimy wykonać następujące czynności -
Krok 1
Przekonwertuj plik .wasm na bufor tablicy przy użyciu ArrayBuffer. Obiekt ArrayBuffer zwróci binarny bufor danych o stałej długości.
Krok 2
Bajty z ArrayBuffer muszą zostać skompilowane do modułu przy użyciu WebAssembly.compile(buffer) funkcjonować.
Plik WebAssembly.compile() funkcja kompiluje i zwraca moduł WebAssembly.Module z podanych bajtów.
Oto kod Javascript omówiony w krokach 1 i 2.
<script type="text/javascript">
let factorial;
fetch("factorial.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
factorial = instance.exports._Z4facti;
console.log('Test the output in Brower Console by using factorial(n)');
});
</script>
Objaśnienie kodu
Do pobrania zawartości pliku factorial.wasm używane jest pobieranie interfejsu API przeglądarki JavaScript.
Zawartość jest konwertowana na bajty za pomocą funkcji arrayBuffer ().
Moduł jest tworzony z bajtów przez wywołanie WebAssembly.compile (mod).
Instancja modułu jest tworzona przy użyciu nowego
WebAssembly.Instance(module)
Silnia eksportu funkcji _Z4facti jest przypisywana do zmiennej silni za pomocą metody WebAssembly.Module.exports ().
Przykład
Tutaj jest module.html wraz z kodem javascript -
module.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Module</title>
</head>
<body>
<script>
let factorial;
fetch("factorial.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
factorial = instance.exports._Z4facti;
console.log('Test the output in Browser Console by using factorial(n)');
});
</script>
</body>
</html>
Wynik
Uruchom module.html w przeglądarce, aby zobaczyć wynik -
W tym rozdziale omówimy funkcję webassembly.validate (), która zweryfikuje dane wyjściowe .wasm. .Wasm jest dostępny, gdy kompilujemy kod w C, C ++ lub rdzeniu.
Możesz skorzystać z następujących narzędzi, aby uzyskać kod WASM.
Wasm Fiddler, który jest dostępny pod adresem https://wasdk.github.io/WasmFiddle/
WebAssembly Explorer, który jest dostępny pod adresem https://mbebenita.github.io/WasmExplorer/.
Składnia
Składnia jest taka, jak podano poniżej -
WebAssembly.validate(bufferSource);
Parametry
bufferSource- BufferSource zawiera kod binarny pochodzący z programu C, C ++ lub Rust. Ma postać typedarray lub ArrayBuffer.
Wartość zwracana
Funkcja zwróci wartość true, jeśli kod .wasm jest prawidłowy, a false, jeśli nie.
Spróbujmy na jednym przykładzie. Idź do Wasm Fiddler , który jest dostępny pod adresemhttps://wasdk.github.io/WasmFiddle/, wprowadź wybrany kod C i wpisz kod WAS.
Blok zaznaczony na czerwono to kod C. Kliknij przycisk Build w środku, aby wykonać kod.
Kliknij przycisk Wasm, aby pobrać kod .wasm. Zapisz plik .wasm na swoim końcu i pozwól nam użyć tego samego do walidacji.
Przykład
Na przykład: validate.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Testing WASM validate()</title>
</head>
<body>
<script>
fetch('program.wasm').then(res => res.arrayBuffer() ).then(function(testbytes) {
var valid = WebAssembly.validate(testbytes);
if (valid) {
console.log("Valid Wasm Bytes!");
} else {
console.log("Invalid Wasm Code!");
}
});
</script>
</body>
</html>
Powyższy plik .html umieściłem na serwerze wamp wraz z pobranym plikiem .wasm. Oto wynik po przetestowaniu go w przeglądarce.
Wynik
Dane wyjściowe są wymienione poniżej -
WebAssembly ma kod w formacie binarnym o nazwie WASM. Możesz również pobrać format tekstowy w WebAssembly i nazywa się on WAT (format tekstowy WebAssembly). Jako programista nie powinieneś pisać kodu w WebAssembly, zamiast tego musisz skompilować języki wysokiego poziomu, takie jak C, C ++ i Rust, do WebAssembly.
Kod WAT
Napiszmy kod WAT krok po kroku.
Step 1 - Punktem wyjścia w WAT jest zadeklarowanie modułu.
(module)
Step 2 - Dodajmy teraz do niego jakąś funkcjonalność w postaci funkcji.
Funkcja jest zadeklarowana, jak pokazano poniżej -
(func <parameters/result> <local variables> <function body>)
Funkcja zaczyna się od słowa kluczowego func, po którym następują parametry lub wynik.
Parametry / wynik
Parametry i wartość zwracana jako wynik.
Parametry mogą mieć następujący typ obsługiwany przez wasm -
- i32: 32-bitowa liczba całkowita
- i64: 64-bitowa liczba całkowita
- f32: 32-bitowa liczba zmiennoprzecinkowa
- f64: 64-bitowa liczba zmiennoprzecinkowa
Parametry funkcji są zapisane jak podano poniżej -
- (parametr i32)
- (parametr i64)
- (parametr f32)
- (parametr f64)
Wynik zostanie zapisany w następujący sposób -
- (wynik i32)
- (wynik i64)
- (wynik f32)
- (wynik f64)
Funkcja z parametrami i wartością zwracaną zostanie zdefiniowana w następujący sposób -
(func (param i32) (param i32) (result i64) <function body>)
Zmienne lokalne
Zmienne lokalne to te, których potrzebujesz w swojej funkcji. Wartość lokalna funkcji zostanie zdefiniowana w następujący sposób -
(func (param i32) (param i32) (local i32) (result i64) <function body>)
Treść funkcji
Treść funkcji jest logiką do wykonania. Ostateczny program będzie wyglądał następująco -
(module (func (param i32) (param i32) (local i32) (result i64) <function body>) )
Step 3 - Aby odczytać i ustawić parametry i zmienne lokalne.
Aby odczytać parametry i zmienne lokalne, użyj get_local i set_local Komenda.
Example
(module
(func (param i32) (param i32) (local i32) (result i64) get_local 0
get_local 1
get_local 2
)
)
Zgodnie z podpisem funkcji,
get_local 0 da param i32
get_local 1 poda następny parametr param i32
get_local 2 da local value i32
Zamiast odwoływać się do parametrów i ustawień lokalnych przy użyciu wartości liczbowych, takich jak 0,1,2, można również użyć nazwy przed parametrami, poprzedzając nazwę znakiem dolara.
Poniższy przykład pokazuje, jak używać nazwy z parametrami i lokalnymi.
Example
(module
(func
(param $a i32) (param $b i32)
(local $c i32) (result i64) get_local $a get_local $b get_local $c
)
)
Step 4 - Instrukcja dotycząca treści i wykonania funkcji.
Wykonanie w wasm jest zgodne ze strategią stosu. Wykonywane instrukcje są wysyłane jedna po drugiej na stosie. Na przykład instrukcja get_local $ a przesunie wartość, którą odczyta na stosie.
Instrukcja jak i32.add to doda wolę zdejmowanie elementów ze stosu.
(func (param $a i32) (param $b i32) get_local $a
get_local $b
i32.add
)
Instrukcja dla i32.add jest ($a+$b). Ostateczna wartość i32.add zostanie umieszczona na stosie i zostanie przypisana do wyniku.
Jeśli podpis funkcji ma zadeklarowany wynik, na końcu wykonania na stosie powinna znajdować się jedna wartość. Jeśli nie ma parametru wyniku, na końcu stos musi być pusty.
Tak więc ostateczny kod wraz z treścią funkcji będzie wyglądał następująco -
(module
(func (param $a i32) (param $b i32) (result i32) get_local $a
get_local $b
i32.add
)
)
Step 5 - Nawiązywanie połączenia z funkcją.
Ostateczny kod z treścią funkcji jest pokazany w kroku 4. Teraz, aby wywołać funkcję, musimy ją wyeksportować.
Aby wyeksportować funkcję, można to zrobić z wartościami indeksu, takimi jak 0,1, ale możemy również podać nazwy. Nazwa będzie poprzedzona znakiem $ i zostanie dodana po słowie kluczowym func.
Example
(module
(func $add (param $a i32) (param $b i32) (result i32) get_local $a
get_local $b i32.add
)
)
Funkcję $ add należy wyeksportować, używając słowa kluczowego export, jak pokazano poniżej -
(module
(func $add (param $a i32)
(param $b i32) (result i32) get_local $a get_local $b i32.add ) (export "add" (func $add))
)
Aby przetestować powyższy kod w przeglądarce, będziesz musiał przekonwertować go do postaci binarnej (.wasm). Zapoznaj się z następnym rozdziałem, w którym pokazano, jak konwertować.WAT to .WASM.
W poprzednim rozdziale widzieliśmy, jak pisać kod w .wattj. format tekstowy WebAssembly. Format tekstowy WebAssembly nie będzie działał bezpośrednio w przeglądarce i musisz przekonwertować go na format binarny, czyli WASM, aby działał w przeglądarce.
WAT na WASM
Przekonwertujmy .WAT na .WASM.
Kod, którego będziemy używać, jest następujący -
(module
(func $add (param $a i32) (param $b i32) (result i32) get_local $a
get_local $b i32.add ) (export "add" (func $add))
)
Teraz przejdź do WebAssembly Studio, który jest dostępny pod adresem https://webassembly.studio/.
Po kliknięciu linku powinieneś zobaczyć coś takiego -
Kliknij pusty projekt Wat i kliknij przycisk Utwórz u dołu.
Zabierze Cię do pustego projektu, jak pokazano poniżej -
Kliknij main.wat i zastąp istniejący kod swoim, a następnie kliknij przycisk Zapisz.
Po zapisaniu kliknij kompilację, aby przekonwertować ją na .wasm -
Jeśli kompilacja się powiedzie, powinieneś zobaczyć utworzony plik .wasm, jak pokazano poniżej -
W dół pliku main.wasm i użyj go w pliku .html, aby zobaczyć dane wyjściowe, jak pokazano poniżej.
Na przykład - add.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Add Function</title>
</head>
<body>
<script>
let sum;
fetch("main.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)) .then(module => {
return new WebAssembly.Instance(module)
})
.then(instance => {
sum = instance.exports.add(10,40);
console.log("The sum of 10 and 40 = " +sum);
});
</script>
</body>
</html>
Funkcja add jest eksportowana, jak pokazano w kodzie. Przekazywane parametry to 2 wartości całkowite 10 i 40, a funkcja zwraca ich sumę.
Wynik
Dane wyjściowe są wyświetlane w przeglądarce.
Łączenie dynamiczne to proces, w którym dwa lub więcej modułów zostanie połączonych ze sobą podczas działania.
Aby zademonstrować, jak działa dynamiczne łączenie, użyjemy programu w C i skompilujemy go do wasm przy użyciu Ecmascript sdk.
Więc mamy -
test1.c
int test1(){
return 100;
}
test2.c
int test2(){
return 200;
}
main.c
#include <stdio.h>
int test1();
int test2();
int main() {
int result = test1() + test2();
return result;
}
W kodzie main.c korzysta z funkcji test1 () i test2 (), które są zdefiniowane w test1.c i test2.c. Sprawdźmy, jak połączyć te moduły w WebAssembly.
Polecenie kompilacji powyższego kodu jest następujące: użyj SIDE_MODULE = 1 do dynamicznego łączenia, jak pokazano w poleceniu.
emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm
Korzystanie z usługi WasmtoWat, która jest dostępna pod adresem https://webassembly.github.io/wabt/demo/wasm2wat/, otrzyma format tekstowy WebAssembly z maintest.wasm.
(module
(type $t0 (func (result i32))) (type $t1 (func))
(type $t2 (func (param i32))) (type $t3 (func (param i32 i32) (result i32)))
(import "env" "stackSave" (func $env.stackSave (type $t0)))
(import "env" "stackRestore" (func $env.stackRestore (type $t2)))
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32))
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
(func $f2 (type $t1)
(call $__wasm_apply_relocs) ) (func $__wasm_apply_relocs (export "__wasm_apply_relocs") (type $t1)) (func $test1 (export "test1") (type $t0) (result i32) (local $l0 i32)
(local.set $l0 (i32.const 100) ) (return (local.get $l0)
)
)
(func $test2 (export "test2") (type $t0) (result i32)
(local $l0 i32) (local.set $l0
(i32.const 200))
(return
(local.get $l0) ) ) (func $__original_main
(export "__original_main")
(type $t0) (result i32) (local $l0 i32)
(local $l1 i32) (local $l2 i32)
(local $l3 i32) (local $l4 i32)
(local $l5 i32) (local $l6 i32)
(local $l7 i32) (local $l8 i32)
(local $l9 i32) (local.set $l0(call $env.stackSave)) (local.set $l1 (i32.const 16))
(local.set $l2 (i32.sub (local.get $l0) (local.get $l1))) (call $env.stackRestore (local.get $l2) ) (local.set $l3(i32.const 0))
(i32.store offset=12 (local.get $l2) (local.get $l3))
(local.set $l4 (call $test1))
(local.set $l5 (call $test2))
(local.set $l6 (i32.add (local.get $l4) (local.get $l5))) (i32.store offset=8 (local.get $l2) (local.get $l6)) (local.set $l7 (i32.load offset=8 (local.get $l2))) (local.set $l8 (i32.const 16))
(local.set $l9 (i32.add (local.get $l2) (local.get $l8))) (call $env.stackRestore (local.get $l9)) (return(local.get $l7))
)
(func $main (export "main") (type $t3)
(param $p0 i32) (param $p1 i32)
(result i32)
(local $l2 i32) (local.set $l2
(call $__original_main)) (return (local.get $l2))
)
(func $__post_instantiate (export "__post_instantiate") (type $t1) (call $f2)) (global $__dso_handle (export "__dso_handle") i32 (i32.const 0))
)
Format tekstowy WebAssembly ma pewne importy zdefiniowane, jak pokazano poniżej -
(import "env" "stackSave" (func $env.stackSave (type $t0)))
(import "env" "stackRestore" (func $env.stackRestore (type $t2)))
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32))
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
Jest to dodawane podczas kompilacji kodu przez emcc (emscripten sdk) i zajmuje się zarządzaniem pamięcią w WebAssembly.
Praca z importami i eksportami
Teraz, aby zobaczyć dane wyjściowe, będziemy musieli zdefiniować importy, które możesz zobaczyć w kodzie .wat -
(import "env" "stackSave" (func $env.stackSave (type $t0)))
(import "env" "stackRestore" (func $env.stackRestore (type $t2)))
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32))
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
Powyższe terminy są wyjaśnione w następujący sposób -
env.stackSave - Służy do zarządzania stosem, funkcjonalności udostępnianej przez skompilowany kod emscripten.
env.stackRestore - Służy do zarządzania stosem, funkcjonalności udostępnianej przez skompilowany kod emscripten.
env.__memory_base- Jest to niezmienne globalne przesunięcie i32, które jest używane w pamięci env i zarezerwowane dla modułu wasm. Moduł może wykorzystać ten globalny w inicjalizatorze swoich segmentów danych, dzięki czemu są one ładowane pod poprawnym adresem.
env.__table_base- Jest to niezmienne globalne przesunięcie i32, które jest używane w tabeli env.table i zarezerwowane dla modułu wasm. Moduł może użyć tego globalnego w inicjalizatorze swoich segmentów elementu tabeli, aby były ładowane z odpowiednim przesunięciem.
env.memory - Będzie to zawierało szczegóły pamięci, które są wymagane do współdzielenia między modułami wasm.
env.table - Będzie to zawierało szczegóły tabeli, które mają być współdzielone między modułami wasm.
Import należy zdefiniować w javascript w następujący sposób -
var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536});
const importObj = {
env: {
stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => {
throw new Error('overflow');
},
table: new WebAssembly.Table({
initial: 0, maximum: 65536, element: 'anyfunc'
}), __table_base: 0,
memory: wasmMemory, __memory_base: 256
}
};
Przykład
Poniżej znajduje się kod javascript, który wykorzystuje importObj wewnątrz WebAssembly.instantiate.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536});
const importObj = {
env: {
stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => {
throw new Error('overflow');
},
table: new WebAssembly.Table({
initial: 0, maximum: 65536, element: 'anyfunc'
}), __table_base: 0,
memory: wasmMemory, __memory_base: 256
}
};
fetch("maintest.wasm") .then(bytes => bytes.arrayBuffer()) .then(
module => WebAssembly.instantiate(module, importObj)
)
.then(finalcode => {
console.log(finalcode);
console.log(WebAssembly.Module.imports(finalcode.module));
console.log(finalcode.instance.exports.test1());
console.log(finalcode.instance.exports.test2());
console.log(finalcode.instance.exports.main());
});
</script>
</body>
</html>
Wynik
Dane wyjściowe są następujące -
Zgodnie z oficjalną witryną WebAssembly.org, która jest dostępna pod adresem https://webassembly.org/docs/security/ głównym celem WebAssembly w zakresie bezpieczeństwa jest -
Model bezpieczeństwa WebAssembly ma dwa ważne cele -
Chroń użytkowników przed błędnymi lub złośliwymi modułami oraz
Zapewnij programistom użyteczne prymitywy i środki zaradcze do tworzenia bezpiecznych aplikacji, w ramach ograniczeń (1).
Skompilowany kod, tj. WASM z C / C ++ / Rust, nie jest wykonywany bezpośrednio w przeglądarce i korzysta z API Javascript. Kod WASM jest umieszczony w piaskownicy, tj. Wykonywany przez opakowanie JavaScript API, a przeglądarka komunikuje się z WASM za pomocą API.
Oto przykład użycia pliku .wasm w przeglądarce.
Przykład - C Program
#include<stdio.h>
int square(int n) {
return n*n;
}
Skorzystamy z eksploratora WASM, aby uzyskać kod WASM -
Pobierz kod WASM i użyj go do przetestowania API.
Przykład
<script type="text/javascript">
const importObj = {
module: {}
};
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module,importObj))
.then(finalcode => {
console.log(finalcode); console.log(finalcode.instance.exports.square(25));
});
</script>
Wynik
Otrzymasz następujący wynik -
Obiekty eksportu mają odniesienie do wywoływanej funkcji. Aby wywołać kwadrat funkcyjny, musisz to zrobić w następujący sposób -
console.log(finalcode.instance.exports.square(25));
Problemy ze skompilowanym kodem WASM
Poniżej przedstawiono problemy ze skompilowanym kodem WASM -
Trudno jest sprawdzić, czy nie został wstawiony jakiś złośliwy kod podczas kompilacji kodu do wasm. W tej chwili nie ma dostępnych narzędzi do weryfikacji kodu.
Wasm jest trudny do przeanalizowania, a błędny / złośliwy kod można łatwo uruchomić w przeglądarce.
W tym rozdziale skompilujemy prosty program w C do obsługi javascript i uruchomimy go w przeglądarce.
Na przykład - C Program
#include<stdio.h>
int square(int n) {
return n*n;
}
Zrobiliśmy instalację emsdk w folderze wa /. W tym samym folderze utwórz kolejny folder cprog / i zapisz powyższy kod jako square.c.
W poprzednim rozdziale zainstalowaliśmy emsdk. Tutaj wykorzystamy emsdk do skompilowania powyższego kodu c.
Skompiluj test.c w wierszu poleceń, jak pokazano poniżej -
emcc square.c -s STANDALONE_WASM –o findsquare.wasm
Polecenie emcc zajmuje się kompilacją kodu, a także podaje kod .wasm. Użyliśmy opcji STANDALONE_WASM, która daje tylko plik .wasm.
Przykład - findsquare.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Square function</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
let square; fetch("findsquare.wasm").then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)) .then(module => {
return new WebAssembly.Instance(module)
})
.then(instance => {
square = instance.exports.square(13);
console.log("The square of 13 = " +square);
document.getElementById("textcontent").innerHTML = "The square of 13 = " +square;
});
</script>
</body>
</html>
Wynik
Dane wyjściowe są takie, jak wspomniano poniżej -
W tym rozdziale skompilujemy prosty program w C ++ do javascript i uruchomimy go w przeglądarce.
Przykład
Program C ++ - Odwracanie podanej liczby.
#include <iostream>
int reversenumber(int n) {
int reverse=0, rem;
while(n!=0) {
rem=n%10; reverse=reverse*10+rem; n/=10;
}
return reverse;
}
Zrobiliśmy instalację emsdk w folderze wa /. W tym samym folderze utwórz kolejny folder cprog / i zapisz powyższy kod jako reverse.cpp.
W poprzednim rozdziale zainstalowaliśmy emsdk. Tutaj wykorzystamy emsdk do skompilowania powyższego kodu c.
Skompiluj test.c w wierszu poleceń, jak pokazano poniżej -
emcc reverse.cpp -s STANDALONE_WASM –o reverse.wasm
Polecenie emcc zajmuje się kompilacją kodu, a także podaje kod .wasm.
Przykład - reversenumber.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Reverse Number</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
let reverse;
fetch("reverse.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
console.log(instance);
reverse = instance.exports._Z13reversenumberi(1439898);
console.log("The reverse of 1439898 = " +reverse);
document.getElementById("textcontent")
.innerHTML = "The reverse of 1439898 = " +reverse;
});
</script>
</body>
</html>
Wynik
Dane wyjściowe są następujące -
Aby uzyskać kod kompilacji RUST, użyjemy narzędzia WebAssembly.studio.
Przejdź do WebAssembly.studio, który jest dostępny w Idź dohttps://webassembly.studio/ i wyświetli ekran, jak pokazano poniżej -
Kliknij Empty Rust Project. Po zakończeniu otrzymasz trzy pliki w folderze src / -
Otwórz plik main.rs i zmień wybrany kod.
Dodam następującą funkcję, która doda dwie podane liczby -
fn add_ints(lhs: i32, rhs: i32) -> i32 {
lhs+rhs
}
Kod dostępny w main.rs wygląda następująco -
#[no_mangle]
pub extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
Zastąp fn add_one swoim, jak pokazano poniżej -
#[no_mangle]
pub extern "C" fn add_ints(lhs: i32, rhs: i32) -> i32 {
lhs+rhs
}
W pliku main.js zmień nazwę funkcji z add_one na add_ints
fetch('../out/main.wasm').then(
response =>
response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes)).then(results => {
instance = results.instance;
document.getElementById("container").textContent = instance.exports.add_one(41);
}).catch(console.error);
Zamień instance.exports.add_one na instance.exports.add_ints (100,100)
fetch('../out/main.wasm').then(
response =>
response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes)).then(results => {
instance = results.instance;
document.getElementById("container").textContent = instance.exports.add_ints(100,100)
}).catch(console.error);
Kliknij przycisk budowania dostępny w interfejsie użytkownika webassembly.studio, aby zbudować kod.
Po zakończeniu kompilacji kliknij przycisk Uruchom dostępny w interfejsie użytkownika, aby zobaczyć dane wyjściowe -
Otrzymujemy wynik jako 200, ponieważ przekazaliśmy instance.exports.add_ints (100,100).
Podobnie możesz napisać inny program dla rdzy i skompilować go w webassembly.studio.
Go dodała obsługę WebAssembly od wersji 1.1. Aby przetestować to najpierw pobierz, Idź.
Przejdź do witryny golang, która jest dostępna pod adresem https://golang.org/dl/i kliknij Pobierz Go. Zgodnie z systemem operacyjnym pobierz i zainstaluj Go.
Po zakończeniu napisz prosty program, który doda dwie liczby.
testnum.go
package main
import "fmt"
func main() {
var a int = 100
var b int = 200
var ret int
ret = sum(a, b)
fmt.Printf( "Sum is : %d\n", ret )
}
/* function returning the max between two numbers */
func sum(num1, num2 int) int {
return num1+num2
}
Aby skompilować powyższy kod do wasm, najpierw ustaw zmienne środowiskowe w Go.
Będziesz musiał uruchomić następujące polecenie -
Set GOOS=js
GOARCH=wasm
Po zakończeniu wykonaj poniższe polecenie -
go build -o testnum.wasm testnum.go
Po wykonaniu polecenia powinieneś otrzymać plik testnum.wasm.
Przetestujmy teraz kod w przeglądarce. Aby to zrobić, musimy pobrać plik wasm_exec.js, który jest instalowany za pomocą go.
Plik wasm_exec.js będzie dostępny w folderze misc / wasm / w go.
Przykład
Oto kod dla testgo.html, który wykorzystuje wasm_exec.js i testnum.wasm.
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
</head>
<body>
<script type="text/javascript">
const importObj = {
module: {}
};
const go = new Go();
async function fetchAndInstantiate() {
const response = await fetch("testnum.wasm");
const buffer = await response.arrayBuffer();
const obj = await WebAssembly.instantiate(buffer, go.importObject);
console.log(obj);
go.run(obj.instance);
}
fetchAndInstantiate();
</script>
</body>
</html>
Wynik
Dane wyjściowe są następujące -
Javascript ma kilka interfejsów API, które mogą współpracować z kodem WASM. API jest również obsługiwane w nodejs.
Zainstaluj NODEJS w swoim systemie. Utwórz plik Factorialtest.js.
Użyjmy kodu czynnikowego C ++, jak pokazano poniżej -
int fact(int n) {
if ((n==0)||(n==1))
return 1;
else
return n*fact(n-1);
}
Otwórz program Wasm Explorer, który jest dostępny pod adresem https://mbebenita.github.io/WasmExplorer/ jak pokazano poniżej -
Pierwsza kolumna ma funkcję silni C ++, druga kolumna ma format tekstowy WebAssembly, a ostatnia kolumna zawiera kod asemblera x86.
Format tekstu WebAssembly jest następujący -
(module
(table 0 anyfunc)
(memory $0 1) (export "memory" (memory $0))
(export "_Z4facti" (func $_Z4facti)) (func $_Z4facti (; 0 ;) (param $0 i32) (result i32) (local $1 i32)
(set_local $1(i32.const 1)) (block $label$0 (br_if $label$0 (i32.eq (i32.or (get_local $0)
(i32.const 1)
)
(i32.const 1)
)
)
(set_local $1 (i32.const 1) ) (loop $label$1 (set_local $1
(i32.mul
(get_local $0) (get_local $1)
)
)
(br_if $label$1
(i32.ne
(i32.or
(tee_local $0 (i32.add (get_local $0)
(i32.const -1)
)
)
(i32.const 1)
)
(i32.const 1)
)
)
)
)
(get_local $1)
)
)
Fakt funkcji C ++ został wyeksportowany jako „_Z4facti”W formacie tekstowym WebAssembly.
Factorialtest.js
const fs = require('fs');
const buf = fs.readFileSync('./factorial.wasm');
const lib = WebAssembly.instantiate(new Uint8Array(buf)).
then(res => {
for (var i=1;i<=10;i++) {
console.log("The factorial of "+i+" = "+res.instance.exports._Z4facti(i))
}
}
);
W wierszu poleceń uruchom polecenie node factorialtest.js, a wynik będzie następujący -
C:\wasmnode>node factorialtest.js
The factorial of 1 = 1
The factorial of 2 = 2
The factorial of 3 = 6
The factorial of 4 = 24
The factorial of 5 = 120
The factorial of 6 = 720
The factorial of 7 = 5040
The factorial of 8 = 40320
The factorial of 9 = 362880
The factorial of 10 = 3628800
Rozdział omawia przykłady dotyczące WebAssembly.
Przykład 1
Poniżej znajduje się przykład programu w języku C, aby uzyskać maksymalny element -
void displaylog(int n);
/* function returning the max between two numbers */
int max(int num1, int num2) {
/* local variable declaration */ int result;
if (num1 > num2)
result = num1;
else result = num2;
displaylog(result);
return result;
}
Skompiluj kod w programie wasm fiddle i pobierz kod .wasm i .wat.
Wat code
Kod Wat jest następujący -
(module
(type $FUNCSIG$vi (func (param i32))) (import "env" "displaylog" (func $displaylog (param i32)))
(table 0 anyfunc)
(memory $0 1) (export "memory" (memory $0))
(export "max" (func $max)) (func $max (; 1 ;) (param $0 i32) (param $1 i32) (result i32)
(call $displaylog (tee_local $0
(select
(get_local $0) (get_local $1)
(i32.gt_s (get_local $0) (get_local $1))
)
)
)
(get_local $0)
)
)
Pobierz kod .wasm i pozwól nam użyć go w pliku .html, jak pokazano poniżej -
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: {
displaylog: n => alert("The max of (400, 130) is " +n)
}
};
fetch("testmax.wasm") .then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode);
console.log(finalcode.instance.exports.max(400,130));
});
</script>
</body>
</html>
Wynik
Dane wyjściowe są następujące -
Przykład 2
Poniżej znajduje się kod C ++, aby uzyskać serię Fibonacciego o podanej liczbie.
#include <iostream>>
void displaylog(int n);
int fibonacciSeries(int number) {
int n1=0,n2=1,n3,i;
for(i=2;i<number;++i) {
n3=n1+n2; displaylog(n); n1=n2; n2=n3;
}
return 0;
}
Używam programu WASM Explorer do kompilowania kodu. Pobierz Wat and Wasm i przetestuj to samo w przeglądarce.
Możesz użyć poniższego kodu -
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: { _Z10displaylogi: n => console.log(n) }
};
fetch("fib.wasm")
.then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode);
console.log(finalcode.instance.exports._Z15fibonacciSeriesi(10));
});
</script>
</body>
</html>
Wynik
Dane wyjściowe są następujące -
Przykład 3
Poniżej znajduje się kod Rusta służący do dodawania elementów w danej tablicy.
fn add_array(x: i32) -> i32 {
let mut sum = 0;
let mut numbers = [10,20,30]; for i in 0..3 {
sum += numbers[i];
}
sum
}
Zamierzamy wykorzystać WebAssembly Studio do skompilowania RUST do wasm.
Zbuduj kod i pobierz plik wasm i wykonaj to samo w przeglądarce.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: {
}
};
fetch("add_array.wasm") .then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode);
console.log(finalcode.instance.exports.add_array());
});
</script>
</body>
</html>
Wynik
Wynik będzie taki, jak podano poniżej -