WebAssembly in Action

Author of the book "WebAssembly in Action"
Save 40% with the code: ggallantbl
The book's original source code can be downloaded from the Manning website and GitHub. The GitHub repository includes an updated-code branch that has been adjusted to work with the latest version of Emscripten (currently version 3.1.44).

Saturday, December 30, 2017

WebAssembly - WasmFiddle and Inline WebAssembly Modules


WasmFiddle

JSFiddle, and tools like it, allow you to test code in the browser to see how it will work instead of having to write an application just to test something. Another advantage of tools like this is that you can share your example code on sites like Stack Overflow.

It turns out that there is a similar tool for WebAssembly called WasmFiddle: https://wasdk.github.io/WasmFiddle


If we replace the code that is in the top-left pane with our code below we can compile the code by pressing the Build button:

int add(int x, int y){ return x + y; }

Because we changed the method in the top-left pain from 'main' to 'add', we need to adjust the logic in the top-right pane from this:
log(wasmInstance.exports.main());
to this:
log(wasmInstance.exports.add(1, 2));

Once you make this change, you can click the Run button which will display the result of the call in the bottom-right pane.

Note: One difference between WasmFiddle's output as compared to Emscripten's output is that there is no underscore character before the method's name when using WasmFiddle.

To share your WebAssembly code with someone, you first need to click on the Share button (above the top-right pane) which will update the URI both in the browser's address bar and just above the top-left pane. You can then copy and share the address.


Inline WebAssembly Modules

WasmFiddle also allows you to download the wasm file or simply view the file's text format or binary representation in the bottom-left pane.

The binary representation from WasmFiddle offers an interesting option when it comes instantiating WebAssembly modules. With the array that you're given, you can bypass the fetch of the wasm file and instantiate a WebAssembly module directly with the array.

The following is some sample code using our Code Buffer from WasmFiddle:
var wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 135, 128, 128, 128, 0, 1, 96, 2, 127, 127, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 144, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 3, 97, 100, 100, 0, 0, 10, 141, 128, 128, 128, 0, 1, 135, 128, 128, 128, 0, 0, 32, 1, 32, 0, 106, 11]);

WebAssembly.compile(wasmCode).then(wasmModule =>
WebAssembly.instantiate(wasmModule, g_importObject)
).then(instance =>
//Warning: No underscore character when using WasmFiddle
alert(instance.exports.add(1, 2))
);

Note: WasmFiddle shows the use of WebAssembly.Module in the top-right pane but that is not recommended because that compiles the module synchronously. The recommended way to compile a module is by using the asynchronous WebAssembly.Compile method as shown in our example above.



If you'd like to know more about WebAssembly, the following are a few articles that might be of interest:
A New Book

I’ve been honored with an opportunity to write a book about WebAssembly.

If you enjoyed this article and would like to know more about WebAssembly, I welcome you to check out my book: WebAssembly in Action

Wednesday, December 27, 2017

WebAssembly - Web Workers


This is a continuation of a series of articles exploring how we can build and work with WebAssembly modules using Emscripten. The previous articles are not required reading to understand what we're going to cover today but, if you're curious, you can find them here:
Today we're going to continue using a bare-bones WebAssembly module (no Emscripten built-in helper methods) just to keep things as clear as possible as we examine HTML5 Web Workers.

Web Workers allow JavaScript code to run in a separate thread from the browser window's UI thread. This allows long-running scripts to do their processing without interfering with the responsiveness of the browser's UI.

Because Web Workers are running in a separate thread from the UI, the worker threads have no access to the DOM or other UI functionality. In addition, Web Workers are not intended to be used in large numbers and are expected to be long-lived since they have a high start-up performance cost and high memory cost per worker instance.

WebAssembly modules can be loaded in either the UI thread or in a Web Worker. You would then take the compiled module and pass that to another thread, or even another window, by using the postMessage method.

Since you will only be passing the compiled module to the other thread, you can call WebAssembly.compile rather than WebAssembly.instantiate to get just the module.

The following is some code that will request the wasm file from the server, compile it into a module, and then pass the compiled module to a Web Worker:

