Skip to content

Commit

Permalink
Move stack switching logic into a new stack switching folder. (#3987)
Browse files Browse the repository at this point in the history
This is a minor rearrangment.

I renamed "continuations" stuff to "stack_switching" and made a new folder
for stack switching logic. I also added logic in esbuild.config.mjs to calculate
which functions exported from stack_switching.mjs and automatically copy them to
Module and into the Emscripten namespace. This makes unit testing a little neater
since there is no modification of the Module object.
  • Loading branch information
hoodmane committed Sep 13, 2023
1 parent 8861b31 commit b2c47ae
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 30 deletions.
13 changes: 7 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ all: check \
dist/module_webworker_dev.js
echo -e "\nSUCCESS!"

src/core/pyodide_pre.o: src/js/_pyodide.out.js src/core/pre.js src/core/create_invokes.out.js
src/core/pyodide_pre.o: src/js/_pyodide.out.js src/core/pre.js src/core/stack_switching/stack_switching.out.js
# Our goal here is to inject src/js/_pyodide.out.js into an archive file so that
# when linked, Emscripten will include it. We use the same pathway that EM_JS
# uses, but EM_JS is itself unsuitable. Why? Because the C preprocessor /
Expand Down Expand Up @@ -57,7 +57,7 @@ src/core/pyodide_pre.o: src/js/_pyodide.out.js src/core/pre.js src/core/create_i
echo '()<::>{' >> tmp.dat # zero argument argspec and start body
cat src/js/_pyodide.out.js >> tmp.dat # All of _pyodide.out.js is body
echo '}' >> tmp.dat # Close function body
cat src/core/create_invokes.out.js >> tmp.dat
cat src/core/stack_switching/stack_switching.out.js >> tmp.dat
cat src/core/pre.js >> tmp.dat # And pre.js
echo "pyodide_js_init();" >> tmp.dat # Then execute the function.

Expand Down Expand Up @@ -129,8 +129,8 @@ node_modules/.installed : src/js/package.json src/js/package-lock.json
dist/pyodide.js src/js/_pyodide.out.js: src/js/*.ts src/js/pyproxy.gen.ts src/js/error_handling.gen.ts node_modules/.installed
cd src/js && npm run tsc && node esbuild.config.mjs && cd -

src/core/create_invokes.out.js : src/core/create_invokes.mjs src/core/runtime_wasm.mjs
cd src/core && npx esbuild --bundle create_invokes.mjs --outfile=create_invokes.out.js
src/core/stack_switching/stack_switching.out.js : src/core/stack_switching/*.mjs
node src/core/stack_switching/esbuild.config.mjs

dist/package.json : src/js/package.json
cp $< $@
Expand Down Expand Up @@ -226,9 +226,10 @@ benchmark: all

clean:
rm -fr dist/*
rm -fr src/*/*.o
rm -fr src/*/*.gen.*
rm -fr node_modules
find src -name '*.o' -delete
find src -name '*.gen.*' -delete
find src -name '*.out.*' -delete
make -C packages clean
echo "The Emsdk, CPython are not cleaned. cd into those directories to do so."

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ import {
typeCodes,
} from "./runtime_wasm.mjs";

if (typeof Module === "undefined") {
globalThis.Module = {};
}

/**
* These produce the following pseudocode:
* ```
Expand Down Expand Up @@ -136,15 +132,13 @@ export function createInvokeModule(sig) {
return mod.generate();
}

let jsWrapperTag;
export let jsWrapperTag;
try {
jsWrapperTag = new WebAssembly.Tag({ parameters: ["externref"] });
Module.jsWrapperTag = jsWrapperTag;
} catch (e) {}

if (jsWrapperTag) {
Module.wrapException = (e) => new WebAssembly.Exception(jsWrapperTag, [e]);
}
export const wrapException = (e) =>
new WebAssembly.Exception(jsWrapperTag, [e]);

function createInvoke(sig) {
if (!jsWrapperTag) {
Expand All @@ -162,19 +156,16 @@ function createInvoke(sig) {
});
return instance.exports["o"];
}
Module.createInvoke = createInvoke;

// We patched Emscripten to call this function on the wasmImports just prior to
// wasm instantiation if it is defined. It replaces all invoke functions with
// our Wasm invokes if wasm EH is available.
Module.adjustWasmImports = function (wasmImports) {
if (jsWrapperTag) {
const i = "invoke_";
for (let name of Object.keys(wasmImports)) {
if (!name.startsWith(i)) {
continue;
}
wasmImports[name] = createInvoke(name.slice(i.length));
export function adjustWasmImports(wasmImports) {
const i = "invoke_";
for (let name of Object.keys(wasmImports)) {
if (!name.startsWith(i)) {
continue;
}
wasmImports[name] = createInvoke(name.slice(i.length));
}
};
}
44 changes: 44 additions & 0 deletions src/core/stack_switching/esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Bundle the stack switching folder as iife and then export names exported from
* stack_switching.mjs onto Module and into the Emscripten namespace.
*/

import { build } from "esbuild";
import { dirname, join } from "node:path";

const __dirname = dirname(new URL(import.meta.url).pathname);

const outfile = join(__dirname, "stack_switching.out.js");
const globalName = "StackSwitching";

const config = {
entryPoints: [join(__dirname, "stack_switching.mjs")],
outfile,
format: "iife",
bundle: true,
globalName,
metafile: true,
};

// First build as "esm" to get the list of exports. The metafile doesn't list
// exports except when we set `format: "esm"`. Setting bundle: false saves a
// tiny amount of time on this pass.
const { metafile } = await build(
Object.assign({}, config, { format: "esm", bundle: false }),
);

// The file name is the metafile.outputs key. It is relative to the current
// working directory, so it's annoying to work it out. There will only be one
// key in any case, so we just extract it with Object.values().
const exports = Object.values(metafile.outputs)[0].exports;

// Add a footer that destructures the exports into the Emscripten namespace.
// Also Object.assign them onto Module.
const footer = `
const {${exports}} = ${globalName};
Object.assign(Module, ${globalName});
`.replaceAll(/\s/g, "");
config.footer = { js: footer };

// Build again, this time as an iife bundle with our extra footer.
await build(config);
File renamed without changes.
17 changes: 17 additions & 0 deletions src/core/stack_switching/stack_switching.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Files exported from here are copied into the Emscripten namespace.
* See esbuild.config.mjs.
*/

import {
jsWrapperTag,
wrapException,
adjustWasmImports,
} from "./create_invokes.mjs";

export { jsWrapperTag };

if (jsWrapperTag) {
Module.adjustWasmImports = adjustWasmImports;
Module.wrapException = wrapException;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { expect } from "chai";

import { readFileSync, writeFileSync } from "node:fs";
import * as vm from "node:vm";
import { readFileSync } from "node:fs";
import loadWabt from "wabt";
import { URL } from "url"; // in Browser, the URL in native accessible on window
import {
Expand All @@ -12,8 +11,8 @@ import {
TypeSection,
WASM_PRELUDE,
insertSectionPrefix,
} from "../../../core/runtime_wasm.mjs";
import { createInvokeModule } from "../../../core/create_invokes.mjs";
} from "../../../core/stack_switching/runtime_wasm.mjs";
import { createInvokeModule } from "../../../core/stack_switching/create_invokes.mjs";

const __dirname = new URL(".", import.meta.url).pathname;

Expand All @@ -29,7 +28,7 @@ function fromWat(wat) {
function fromWatFile(file) {
return parseWat(
file,
readFileSync(__dirname + "invokes/" + file, { encoding: "utf8" }),
readFileSync(__dirname + "wat/" + file, { encoding: "utf8" }),
{
mutable_globals: true,
exceptions: true,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit b2c47ae

Please sign in to comment.