I like experimenting with new technologies and also a lover of C/C++ programming language. I was listening about WebAssembly for past few years like 1 or so and was trying out different methods to make a working demo of C wasm file in Browser but recently I came to know about a npm module or library that will help to make simple with Emscripten. If you don't know about webassembly take a look at my previous article in this series. Why and How webassembly?
We are going to use library called webassembly which is a npm module which provides a C header in which WebAssembly functionality is provided by a C header. A small JavaScript support library (distributions) provides the browser runtime.
I created a repo in my Github name is wasm-experiments with sub-folder js-webassembly. I tried creating a simple calculator with WebAssembly. Let’s see it code:
#include <webassembly.h>
#include <math.h>
export double add(double a, double b) {
return a + b;
}
export double substract(double a, double b) {
return a - b;
}
export double multiply(double a, double b) {
return a * b;
}
export double divide(double a, double b) {
return a / b;
}
export double mod(int a, int b) {
return a % b;
}
export double square_root(double a) {
return sqrt(a);
}
In C code is imported webassembly.h
which is provided by the library and exporting each function like we normally do in Javascript or Typescript. To compile we run wa compile -O -o program.wasm program.c
. The library provides CLI for easy use where we can give output path with -o and input C program with -O being flag for optimization.
const wasm = require("webassembly");
const loaded = wasm.load("./program.wasm");
loaded.then(module => {
console.log("1 + 2 = " + module.exports.add(1,2));
console.log("1 - 2 = " + module.exports.substract(1,2));
console.log("1 X 2 = " + module.exports.multiply(1,2));
console.log("1 / 2 = " + module.exports.divide(1,2));
console.log("1 % 2 = " + module.exports.mod(1,2));
console.log("Square root of 9 = " + module.exports.square_root(9));
})
.catch(err => {
console.log("Error loading wasm file:", err);
})
Then in javascript I just used single library webassembly to load the wasm file which return a promise which resolves to c module and we can now directly use it the exported function from javascript using module.exports.<function_name>
.
This was the easiest part, I wanted to use same wasm file in browser also. So I created a loader script for wasm.
function loadWebAssembly(filename, imports) {
return fetch(filename)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {
imports = imports || {};
imports.env = imports.env || {};
imports.env.memoryBase = imports.env.memoryBase || 0;
imports.env.tableBase = imports.env.tableBase || 0;
if (!imports.env.memory) {
imports.env.memory = new WebAssembly.Memory({ initial: 256 });
}
if (!imports.env.table) {
imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' });
}
return new WebAssembly.Instance(module, imports);
});
}
Below script is written in index.html
which loads the wasm file and exposes exported functions to globally.
if ( !( 'WebAssembly' in window ) ) {
alert( 'You need Webassembly enabled browser' )
}
var cadd, csubs, cmultiply, cdivide, cmod, csquare_root;
loadWebAssembly( './assets/wasm/program.wasm' )
.then( instance => {
var exports = instance.exports;
console.log( '1 + 2 = ' + exports.add( 1, 2 ) );
console.log( '1 - 2 = ' + exports.substract( 1, 2 ) );
console.log( '1 * 2 = ' + exports.multiply( 1, 2 ) );
console.log( '1 / 2 = ' + exports.divide( 1, 2 ) );
console.log( '1 % 2 = ' + exports.mod( 1, 2 ) );
console.log( 'Square root of 9 = ' + exports.square_root( 9 ) );
cadd = exports.add;
csubs = exports.substract;
cmultiply = exports.multiply;
cdivide = exports.divide;
cmod = exports.mod;
csquare_root = exports.square_root;
} );
Following code is in main.js
which uses C program defined functions to calculate sum, difference etc.
const result = document.querySelector('#result');
const czero = document.querySelector('#czero');
// ... varibales for reference in document
const controls = [result, czero, cezero, positive_negative, sqrt, one, two, three, four, five, six, seven, eight, nine, zero, add, multiply, mod, divide, equals, subs];
// Adding event listner
controls.forEach(c => {
if (c !== 'result') {
c.addEventListener('click', (e) => {
handleDisplay(e);
});
}
})
function handleDisplay(e) {
// ... code to handle display in result
}
// function using exported from c
function calculate(text) {
if (text.includes("+")) {
const operandsArray = splitText(text, "+");
return cadd(operandsArray[0], operandsArray[1]);
}
if (text.includes("-")) {
const operandsArray = splitText(text, "-");
return parseInt(csubs(operandsArray[0], operandsArray[1]));
}
if (text.includes("*")) {
const operandsArray = splitText(text, "*");
return cmultiply(operandsArray[0], operandsArray[1]);
}
if (text.includes("/")) {
const operandsArray = splitText(text, "/");
return cdivide(operandsArray[0], operandsArray[1]);
}
if (text.includes("%")) {
const operandsArray = splitText(text, "%");
return cmod(operandsArray[0], operandsArray[1]);
}
}
function splitText(text, operand) {
// ... code to split expression for operation
}
Head over to Github to look at full code.
Hope you like it. More experiments coming soon.
Source
- WebAssembly project — https://github.com/dcodeIO/webassembly
- WebAssembly — https://webassembly.org/
- Mozilla WebAssembly — https://developer.mozilla.org/en-US/docs/WebAssembly
Happy learning, Happy coding!