fetch("test.wasm").then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.compile(bytes)
).then(WasmModule =>
g_WebWorker.postMessage(WasmModule)
);

The code above will also work in a Web Worker if you wanted to do this in reverse (load and compile the module in a Web Worker and then pass the module to the UI thread). The only difference would be that you would use self.postMessage as opposed to g_WebWorker.postMessge in the example above.

The following is some example code that creates a Web Worker, loads the wasm file, and passes the compiled module to the Web Worker:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<input type="button" value="Test" onclick="OnClickTest();" />

<script type="text/javascript">
// Create a Web Worker (separate thread) that we'll pass the
// WebAssembly module to.
var g_WebWorker = new Worker("WebWorker.js");
g_WebWorker.onerror = function (evt) {
console.log(`Error from Web Worker: ${evt.message}`);
}
g_WebWorker.onmessage = function (evt) {
alert(`Message from the Web Worker:\n\n ${evt.data}`);
}


// Request the wasm file from the server and compile it...(Typically
// we would call 'WebAssembly.instantiate' which compiles and
// instantiates the module. In this case, however, we just want the
// compiled module which will be passed to the Web Worker. The
// Web Worker will be responsible for instantiating the module.)
fetch("test.wasm").then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.compile(bytes)
).then(WasmModule =>
g_WebWorker.postMessage({ "MessagePurpose": "CompiledModule", "WasmModule": WasmModule })
);

function OnClickTest() {
// Ask the Web Worker to add two values
g_WebWorker.postMessage({ "MessagePurpose": "AddValues", "Val1": 1, "Val2": 2 });
}
</script>
</body>
</html>

The following is the content of our WebWorker.js file:

var g_importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({ initial: 256 }),
'table': new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
};

// The WebAssembly module instance that we'll be working with
var g_objInstance = null;


// Listen for messages from the main thread. Because all messages to this
// thread come through this method, we need a way to know what is being
// asked of us which is why we included the MessagePurpose property.
self.onmessage = function (evt) {
// If we've been asked to call the module's Add method then...
var objData = evt.data;
var sMessagePurpose = objData.MessagePurpose;
if (sMessagePurpose === "AddValues") {

// Call the add method in the WebAssembly module and pass the
// result back to the main thread
var iResult = g_objInstance.exports._add(objData.Val1, objData.Val2);
self.postMessage(`This is the Web Worker...The result of ${objData.Val1.toString()} + ${objData.Val2.toString()} is ${iResult.toString()}.`);

} // If we've been passed a compiled WebAssembly module then...
else if (sMessagePurpose === "CompiledModule") {

// NOTE: Unlike when we pass in the bytes to instantiate, we don't
// have a separate 'instance' and 'modules' object returned in this
// case since we started out with the module object. We're only
// passed back the instance in this case.
WebAssembly.instantiate(objData.WasmModule, g_importObject).then(instance =>
g_objInstance = instance // Hold onto the module's instance so that we can reuse it
);

}
}

The following is the C code as well as the command line needed to turn the code into a WebAssembly module for today's article:

int add(int x, int y){ return x + y; }

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm


Inline Web Workers

While reading about using Web Workers, I ran across one comment about how this adds yet another network request (one for the wasm file and now another for the Web Worker's JavaScript file).

The following may not work for every situation, especially if the Web Worker's code is large or complex, but there is a way to create an inline Web Worker by using a Blob object.

You can pass a Blob object a string of JavaScript but I found that using a literal string was difficult to work with and that having a section in the HTML felt more natural which is why we're using a custom Script tag in the sample code below.

The following is an example of how you can create an inline Web Worker:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<input type="button" value="Test" onclick="OnClickTest();" />

<script id="scriptWorker" type="javascript/worker">
var g_importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({ initial: 256 }),
'table': new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
};

// The WebAssembly module instance that we'll be working with
var g_objInstance = null;

