Skip to content

Instantly share code, notes, and snippets.

@ityonemo
Created April 23, 2021 05:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ityonemo/65b1ab72ff693f36910cd9a0980c9dfb to your computer and use it in GitHub Desktop.
Save ityonemo/65b1ab72ff693f36910cd9a0980c9dfb to your computer and use it in GitHub Desktop.
Chiaroscuro - beam modules into wasm runtime
const get_memory = () => chiaroscuro.memory;
var chiaroscuro = {
imports: {
env: {
// exported functions
abort(msg, file, line, column) {
console.error("abort(" + msg + ")@" + file + " " + line + ":" + column);
},
jsConsoleLogInt(int) {
console.log("int:" + int)
},
jsConsoleLogStr(ptr, len) {
var bytes = new Int8Array(get_memory().buffer, ptr, len);
console.log(new TextDecoder().decode(bytes));
}
},
},
exports: undefined,
memory: undefined,
modules: {}
}
// CHIAROSCURO LOADING PIPELINE
//
const load_chiaroscuro = (response) => {
if (response.ok) return response.arrayBuffer();
console.error("failed to load chiaroscuro: " +
response.status + " " +
response.statusText);
throw "error";
}
const instantiate_chiaroscuro = (bytes) =>
WebAssembly.instantiate(bytes, chiaroscuro.imports);
const rebind_exports = (wasm_instance) => {
chiaroscuro.exports =
wasm_instance.instance.exports;
chiaroscuro.memory = wasm_instance.instance.exports.memory;
}
// MODULE LOADING PIPELINE
//
const load_module = (module) => {
fetch("modules/" + beam_filename(module))
.then(load_beam_module)
.then(instantiate_module)
chiaroscuro.modules[module] = "foobar"
window[module] = chiaroscuro.modules[module]
}
const load_beam_module = (response) => {
if (response.ok) return response.arrayBuffer();
basename = response.url.replace(/\\/g,'/').replace( /.*\//, '' );
console.error("failed to load module " +
basename + ": " +
response.status + " " +
response.statusText);
throw "error";
}
const instantiate_module = (bytes) => {
length = bytes.byteLength;
addr = chiaroscuro.exports.allocate(length);
buffer = chiaroscuro.memory.buffer;
// transfer contents from the module bytes to the new thing
new Uint8Array(buffer, length, addr).set(new Uint8Array(bytes), 0)
// triggers processing the module into memory.
chiaroscuro.exports.instantiate(addr, length);
}
const beam_filename = (name) => {
if (/[A-Z]/.test(name[0])) {
return "Elixir." + name + ".beam"
} else {
return name + ".beam"
}
}
chiaroscuro.load = load_module;
fetch('wasm/chiaroscuro.wasm')
.then(load_chiaroscuro)
.then(instantiate_chiaroscuro)
.then(rebind_exports)
const std = @import("std");
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
extern fn jsConsoleLogStr(?[*]const u8, u32) void;
extern fn jsConsoleLogInt(u32) void;
fn print(string: []const u8) void {
jsConsoleLogStr(string.ptr, string.len);
}
export fn allocate(size: u32) u32 {
var slab = gpa.allocator.alloc(u8, size) catch unreachable;
return @ptrToInt(slab.ptr);
}
export fn instantiate(ptr: u32, size: u32) void {
var raw_module = @intToPtr([*]u8, size);
var module = raw_module[0..size];
// actual processing to go here.
// this isn't complete yet.
jsConsoleLogInt(module[0]);
jsConsoleLogInt(module[1]);
jsConsoleLogInt(module[2]);
jsConsoleLogInt(module[3]);
}
ebin_dir = :chiaroscuro
|> Application.app_dir()
|> Path.join("ebin")
plug Plug.Static,
at: "/modules",
from: ebin_dir,
only: ["Elixir.TestModule.beam"]
defmodule Mix.Tasks.Compile.Zig do
use Mix.Task
def run(_) do
System.cmd("zig", ~w(
build-lib
-target wasm32-freestanding
-O ReleaseSmall
src/chiaroscuro.zig))
File.cp!("chiaroscuro.wasm", "priv/static/wasm/chiaroscuro.wasm")
File.rm!("chiaroscuro.wasm")
:ok
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment