npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@deislabs/wasm-linker-js

v0.2.1

Published

A simple WebAssembly linker

Downloads

12

Readme

wasm-linker-js

A simple WebAssembly Linker in JavaScript

actions badge NPM version

This is an experimental JavaScript library that helps instantiating WebAssembly modules with imports by providing functionality to link JavaScript objects (functions, memories, globals) as imports, as well as automatically perform name based resolution for linking entire modules.

The API loosely follows the Wasmtime linker, (see the linker documentation), and it exposes asynchronous import functionality enabled by Binaryen and Asyncify.

Using the Linker

For more examples of using the Linker in both TypeScript and JavaScript, check the linker tests and the Node.js examples.

First, add the package to your project:

$ npm install @deislabs/wasm-linker-js

Note that in order to run the examples shown here, binaryen is also required (npm install binaryen), in order to show the text format of the WebAssembly modules. In real world scenarios that is not necessary, and the modules can be compiled from their binary representation without additional dependencies.

Defining a single import

Assuming we are trying to instantiate the module represented in its text format below (transformed to its binary representation using Binaryen), we can satisfy its import using the define method available on the linker:

const { Linker } = require("@deislabs/wasm-linker-js");
const { parseText } = require("binaryen");

const usingAdd = `
(module
    (import "calculator" "add" (func $calc_add (param i32 i32) (result i32)))
  
    (memory 1 1)
    (export "memory" (memory 0))
    (export "add" (func $add))

    (func $add (param i32) (param i32) (result i32)
        (return
            (call $calc_add
                (local.get 0)
                (local.get 1)
            )
        )
    )
)
`;

(async () => {
  var linker = new Linker();

  // The "usingAdd" module imports calculator.add.
  // We define it,  provide a JS implementation, then
  // instantiate it.
  linker.define("calculator", "add", (a, b) => a + b);
  var calc = await linker.instantiate(parseText(usingAdd).emitBinary());

  var result = calc.instance.exports.add(1, 2);
  console.log(result);
})();

Linking an entire module

If we have a compiled module that exports items (defined below in its text format and contained in the add constant) that our initial module needs to import, we can add it to the linker, then continue instantiating our module (defined above in its text format and contained in the usingAdd constant):

const { Linker } = require("@deislabs/wasm-linker-js");
const { parseText } = require("binaryen");

const add = `
(module
  (memory 1 1)
  (export "memory" (memory 0))
  (export "add" (func $add))
  
  (func $add (param i32) (param i32) (result i32)
      (return
          (i32.add
              (local.get 0)
              (local.get 1)
          )
      )
  )
)
`;

(async () => {
  var linker = new Linker();

  // The "usingAdd" module above imports calculator.add.
  // We link a module that exports the functionality
  // required, then instantiate the module that uses it.
  await linker.module(
    "calculator",
    new WebAssembly.Module(parseText(add).emitBinary())
  );
  var calc = await linker.instantiate(parseText(usingAdd).emitBinary());
  var result = calc.instance.exports.add(1, 2);
  console.log(result);
})();

Defining asynchronous imports

The current WebAssembly MVP does not have a way of waiting for the execution of asynchronous imports (see this issue). To enable this functionality, Binaryen has a pass that transforms a Wasm module and allows it to pause and resume by unwiding and rewinding the call stack. When enabled, this library can use the JavaScript wrapper of Asyncify and define asynchronous import functions for WebAssembly modules (note that the Asyncify pass must have been applied to the module before instantiating using the linker):

const { Linker } = require("@deislabs/wasm-linker-js");
const { parseText } = require("binaryen");

(async () => {
  var useAsyncify = true;
  var linker = new Linker(useAsyncify);

  // Notice how we define an asynchronous import, which
  // will wait for 1.5s before returning the result.
  var sleep = function (ms) {
    return new Promise((resolve) => {
      setTimeout(resolve, ms);
    });
  };
  linker.define("calculator", "add", async (a, b) => {
    await sleep(1500);
    return a + b;
  });

  let bytes = parseText(usingAdd);

  // we perform the asyncify compiler pass from Binaryen
  bytes.runPasses(["asyncify"]);
  var calc = await linker.instantiate(bytes.emitBinary());

  var result = await calc.instance.exports.add(1, 2);
  console.log(result);
})();

Using the streaming APIs in the browser

For browsers that support the WebAssembly streaming APIs, the linker exposes two methods that can be used to efficiently instantiate modules from a streamed source: moduleStreaming, which instantiates a module and adds its exports to the linker's cache, and instantiateStreaming, which instantiates a module and returns its the WebAssemblyInstantiatedSource:

(async () => {
  var linker = new Linker();
  await linker.moduleStreaming("calculator", fetch("calculator.wasm"));
  var mod = await linker.instantiateStreaming(fetch("using_calculator.wasm"));
  console.log(mod.instance.exports.multiply(3, 4));
}

See the documentation for the browser streaming APIs for more information about instantiating modules from streamed sources.

The linker also allows adding an already instantiated module, through the instance method, and aliasing a module under a new name, through the alias method. Most public methods defined on the Linker have a correspondent in the Wasmtime Linker, and we try to keep the APIs similar.

Implementation notes and known issues

  • When defining multiple import items with the same name, the last one takes precedence (the existing items are replaced). This behavior could change in the future to add a configurable property defining whether import shadowing should be allowed.
  • When instantiating a linker with Asyncify enabled, all modules linked and instantiated with the linker will be instantiated using Asyncify's JavaScript wrapper. This behavior could change in the future to allow a per-instance (and by extension per module linked) setting for Asyncify. (this can be avoided through instantiating a module separately and adding it to the linker using the instance method).
  • There is a browser example in the examples/ directory in this repository. While functional, the implementation is far from ideal - the WebPack configuration for generating a browser-compatible library is not optimal (this should be changed to use ECMAScript modules).
  • This library is experimental, and the API is not stable. We welcome feedback on both the public API and the implementation of this library.

Contributing

This project welcomes contributions through the GitHub pull request process. Prerequisites to building the project:

  • Node.js
  • npm

To iterate on the project locally:

$ npm run build
$ npm test
$ npm run examples

Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct.

For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.