// Listen for messages from the main thread. Because all
// messages to this thread come through this method, we need a
// way to know what is being asked of us which is why we included
// the MessagePurpose property.
self.onmessage = function (evt) {
// If we've been asked to call the module's Add method then...
var objData = evt.data;
var sMessagePurpose = objData.MessagePurpose;
if (sMessagePurpose === "AddValues") {

// Call the add method in the WebAssembly module and pass
// the result back to the main thread
var iResult = g_objInstance.exports._add(objData.Val1, objData.Val2);
self.postMessage(`This is the Web Worker...The result of ${objData.Val1.toString()} + ${objData.Val2.toString()} is ${iResult.toString()}.`);

}// If we've been passed a compiled WebAssembly module then...
else if (sMessagePurpose === "CompiledModule") {

// NOTE: Unlike when we pass in the bytes to instantiate, we
// don't have a separate 'instance' and 'modules' object
// returned in this case since we started out with the module
// object. We're only passed back the instance in this case.
WebAssembly.instantiate(objData.WasmModule, g_importObject).then(instance =>
g_objInstance = instance // Hold onto the module's instance so that we can reuse it
);

}
}
</script>

<script type="text/javascript">
// Load the text from our special Script tag into a Blob and then
// grab the URI from the blob
var bInlineWorker = new Blob([document.getElementById("scriptWorker").textContent]);
var sBlobURL = window.URL.createObjectURL(bInlineWorker);

// Create a Web Worker (separate thread) that we'll pass the
// WebAssembly module to.
var g_WebWorker = new Worker(sBlobURL);
g_WebWorker.onerror = function (evt) {
console.log(`Error from Web Worker: ${evt.message}`);
}
g_WebWorker.onmessage = function (evt) {
alert(`Message from the Web Worker:\n\n ${evt.data}`);
}


// Request the wasm file from the server and compile it...(Typically
// we would call 'WebAssembly.instantiate' which compiles and
// instantiates the module. In this case, however, we just want the
// compiled module which will be passed to the Web Worker. The
// Web Worker will be responsible for instantiating the module.)
fetch("test.wasm").then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.compile(bytes)
).then(WasmModule =>
g_WebWorker.postMessage({ "MessagePurpose": "CompiledModule", "WasmModule": WasmModule })
);

function OnClickTest() {
// Ask the Web Worker to add two values
g_WebWorker.postMessage({ "MessagePurpose": "AddValues", "Val1": 1, "Val2": 2 });
}
</script>
</body>
</html>


Summary

One nice thing about Web Workers is that they also have access to IndexedDB which means the worker thread can handle caching too if you wish to work with WebAssembly modules entirely from the worker thread.

We didn't dig into caching a WebAssembly module in this article but, if you're interested, you can check out our previous article: WebAssembly - Caching to HTML5 IndexedDB

The focus of today's article with Web Workers was just around what was needed when working with WebAssembly modules. If you'd like to know more about Web Workers, I have several articles that may be of interest:


A New Book

I’ve been honored with an opportunity to write a book about WebAssembly.

If you enjoyed this article and would like to know more about WebAssembly, I welcome you to check out my book: WebAssembly in Action

Thursday, December 21, 2017

WebAssembly – Caching to HTML5 IndexedDB


Update: September 1, 2018

Originally, the specification for WebAssembly called for explicit caching of a compiled WebAssembly module to HTML5 IndexedDB.

Firefox and Edge added support for serialization to IndexedDB but, after some discussion between the WebAssembly Community Group, browser makers, and others, it has been decided that it will be best for browsers to implicitly cache WebAssembly modules instead.

As a result of this decision, Firefox 63, which will ship on October 23rd, 2018 will no longer allow WebAssembly modules to be cached to IndexedDB (https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/63)

The content of this article should not be used and any explicit caching already in place in your code should be removed.

This article is a continuation of a series exploring how we can build and work with WebAssembly modules using Emscripten. The previous articles are not required reading to understand what we're going to cover today but, if you're curious, you can find them here:
Today we're going to continue using a bare-bones WebAssembly module (no Emscripten built-in helper methods) just to keep things as clear as possible as we dig into an important topic.

One of the main reasons why we would want to use a WebAssembly module in the first place is for the performance improvements that it brings but we haven't yet made use of one key performance item.

