Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prep work for WAMR integration #652

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ src/version.h
node_modules/
types/api/
test_dir*/

bun.lockb
.cache/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
[submodule "deps/mimalloc"]
path = deps/mimalloc
url = https://github.com/microsoft/mimalloc
[submodule "deps/wamr"]
path = deps/wamr
url = https://github.com/bytecodealliance/wasm-micro-runtime
15 changes: 15 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ add_subdirectory(deps/sqlite3 EXCLUDE_FROM_ALL)
set(BUILD_WASI "simple" CACHE STRING "WASI implementation")
add_subdirectory(deps/wasm3 EXCLUDE_FROM_ALL)

#TODO: Add some sane configuration for it and possible profiles.
#https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/embed_wamr.md
set (WAMR_BUILD_PLATFORM "linux")
set (WAMR_BUILD_TARGET "X86_64")
set (WAMR_ROOT_DIR deps/wamr)
set (WAMR_BUILD_INTERP 1)
set (WAMR_BUILD_FAST_INTERP 0)
set (WAMR_BUILD_AOT 0)
set (WAMR_BUILD_LIBC_BUILTIN 1)
set (WAMR_BUILD_LIBC_WASI 1)
set (WAMR_BUILD_SIMD 1)
include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)

if(BUILD_WITH_MIMALLOC)
option(MI_OVERRIDE "" OFF)
option(MI_BUILD_SHARED "" OFF)
Expand All @@ -83,6 +96,7 @@ add_library(tjs STATIC
src/version.c
src/vm.c
src/wasm.c
src/wamr.c
src/worker.c
src/ws.c
src/xhr.c
Expand All @@ -103,6 +117,7 @@ add_library(tjs STATIC
src/bundles/c/core/run-repl.c
src/bundles/c/core/worker-bootstrap.c
deps/quickjs/cutils.c
${WAMR_RUNTIME_LIB_SOURCE}
)

if(UNIX)
Expand Down
1 change: 1 addition & 0 deletions deps/wamr
Submodule wamr added at 213309
51,484 changes: 25,931 additions & 25,553 deletions src/bundles/c/core/polyfills.c

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/js/polyfills/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import './crypto.js';
import './performance.js';
import './storage.js';
import './wasm.js';
import './wamr.js';
import './worker.js';
import './ws.js';

Expand Down
205 changes: 205 additions & 0 deletions src/js/polyfills/wamr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
const core = globalThis[Symbol.for('tjs.internal.core')];
const wasm = core.wamr;

const kWasmModule = Symbol('kWasmModule');
const kWasmModuleRef = Symbol('kWasmModuleRef');
const kWasmExports = Symbol('kWasmExports');
const kWasmInstance = Symbol('kWasmInstance');
const kWasmInstances = Symbol('kWasmInstances');
const kWasiLinked = Symbol('kWasiLinked');
const kWasiStarted = Symbol('kWasiStarted');
const kWasiOptions = Symbol('kWasiOptions');


class CompileError extends Error {
constructor(...args) {
super(...args);
this.name = 'CompileError';
}
}

class LinkError extends Error {
constructor(...args) {
super(...args);
this.name = 'LinkError';
}
}

class RuntimeError extends Error {
constructor(...args) {
super(...args);
this.name = 'RuntimeError';
}
}


function getWasmError(e) {
switch (e.wasmError) {
case 'CompileError':
return new CompileError(e.message);
case 'LinkError':
return new LinkError(e.message);
case 'RuntimeError':
return new RuntimeError(e.message);
default:
return new TypeError(`Invalid WASM error: ${e.wasmError}`);
}
}

function callWasmFunction(name, ...args) {
const instance = this;

try {
return instance.callFunction(name, ...args);
} catch (e) {
if (e.wasmError) {
throw getWasmError(e);
} else {
throw e;
}
}
}

function buildInstance(mod) {
try {
return wasm.buildInstance(mod);
} catch (e) {
if (e.wasmError) {
throw getWasmError(e);
} else {
throw e;
}
}
}

function linkWasi(instance) {
try {
instance.linkWasi();
} catch (e) {
if (e.wasmError) {
throw getWasmError(e);
} else {
throw e;
}
}
}

function parseModule(buf) {
try {
return wasm.parseModule(buf);
} catch (e) {
if (e.wasmError) {
throw getWasmError(e);
} else {
throw e;
}
}
}

class Module {
constructor(buf) {
this[kWasmModule] = parseModule(buf);
}

static exports(module) {
return wasm.moduleExports(module[kWasmModule]);
}

// eslint-disable-next-line no-unused-vars
static imports(module) {
// TODO.
return {};
}
}

class Instance {
constructor(module, importObject = {}) {
const instance = buildInstance(module[kWasmModule]);

if (importObject.wasi_unstable) {
linkWasi(instance);
this[kWasiLinked] = true;
}

const _exports = Module.exports(module);
const exports = Object.create(null);

for (const item of _exports) {
if (item.kind === 'function') {
exports[item.name] = callWasmFunction.bind(instance, item.name);
}
}

this[kWasmInstance] = instance;
this[kWasmExports] = Object.freeze(exports);
this[kWasmModuleRef] = module;
globalThis.WebAssembly[kWasmInstances].push(this);
}

get exports() {
return this[kWasmExports];
}
}

class WASI {
wasiImport = 'w4s1'; // Doesn't matter right now.

constructor(options = { args: [], env: {}, preopens: {} }) {
this[kWasiStarted] = false;

if (options === null || typeof options !== 'object') {
throw new TypeError('options must be an object');
}

this[kWasiOptions] = JSON.parse(JSON.stringify(options));
}

start(instance) {
if (this[kWasiStarted]) {
throw new Error('WASI instance has already started');
}

if (!instance[kWasiLinked]) {
throw new Error('WASM instance doesn\'t have WASI linked');
}

if (!instance.exports._start) {
throw new TypeError('WASI entrypoint not found');
}

this[kWasiStarted] = true;
instance.exports._start(...(this[kWasiOptions].args ?? []));
}
}

class WebAssembly {
Module = Module;
Instance = Instance;
CompileError = CompileError;
LinkError = LinkError;
RuntimeError = RuntimeError;
WASI = WASI;

constructor() {
this[kWasmInstances] = [];
}

async compile(src) {
return new Module(src);
}

async instantiate(src, importObject) {
const module = await this.compile(src);
const instance = new Instance(module, importObject);

return { module, instance };
}
}


Object.defineProperty(globalThis, 'WebAssemblyWAMR', {
enumerable: true,
configurable: true,
writable: true,
value: new WebAssembly()
});
30 changes: 30 additions & 0 deletions src/wamr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "wamr.h"

#include "private.h"
#include "tjs.h"
#include "utils.h"

void testfn(){
char *buffer, error_buf[128];
wasm_module_t module;
wasm_module_inst_t module_inst;
wasm_function_inst_t func;
wasm_exec_env_t exec_env;
uint32 size, stack_size = 8092, heap_size = 8092;

/* initialize the wasm runtime by default configurations */
wasm_runtime_init();

/* read WASM file into a memory buffer */
//buffer = read_wasm_binary_to_buffer(…, &size);

/* add line below if we want to export native functions to WASM app */
//wasm_runtime_register_natives(...);

/* parse the WASM file from buffer and create a WASM module */
module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf));

/* create an instance of the WASM module (WASM linear memory is ready) */
module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
error_buf, sizeof(error_buf));
}
8 changes: 8 additions & 0 deletions src/wamr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef TJS_WAMR_H
#define TJS_WAMR_H

#include <wasm_c_api.h>
#include <wasm_runtime_common.h>
#include <wasm_loader_common.h>

#endif
Loading