1 Commits
v2.0.5 ... main

Author SHA1 Message Date
=
830c0a7586 update wfa.js,
reduce clobbering of global namespaces
2025-10-02 21:56:39 +00:00
3 changed files with 70 additions and 51 deletions

View File

@@ -1,11 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<script src="dist/wfa.js" type="module"></script> <script src="wfa.js" type="module"></script>
<script type="module"> <script type="module">
import wfaInit from "./dist/wfa.js"; import wfaInit from "./wfa.js";
wfaInit("dist/wfa.wasm");
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
wfaInit("dist/wfa.wasm");
document.querySelector("#submit").addEventListener("click", () => { document.querySelector("#submit").addEventListener("click", () => {
a = document.querySelector("#a").value a = document.querySelector("#a").value
b = document.querySelector("#b").value b = document.querySelector("#b").value
@@ -15,8 +15,8 @@
o: 0, o: 0,
e: 1 e: 1
}; };
const { score, CIGAR } = global.wfAlign(a, b, penalties, true); const { score, CIGAR } = global.wfa.wfAlign(a, b, penalties, true);
const alignment = global.DecodeCIGAR(CIGAR); const alignment = global.wfa.DecodeCIGAR(CIGAR);
document.querySelector("#result").innerText = `${score}, ${CIGAR}, ${alignment}`; document.querySelector("#result").innerText = `${score}, ${CIGAR}, ${alignment}`;
}) })
}); });

View File