Typically, when you go to a webpage that has a JavaScript file, the browser will cache it so that it doesn't have to download it again. The next time you go to that same webpage, if the file is in the browser's cache, it will load that rather than pull it from the server which saves time.

If you've been watching your network traffic, while working with the WebAssembly examples so far, you may have noticed that the wasm file is requested every time your page is loaded which isn't desired if the module hasn't changed.


HTML5 IndexedDB

WebAssembly modules were created with the ability to cache the compiled module in mind but the trick is that the caching is something that needs to be done explicitly by us.

In JavaScript, modules are cached to IndexedDB.

Up until this point, with all of my WebAssembly testing, I've simply been using the file system and double clicking on the html file to test things in the browser.

For security reasons, however, browsers will not allow websites to access IndexedDB from a local file which means we need to set up a server of some sort. I'm a developer on Windows so I'm going to use IIS.

IIS has a whitelist of file extensions that it will allow a website to provide and .wasm was not in my list which resulted in a 404 error when the page tried to fetch the wasm file.

Adding .wasm to the list in IIS is fairly simple:
  • Open up Internet Information Services (IIS) Manager (found in Control Panel, Administrative Tools)
  • You can set this at the root, Default Web Site, or at the individual Application level
  • Double click on the MIME Types link
  • Click the Add... link on the Actions pane to the right
  • Enter wasm for the file name extension
  • Enter application/wasm for the MIME type
  • Click OK


Caching

So far, we've been working with the module's instance but the result object from the WebAssembly.instantiate call also returns a module object which is the compiled module that we can cache in an IndexedDB database:

// Request the wasm file from the server and compile it... fetch(sWasmURI).then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, g_importObject)
).then(results => {
// We've been working with the .instance object so far
objModuleInstance = results.instance;

// The results object also holds a .module object which is
// what we can cache:
// results.module
});

When we retrieve the compiled module from the cache, we will need to pass it to WebAssembly.instantiate but there are a couple of differences compared to when we download the file.

The first difference is that we don't need to do a fetch or set up an arrayBuffer.

The second difference is that the return object from the instantiate object is the instance itself.

WebAssembly.instantiate(objModule, g_importObject).then(instance =>
g_objModuleInstance = instance
);


The following is some example code that shows how you can work with IndexedDB to cache and load WebAssembly modules:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<input type="button" value="Test" onclick="OnClickTest();" />

<script src="IndexedDB.js"></script>
<script type="text/javascript">
var g_importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({ initial: 256 }),
'table': new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
};

// The WebAssembly module instance that we'll be working with
var g_objModuleInstance = null;

// If we need to change the structure of the database, we can
// increment the DB_VERSION value to trigger the
// onupgradeneeded event when opening the database
var DB_VERSION = 1;
var DB_NAME = "WasmCache";
var DB_OBJSTORE_MODULES = "Modules";

// We've set things up in such a way so that each wasm file can
// have a version and we only clear the items from the cache if
// the version doesn't match
var g_sTestWasmURI = "test.wasm";
var g_sTestWasmVersion = "1.0.0";

// Check to see if the module is cached and, if so, use that.
// Otherwise, download the module and cache it.
GetCompiledModuleFromIndexedDB(g_sTestWasmURI, g_sTestWasmVersion);


