Files
pyscript/pyscript.core/esm/worker/_template.js
2023-06-12 22:18:55 +02:00

100 lines
3.4 KiB
JavaScript

// ⚠️ This file is used to generate xworker.js
// That means if any import is circular or brings in too much
// that would be a higher payload for every worker.
// Please check via `npm run size` that worker code is not much
// bigger than it used to be before any changes is applied to this file.
import coincident from "coincident/structured";
import { create } from "../utils.js";
import { registry } from "../runtimes.js";
import { getRuntime, getRuntimeID } from "../loader.js";
// bails out out of the box with a native/meaningful error
// in case the SharedArrayBuffer is not available
try {
new SharedArrayBuffer(4);
} catch (_) {
throw new Error(
[
"Unable to use SharedArrayBuffer due insecure environment.",
"Please read requirements in MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements",
].join("\n"),
);
}
let engine, run, runtimeEvent;
const add = (type, fn) => {
addEventListener(
type,
fn ||
(async (event) => {
const runtime = await engine;
runtimeEvent = event;
run(runtime, `xworker.on${type}(xworker.event);`, xworker);
}),
!!fn && { once: true },
);
};
const xworker = {
// allows synchronous utilities between this worker and the main thread
sync: coincident(self),
// standard worker related events / features
onerror() {},
onmessage() {},
onmessageerror() {},
postMessage: postMessage.bind(self),
// this getter exists so that arbitrarily access to xworker.event
// would always fail once an event has been dispatched, as that's not
// meant to be accessed in the wild, respecting the one-off event nature of JS.
get event() {
const event = runtimeEvent;
if (!event) throw new Error("Unauthorized event access");
runtimeEvent = void 0;
return event;
},
};
add("message", ({ data: { options, code, hooks } }) => {
engine = (async () => {
const { type, version, config, async: isAsync } = options;
const engine = await getRuntime(getRuntimeID(type, version), config);
const details = create(registry.get(type));
const name = `runWorker${isAsync ? "Async" : ""}`;
// patch code if needed
const { beforeRun, beforeRunAsync, afterRun, afterRunAsync } = hooks;
const after = afterRun || afterRunAsync;
const before = beforeRun || beforeRunAsync;
// append code that should be executed *after* first
if (after) {
const method = details[name];
details[name] = function (runtime, code, xworker) {
return method.call(this, runtime, `${code}\n${after}`, xworker);
};
}
// prepend code that should be executed *before* (so that after is post-patched)
if (before) {
const method = details[name];
details[name] = function (runtime, code, xworker) {
return method.call(
this,
runtime,
`${before}\n${code}`,
xworker,
);
};
}
run = details[name].bind(details);
run(engine, code, xworker);
return engine;
})();
add("error");
add("message");
add("messageerror");
});