@@ -7,8 +7,8 @@ import (
func main() { func main() {
c := make(chan bool) c := make(chan bool)
js.Global().Set("wfAlign", js.FuncOf(wfAlign)) js.Global().Get("wfa").Set("wfAlign", js.FuncOf(wfAlign))
js.Global().Set("DecodeCIGAR", js.FuncOf(DecodeCIGAR)) js.Global().Get("wfa").Set("DecodeCIGAR", js.FuncOf(DecodeCIGAR))
<-c <-c
} }

107
wfa.js
View File

@@ -1,5 +1,3 @@
// wasm_exec.js from tinygo
// Copyright 2018 The Go Authors. All rights reserved. // Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@@ -134,6 +132,7 @@
const decoder = new TextDecoder("utf-8"); const decoder = new TextDecoder("utf-8");
let reinterpretBuf = new DataView(new ArrayBuffer(8)); let reinterpretBuf = new DataView(new ArrayBuffer(8));
var logLine = []; var logLine = [];
const wasmExit = {}; // thrown to exit via proc_exit (not an error)
global.Go = class { global.Go = class {
constructor() { constructor() {
@@ -272,14 +271,11 @@
fd_close: () => 0, // dummy fd_close: () => 0, // dummy
fd_fdstat_get: () => 0, // dummy fd_fdstat_get: () => 0, // dummy
fd_seek: () => 0, // dummy fd_seek: () => 0, // dummy
"proc_exit": (code) => { proc_exit: (code) => {
if (global.process) { this.exited = true;
// Node.js this.exitCode = code;
process.exit(code); this._resolveExitPromise();
} else { throw wasmExit;
// Can't exit in a browser.
throw 'trying to exit with code ' + code;
}
}, },
random_get: (bufPtr, bufLen) => { random_get: (bufPtr, bufLen) => {
crypto.getRandomValues(loadSlice(bufPtr, bufLen)); crypto.getRandomValues(loadSlice(bufPtr, bufLen));
@@ -287,31 +283,45 @@
}, },
}, },
gojs: { gojs: {
// func ticks() float64 // func ticks() int64
"runtime.ticks": () => { "runtime.ticks": () => {
return timeOrigin + performance.now(); return BigInt((timeOrigin + performance.now()) * 1e6);
}, },
// func sleepTicks(timeout float64) // func sleepTicks(timeout int64)
"runtime.sleepTicks": (timeout) => { "runtime.sleepTicks": (timeout) => {
// Do not sleep, only reactivate scheduler after the given timeout. // Do not sleep, only reactivate scheduler after the given timeout.
setTimeout(this._inst.exports.go_scheduler, timeout); setTimeout(() => {
if (this.exited) return;
try {
this._inst.exports.go_scheduler();
} catch (e) {
if (e !== wasmExit) throw e;
}
}, Number(timeout)/1e6);
}, },
// func finalizeRef(v ref) // func finalizeRef(v ref)
"syscall/js.finalizeRef": (v_ref) => { "syscall/js.finalizeRef": (v_ref) => {
const id = mem().getUint32(unboxValue(v_ref), true); // Note: TinyGo does not support finalizers so this is only called
this._goRefCounts[id]--; // for one specific case, by js.go:jsString. and can/might leak memory.
if (this._goRefCounts[id] === 0) { const id = v_ref & 0xffffffffn;
const v = this._values[id]; if (this._goRefCounts?.[id] !== undefined) {
this._values[id] = null; this._goRefCounts[id]--;
this._ids.delete(v); if (this._goRefCounts[id] === 0) {
this._idPool.push(id); const v = this._values[id];
this._values[id] = null;
this._ids.delete(v);
this._idPool.push(id);
}
} else {
console.error("syscall/js.finalizeRef: unknown id", id);
} }
}, },
// func stringVal(value string) ref // func stringVal(value string) ref
"syscall/js.stringVal": (value_ptr, value_len) => { "syscall/js.stringVal": (value_ptr, value_len) => {
value_ptr >>>= 0;
const s = loadString(value_ptr, value_len); const s = loadString(value_ptr, value_len);
return boxValue(s); return boxValue(s);
}, },
@@ -472,21 +482,25 @@
this._ids = new Map(); // mapping from JS values to reference ids this._ids = new Map(); // mapping from JS values to reference ids
this._idPool = []; // unused ids that have been garbage collected this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited this.exited = false; // whether the Go program has exited
this.exitCode = 0;
while (true) { if (this._inst.exports._start) {
const callbackPromise = new Promise((resolve) => { let exitPromise = new Promise((resolve, reject) => {
this._resolveCallbackPromise = () => { this._resolveExitPromise = resolve;
if (this.exited) {
throw new Error("bad callback: Go program has already exited");
}
setTimeout(resolve, 0); // make sure it is asynchronous
};
}); });
this._inst.exports._start();
if (this.exited) { // Run program, but catch the wasmExit exception that's thrown
break; // to return back here.
try {
this._inst.exports._start();
} catch (e) {
if (e !== wasmExit) throw e;
} }
await callbackPromise;
await exitPromise;
return this.exitCode;
} else {
this._inst.exports._initialize();
} }
} }
@@ -494,7 +508,11 @@
if (this.exited) { if (this.exited) {
throw new Error("Go program has already exited"); throw new Error("Go program has already exited");
} }
this._inst.exports.resume(); try {
this._inst.exports.resume();
} catch (e) {
if (e !== wasmExit) throw e;
}
if (this.exited) { if (this.exited) {
this._resolveExitPromise(); this._resolveExitPromise();
} }
@@ -524,8 +542,9 @@
} }
const go = new Go(); const go = new Go();
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => {
return go.run(result.instance); let exitCode = await go.run(result.instance);
process.exit(exitCode);
}).catch((err) => { }).catch((err) => {
console.error(err); console.error(err);
process.exit(1); process.exit(1);
@@ -537,22 +556,22 @@
export default function init (path) { export default function init (path) {
return new Promise ((res) => { return new Promise ((res) => {
const go = new Go(); const go = new Go();
var wasm; const run = (obj) => {
const wasm = obj.instance;
global.wfa = wasm
go.run(wasm);
res()
}
if ('instantiateStreaming' in WebAssembly) { if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(path), go.importObject).then(function (obj) { WebAssembly.instantiateStreaming(fetch(path), go.importObject).then(function (obj) {
wasm = obj.instance; run(obj)
go.run(wasm);
res()
}) })
} else { } else {
fetch(path).then(resp => fetch(path).then(resp =>
resp.arrayBuffer() resp.arrayBuffer()
).then(bytes => ).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) { WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
wasm = obj.instance; run(obj)
go.run(wasm);
res()
}) })
) )
} }