function GetCompiledModuleFromIndexedDB(sWasmURI, sWasmVersion) {

// If we successfully opened the database then...
OpenDB(DB_NAME, DB_VERSION, HandleUpgradeDB).then(dbConnection => {

// If we successfully obtained the requested record then...
GetRecordFromObjectStore(dbConnection, DB_OBJSTORE_MODULES, sWasmURI).then(objRecord => {

// If the version stored for this module doesn't match the
// version we need then the module cached is out of date...
if (objRecord.WasmVersion !== sWasmVersion) {

// Have the record deleted and then fetch the proper file
DeleteRecordFromObjectStore(dbConnection, DB_OBJSTORE_MODULES, sWasmURI).then(result => {
LoadWebAssemblyFromFile(dbConnection, sWasmURI, sWasmVersion);
});

}
else { // The cached record is the version we need...

// Have the module instantiated.
//
// NOTE: Unlike when we pass in the bytes to instantiate
// in the LoadWebAssemblyFromFile method below, we
// don't have a separate 'instance' and 'modules' object
// returned in this case since we started out with the
// module object. We're only passed back the instance in
// this case.
WebAssembly.instantiate(objRecord.WasmModule, g_importObject).then(instance =>
g_objModuleInstance = instance // Hold onto the module's instance so that we can reuse it
);

}

}, sErrorMsg => { // Error in GetRecordFromObjectStore...

// We weren't able to pull the module from cache (most
// likely because it doesn't exist yet - hasn't been cached
// yet). Log the error and then fetch the file.
console.log(sErrorMsg);
LoadWebAssemblyFromFile(dbConnection, sWasmURI, sWasmVersion);

});

}, sErrorMsg => { // Error in OpenDB...

// Log the error and then fetch the file (won't be able to
// cache it in this case because we don't have a database
// connection to work with)
console.log(sErrorMsg);
LoadWebAssemblyFromFile(null, sWasmURI, sWasmVersion);

});

}

// Called by indexeddb if the database was just created or if the
// database version was changed
function HandleUpgradeDB(evt) {
// Create the object store which will hold 3 properties:
// • WasmURI - (primary key) e.g. 'test.wasm'
// • WasmVersion - e.g. '1.0.1'
// • WasmModule - the compiled module
var dbConnection = evt.target.result;
dbConnection.createObjectStore(DB_OBJSTORE_MODULES, { keyPath: "WasmURI" });
}

function LoadWebAssemblyFromFile(dbConnection, sWasmURI, sWasmVersion) {
// Request the wasm file from the server and compile it...
fetch(sWasmURI).then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, g_importObject)
).then(results => {
// Hold onto the module's instance so that we can reuse it
g_objModuleInstance = results.instance;

// Only do the following if we have a database connection
// object (this method will be passed a null if we failed to load
// the module from cache due to an error when trying to open
// the database)
if (dbConnection !== null) {
// WARNING: Not all browsers that support WebAssembly
// also support the ability to store the module in IndexedDB
// (seems to work fine in Edge 16 and in Firefox but it
// doesn't work for me in Chrome 63)
try {
// Create the object we're about to store
var objRecord = { "WasmURI": sWasmURI, "WasmVersion": sWasmVersion, "WasmModule": results.module };

// Cache the compiled module so that we don't have to
// pull the file from the server again unless we change
// the module's version number.
SaveRecordToObjectStore(dbConnection, DB_OBJSTORE_MODULES, objRecord);
}
catch (ex) {
console.log(`Unable to save the WebAssembly module to IndexedDB: ${ex.message}`);
}
}
});
}

function OnClickTest() {
// Call the module's add method and display the results
var iResult = g_objModuleInstance.exports._add(1, 2);
alert(iResult.toString());
}
</script>
</body>
</html>

The following is the content of our IndexDB.js file:

// Helper methods to work with an IndexedDB database
//
// Note: IndexedDB methods are asynchronous. To make things a bit
// easier to work with for the calling code, I've added Promises.

function OpenDB(sDatabaseName, sDatabaseVersion, fncUpgradeDB) {
return new Promise(function (fncResolve, fncReject) {

// Make a request for the database to be opened
var dbRequest = indexedDB.open(sDatabaseName, sDatabaseVersion);

dbRequest.onerror = function (evt) { fncReject(`Error in OpenDB: ${evt.target.error}`); }

// Pass the database connection object to the resolve method of the
// promise
dbRequest.onsuccess = function (evt) { fncResolve(evt.target.result); }

// This event handler will only be called if we're creating the database
// for the first time or if we're upgrading the database to a new
// version (this will be triggered before the onsuccess event handler
// above if it does get called). Let the calling code handle upgrading
// the database if needed to keep this file as generic as possible.
dbRequest.onupgradeneeded = fncUpgradeDB;

});
}


