Relatively, WebAssembly has had a short history but brought far-reaching significance to Web development. First released in 2015, its precursor technologies were asm.js from Mozilla and Google Native Client, and the initial implementation was based on the asm.js feature set. (Source: Wikipedia.) Since March 2017 when the preview of the minimum viable product (MVP) created by WebAssembly ended, WASM has evolved rapidly and is now deployed to all major browsers. In 2019, WebAssembly 1.0 became a W3C recommended standard, breaking the previous situation where web development was limited to JavaScript. Then, what is WebAssembly?This article gives you a comprehensive introduction to this WebAssembly technology from various aspects.
What Is WebAssembly?
WebAssembly (abbreviated as Wasm) is a new type of code that can be run in modern Web browsers. It is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides an effective compilation target for languages such as C/C++, C#, and Rust so that they can run on the Web. Developers choose their own programming language to write code and then compile it as WebAssembly code. It runs on the client side (usually a Web browser) where it is compiled to executable machine code and executed at near-native speed.
As Wasm provides the browser with code compiled in binary format, it saves time in downloading and parsing JavaScript code, which improves the efficiency of code execution. Besides, WebAssembly allows developers to manually manage memory as well as the ability to directly access memory, improving efficiency. Aside from excellent parsing and execution capabilities, Wasm has a low-level virtual machine that is unrelated to hardware and operating system, therefore, it builds a safe and reliable runtime environment. Now, Wasm is compatible with major Web browsers, including Chrome, Edge, Firefox, Opera, and Safari.
Wasm is designed to work with JavaScript to achieve high-performance applications on the Web platform. In all, performing the native-like development speed on the Web platform, WebAssembly manifests its performance and functionality while JavaScript displays its flexibility, as a result, client apps can be easily run on the Web.
WebAssembly Key Concepts
Before learning the steps of how to compile C to Wasm, you need to understand four key concepts of Wasm.
- Module: A WebAssembly binary that has been compiled by the browser into executable machine code. It contains a set of functions and data, eg. global variable and initialized memory. A Module is stateless and declares imports and exports just like an ES2015 module.
- Memory: WebAssembly uses a linear array data structure to represent memory. Linear memory is a contiguous block of bytes that is always sized as a multiple of one page (64 KiB). The WebAssembly code can read and write these bytes directly.
- Table: A resizable array-like structure containing references (e.g., to functions) that cannot be stored as raw bytes in Memory for security and portability reasons.
- Instance: A Module paired with all the states it uses at runtime including a Memory, Table, and set of imported values. An Instance is like an ES module that has been loaded into a particular global with a particular set of imports.
A WebAssembly Module defines a set of functions, global variables, memories, and tables. By combining specific imported and exported values, they can be instantiated to a running application. Memory and tables can also be instantiated as a running application, and their values change as the application executes.
Steps to Compile from C/C++ to Wasm
Before demonstrating how to compile from C to Wasm, a precondition has to be satisfied - get the Emscripten SDK to set up the required development environment. There are two main scenarios using Emscripten to compile. Learn the below detailed operations separately.
Creating HTML and JavaScript
It is possible to write a program in C and compile it to WebAssembly with Emscripten SDK. After creating an HTML file, load and run the WebAssembly code on a Web server.
1. First, we need an example to compile. Copy the following simple C example, and save it in a file called hello.c
in a local directory:
#include
int main() {
printf("Hello World\n");
return 0;
}
2. Now, use the terminal window you used to enter the Emscripten compiler environment, navigate to the same directory as your hello.c file, and run the following command:
emcc hello.c -o hello.html
3. The options passed through the command are as follows:
-o hello.html—This specifies that we want Emscripten to generate an HTML page to run our code in (and a filename to use), as well as the Wasm module and the JavaScript "glue" code to compile and instantiate the Wasm so it can be used in the Web environment.
4. By far, your source directory should include:
- The binary Wasm module code (hello.wasm)
- A JavaScript file containing glue code to translate between the native C functions, and JavaScript/Wasm (hello.js)
- An HTML file to load, compile, and instantiate your Wasm code, and display its output in the browser (hello.html)
5. Now, run the example by loading the generated hello.html in any browser that supports WebAssembly. It is enabled by default from Firefox 52, Chrome 57, Edge 57, and Opera 44.
6. If everything runs well, you will see "Hello world" output in the Emscripten console showing on the Web page, and your browser's JavaScript console.
Using a Custom HTML Template
1. At the beginning, save the following C code in a file called hello2.c in the new directory.
#include
int main() {
printf("Hello World\n");
return 0;
}
2. Search for the shell_minimal.html file in the emsdk repo and copy it into a subdirectory called html_template inside your previous new directory.
3. Now, navigate to the new directory (identically, in your Emscripten compiler environment terminal window) and run the following commands.
emcc -o hello2.html hello2.c -O3 --shell-file html_template/shell_minimal.html
4. The options passed this time are a bit different:
- We've specified -o hello2.html, meaning that the compiler will still output the JavaScript glue code and .html.
- We've specified -O3, which is used to optimize the code. Emcc has optimization levels like any other C compiler, including: -O0 (no optimization), -O1, -O2, -Os, -OZ, -Og, and -O3. -O3 is a good setting for release builds.
- We've also specified --shell-file html_template/shell_minimal.html — this provides the path to the HTML template you want to use to create the HTML you will run your example through.
5. Now, let's start to run this example. The above command will generate hello2.html whose content is nearly the same as the template with some glue code added to load the generated Wasm, run it, etc. When opening it in a browser, you will see an output like the last example.
Calling a Custom Function Defined in C
If you define a function in C code and want to call it from JavaScript based on needs, you can achieve this goal by using the Emscripten ccall() function, and the EMSCRIPTEN_KEEPALIVE declaration (which adds your functions to the exported functions list.)
1. At first, save the following code as hello3.c in the new directory. In the default state, codes generated by Emscripten only call the main() function while other functions are eliminated as dead code. This situation can be prevented from happening when you add EMSCRIPTEN_KEEPALIVE in the front of a function name. To use EMSCRIPTEN_KEEPALIVE, you need to import the emscripten.h library first.
#include
#include <emscripten/emscripten.h>
int main() {
printf("Hello World\n");
return 0;
}
#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN
#endif
EXTERN EMSCRIPTEN_KEEPALIVE void myFunction(int argc, char ** argv) {
printf("MyFunction Called\n");
}
2. For convenience, add html_template/shell_minimal.html with {{{ SCRIPT }}} into this new directory. (obviously, it needs to be put in a central place in the real dev environment)
3. Now, let's rerun the compilation step. Under the latest directory (while inside your Emscripten compiler environment terminal window), compile C code with the below command. Please note that we need to use NO_EXIT_RUNTIME to compile: otherwise, when main() exits, the runtime would be shut down and it would be invalid to call compiled code. This is necessary to emulate proper C: for example, to ensure that atexit() functions are called.
emcc -o hello3.html hello3.c --shell-file html_template/shell_minimal.html -s NO_EXIT_RUNTIME=1 -s "EXPORTED_RUNTIME_METHODS=['ccall']"
4. Loading this example in a browser will display the same result as the previous one.
5. Now, we need to run a new myFunction() function from JavaScript. First, open the hello3.html in a text editor.
6. Add a <button> element as shown below, just above the first opening <script type='text/javascript'> tag.
<button id="mybutton">Run myFunction</button>
7. Now add the following code at the end of the first <script> element:
document.getElementById("mybutton").addEventListener("click", () => {
alert("check console");
const result = Module.ccall(
"myFunction", // name of C function
null, // return type
null, // argument types
null, // arguments
);
});
Final Words
Today, WebAssembly has been gradually used in many new Web applications, including complex graphics and game development, AI and machine learning, audio and video processing, and so on. As the technology evolves, the field of applying WebAssembly will become broader and broader, and the influence of it will become greater and greater.
Recently, we released ComPDFKit Web Demo applying the latest WebAssembly technology to provide developers with a 100% frontend solution. It runs on the client side without relying on the server to process PDF documents. With this solution, the speed of rendering PDFs is faster and the performance to render PDFs across different platforms is consistent. Welcome to contact us for more information and to try our Demo.