// Helper method to simplify the code some
function GetObjectStore(dbConnection, sObjectStoreName, sTransactionMode) {
// Create a transation and, from the transaction, get the object store
// object
return dbConnection.transaction([sObjectStoreName], sTransactionMode).objectStore(sObjectStoreName);
}


function GetRecordFromObjectStore(dbConnection, sObjectStoreName, sRecordID) {
return new Promise(function (fncResolve, fncReject) {

// Request the record specified
var dbGetRequest = GetObjectStore(dbConnection, sObjectStoreName, "readonly").get(sRecordID);

dbGetRequest.onerror = function (evt) { fncReject(`Error in GetRecordFromObjectStore: ${evt.target.error}`); }

dbGetRequest.onsuccess = function (evt) {
// If we have a record then...(we have to check because there
// won't be a record if the database was just created)
var objRecord = evt.target.result;
if (objRecord) { fncResolve(objRecord); }
else { fncReject(`The record '${sRecordID}' was not found in the object store '${sObjectStoreName}'`); }
}

});
}


function DeleteRecordFromObjectStore(dbConnection, sObjectStoreName, sRecordID) {
return new Promise(function (fncResolve, fncReject) {

// Request the delete of the record specified
var dbDeleteRequest = GetObjectStore(dbConnection, sObjectStoreName, "readwrite").delete(sRecordID);

dbDeleteRequest.onerror = function (evt) { fncReject(`Error in DeleteRecordFromObjectStore: ${evt.target.error}`); }

dbDeleteRequest.onsuccess = function (evt) { fncResolve(); }

});
}


function SaveRecordToObjectStore(dbConnection, sObjectStoreName, objRecord) {

// Request the put of our record (if it doesn't already exist, it gets added. otherwise, it gets updated)
var dbPutRequest = GetObjectStore(dbConnection, sObjectStoreName, "readwrite").put(objRecord);

dbPutRequest.onerror = function (evt) { console.log(`Error in SaveToIndexedDB: ${evt.target.error}`); }

dbPutRequest.onsuccess = function (evt) { console.log(`Successfully stored the record`); }

}


The following is the C code and command line needed to turn the C code into a WebAssembly module for today's article:

int add(int x, int y) { return x + y; }

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm


Summary

Even though we tried to keep the code as clean as possible, this article was a bit more involved because of everything that is involved when working with IndexedDB databases.

Because this article was focused around WebAssembly caching, we only dug into IndexedDB as deep as was needed.

Fortunately, I had the privilege of writing a DZone Refcard on HTML5 IndexedDB a little while ago that you're more than welcome to check out if you would like more information on the technology.


Edit made on Monday, January 1st, 2018: Changed the MIME Type from application/octet-stream to application/wasm after discovering the WebAssembly meeting notes from October 23rd, 2017 proposing the MIME Type be submitted to IANA to make it official.

Saturday, December 16, 2017

WebAssembly – Calling into JavaScript from bare bones C code


In my previous blog post, we explored the idea of creating a bare bones WebAssembly module with no Emscripten plumbing. In that article, we were able to call into the module from JavaScript but we didn't get into the reverse:

How does one call into JavaScript from a module?


Calling into JavaScript from a WebAssembly module

In this article we're going to try and get a WebAssembly module to call a method that's defined in our JavaScript.

In order to compile C code, when the method being used isn't in the source code, you need to define the method signature using the extern keyword as shown in the following example:

// Define the JavaScript method's signature that we're going to be calling.
extern void CallJS(int iVal);

// A method that the JavaScript will call into to trigger our code
// that will in turn call a JavaScript method passing along the value
// received.
void Test(int iVal){ CallJS(iVal); }

You can run the following command line to build the wasm file:

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm

To define the method in our JavaScript, we just need to add it to the env object that is part of the main object that we pass as the 2nd parameter to the WebAssembly.instantiate method:

var importObject = {
'env': {
// ... (other properties/methods of this object)

'_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
}
};

Just like how we needed to add an underscore character before the method name when calling into the module from JavaScript, we also need to include an underscore before the method name here.

The following is the full HTML/JavaScript for our example module:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="text" id="txtValue" />
<input type="button" value="Pass Value" onclick="PassValue();" />

<script type="text/javascript">
var gModule = null;

var importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({initial: 256}),
'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'}),

'_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
}
};

fetch('test.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
gModule = results.instance; // Hold onto the module's instance so that we can reuse it
});

function PassValue(){
// Get the value from the textbox (convert the value from a string to an int)
var iVal = parseInt(document.getElementById("txtValue").value,10);

// Call the method in the module
gModule.exports._Test(iVal);
}
</script>
</body>
</html>



A New Book

I’ve been honored with an opportunity to write a book about WebAssembly.

If you enjoyed this article and would like to know more about WebAssembly, I welcome you to check out my book: WebAssembly in Action

Thursday, December 14, 2017

WebAssembly – Using Emscripten to create a bare bones module


In my last blog post, An Introduction to WebAssembly, I showed you some of the basics of working with a WebAssembly module by using Emscripten.

This article is the result of some research and experiments to see if it’s possible to build a WebAssembly module using Emscripten but to not have any of the plumbing code.

For example, if we were to build the following C file using the following command line, the result would be an HTML file that is 101 KB, a JS file that is 80.2 KB, and a wasm file that is 9.4 KB!

#include <stdio.h>
#include "../emscripten/emscripten.h"

int main() { return 0; }

int EMSCRIPTEN_KEEPALIVE add(int x, int y) { return x + y; }

emcc test.c -s WASM=1 -s NO_EXIT_RUNTIME=1 -O1 -o hello.html


Having all of that plumbing is very convenient because it lets you start playing around with WebAssembly modules right away but what if we want just the bare minimum and will handle the HTML and JavaScript ourselves?

Fortunately, there is a way to tell Emscripten to output just the bare bones wasm file.

Let's first strip the C file down to just the bare minimum. In this case, we only want the add method:

int add(int x, int y) { return x + y; }

If we use the following command line, we will get just the wasm file and it’s only 202 bytes!

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm

The SIDE_MODULE flag tells Emscripten to compile only our methods and nothing else which means you will also not have access to things like printf or malloc.

If not specified, the default optimization flag used is -O0 (capital letter o and the number 0) but that results in the following error being thrown when you try to load the module:

LinkError: import object field 'DYNAMICTOP_PTR' is not a Number

Adding any optimization flag other than -O0 will fix the issue so we went with -O1 (capital letter o and the number 1) for this example.

One thing to note, however, is that the O appears to be case sensitive. The various optimization flags that are available can be found here: https://kripken.github.io/emscripten-site/docs/optimizing/Optimizing-Code.html

We also need to specify the name of the output file in the command line because, if we don't, Emscripten will output the file with the name a.out.wasm.


Because we've decided not to use Emscripten's plumbing code, we need to write our own HTML and JavaScript. The following is some example HTML and JavaScript to load in the module:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="button" value="test" onclick="javascript:OnClickTest();" />

<script type="text/javascript">
var gModule = null;

var importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({initial: 256}),
'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'})
}
};

fetch('test.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
gModule = results.instance; // Hold onto the module's instance so that we can reuse it
});

function OnClickTest(){
alert(gModule.exports._add(1, 2));
}
</script>
</body>
</html>


One thing that you may have noticed that is different with our call to the C method, as compared to what we did in the previous blog post, is that we're not using Module.ccall or Module.cwrap. Those are Emscripten helper methods. Here, we're calling the C method directly.

Something else to be aware of here is that your JavaScript will need to include an underscore character before the method name. For example, in our case, our method is called add. When you call the add method in JavaScript you would use _add(1, 2); rather than add(1, 2);

Although this approach might not be a solution that will work in every situation, given that you don't have access to things like malloc, it might be a useful way of creating a helper library if you're doing things like number crunching and don't need all of the overhead that comes with Emscripten's plumbing.


A New Book

I’ve been honored with an opportunity to write a book about WebAssembly.

If you enjoyed this article and would like to know more about WebAssembly, I welcome you to check out my book: WebAssembly in Action