Commit 8e8ac26b authored by Jason Stafford's avatar Jason Stafford

first cut at supporting web workers

parent 61512f17
......@@ -183,7 +183,7 @@ EM_JS(void, hiwire_push_object_pair, (int idobj, int idkey, int idval), {
EM_JS(int, hiwire_get_global, (int idname), {
var jsname = UTF8ToString(idname);
return Module.hiwire_new_value(window[jsname]);
return Module.hiwire_new_value(self[jsname]);
});
EM_JS(int, hiwire_get_member_string, (int idobj, int idkey), {
......
......@@ -6,7 +6,7 @@ var languagePluginLoader = new Promise((resolve, reject) => {
// This is filled in by the Makefile to be either a local file or the
// deployed location. TODO: This should be done in a less hacky
// way.
var baseURL = window.languagePluginUrl || '{{DEPLOY}}';
var baseURL = self.languagePluginUrl || '{{DEPLOY}}';
baseURL = baseURL.substr(0, baseURL.lastIndexOf('/')) + '/';
////////////////////////////////////////////////////////////
......@@ -50,7 +50,7 @@ var languagePluginLoader = new Promise((resolve, reject) => {
} catch {
return;
}
for (entry of dirs) {
for (let entry of dirs) {
if (entry.startsWith('.')) {
continue;
}
......@@ -76,10 +76,31 @@ var languagePluginLoader = new Promise((resolve, reject) => {
}
// clang-format on
function loadScript(url, onload, onerror) {
if (self.document) { // browser
const script = self.document.createElement('script');
script.src = url;
script.onload = (e) => {
onload();
};
script.onerror = (e) => {
onerror();
};
self.document.body.appendChild(script);
} else if (self.importScripts) { // webworker
try {
self.importScripts(url);
onload();
} catch {
onerror();
}
}
}
let _loadPackage = (names, messageCallback) => {
// DFS to find all dependencies of the requested packages
let packages = window.pyodide._module.packages.dependencies;
let loadedPackages = window.pyodide.loadedPackages;
let packages = self.pyodide._module.packages.dependencies;
let loadedPackages = self.pyodide.loadedPackages;
let queue = [].concat(names || []);
let toLoad = new Array();
while (queue.length) {
......@@ -124,7 +145,7 @@ var languagePluginLoader = new Promise((resolve, reject) => {
}
}
window.pyodide._module.locateFile = (path) => {
self.pyodide._module.locateFile = (path) => {
// handle packages loaded from custom URLs
let package = path.replace(/\.data$/, "");
if (package in toLoad) {
......@@ -152,14 +173,14 @@ var languagePluginLoader = new Promise((resolve, reject) => {
// exactly "toLoad * 2" times.
var packageCounter = Object.keys(toLoad).length * 2;
window.pyodide._module.monitorRunDependencies = () => {
self.pyodide._module.monitorRunDependencies = () => {
packageCounter--;
if (packageCounter === 0) {
for (let package in toLoad) {
window.pyodide.loadedPackages[package] = toLoad[package];
self.pyodide.loadedPackages[package] = toLoad[package];
}
delete window.pyodide._module.monitorRunDependencies;
window.removeEventListener('error', windowErrorHandler);
delete self.pyodide._module.monitorRunDependencies;
self.removeEventListener('error', windowErrorHandler);
if (!isFirefox) {
preloadWasm().then(() => {resolve(`Loaded ${packageList}`)});
} else {
......@@ -171,42 +192,41 @@ var languagePluginLoader = new Promise((resolve, reject) => {
// Add a handler for any exceptions that are thrown in the process of
// loading a package
var windowErrorHandler = (err) => {
delete window.pyodide._module.monitorRunDependencies;
window.removeEventListener('error', windowErrorHandler);
delete self.pyodide._module.monitorRunDependencies;
self.removeEventListener('error', windowErrorHandler);
// Set up a new Promise chain, since this one failed
loadPackagePromise = new Promise((resolve) => resolve());
reject(err.message);
};
window.addEventListener('error', windowErrorHandler);
self.addEventListener('error', windowErrorHandler);
for (let package in toLoad) {
let script = document.createElement('script');
let scriptSrc;
let package_uri = toLoad[package];
if (package_uri == 'default channel') {
script.src = `${baseURL}${package}.js`;
scriptSrc = `${baseURL}${package}.js`;
} else {
script.src = `${package_uri}`;
scriptSrc = `${package_uri}`;
}
script.onerror = (e) => {
loadScript(scriptSrc, () => {}, () => {
// If the package_uri fails to load, call monitorRunDependencies twice
// (so packageCounter will still hit 0 and finish loading), and remove
// the package from toLoad so we don't mark it as loaded.
console.error(`Couldn't load package from URL ${script.src}`)
console.error(`Couldn't load package from URL ${scriptSrc}`)
let index = toLoad.indexOf(package);
if (index !== -1) {
toLoad.splice(index, 1);
}
for (let i = 0; i < 2; i++) {
window.pyodide._module.monitorRunDependencies();
self.pyodide._module.monitorRunDependencies();
}
};
document.body.appendChild(script);
});
}
// We have to invalidate Python's import caches, or it won't
// see the new files. This is done here so it happens in parallel
// with the fetching over the network.
window.pyodide.runPython('import importlib as _importlib\n' +
self.pyodide.runPython('import importlib as _importlib\n' +
'_importlib.invalidate_caches()\n');
});
......@@ -275,7 +295,7 @@ var languagePluginLoader = new Promise((resolve, reject) => {
// Loading Pyodide
let wasmURL = `${baseURL}pyodide.asm.wasm`;
let Module = {};
window.Module = Module;
self.Module = Module;
Module.noImageDecoding = true;
Module.noAudioDecoding = true;
......@@ -303,15 +323,15 @@ var languagePluginLoader = new Promise((resolve, reject) => {
Module.locateFile = (path) => baseURL + path;
var postRunPromise = new Promise((resolve, reject) => {
Module.postRun = () => {
delete window.Module;
delete self.Module;
fetch(`${baseURL}packages.json`)
.then((response) => response.json())
.then((json) => {
fixRecursionLimit(window.pyodide);
window.pyodide.globals =
window.pyodide.runPython('import sys\nsys.modules["__main__"]');
window.pyodide = makePublicAPI(window.pyodide, PUBLIC_API);
window.pyodide._module.packages = json;
fixRecursionLimit(self.pyodide);
self.pyodide.globals =
self.pyodide.runPython('import sys\nsys.modules["__main__"]');
self.pyodide = makePublicAPI(self.pyodide, PUBLIC_API);
self.pyodide._module.packages = json;
resolve();
});
};
......@@ -329,28 +349,23 @@ var languagePluginLoader = new Promise((resolve, reject) => {
Promise.all([ postRunPromise, dataLoadPromise ]).then(() => resolve());
let data_script = document.createElement('script');
data_script.src = `${baseURL}pyodide.asm.data.js`;
data_script.onload = (event) => {
let script = document.createElement('script');
script.src = `${baseURL}pyodide.asm.js`;
script.onload = () => {
const data_script_src = `${baseURL}pyodide.asm.data.js`;
loadScript(data_script_src, () => {
const scriptSrc = `${baseURL}pyodide.asm.js`;
loadScript(scriptSrc, () => {
// The emscripten module needs to be at this location for the core
// filesystem to install itself. Once that's complete, it will be replaced
// by the call to `makePublicAPI` with a more limited public API.
window.pyodide = pyodide(Module);
window.pyodide.loadedPackages = new Array();
window.pyodide.loadPackage = loadPackage;
};
document.head.appendChild(script);
};
document.head.appendChild(data_script);
self.pyodide = pyodide(Module);
self.pyodide.loadedPackages = new Array();
self.pyodide.loadPackage = loadPackage;
}, () => {});
}, () => {});
////////////////////////////////////////////////////////////
// Iodide-specific functionality, that doesn't make sense
// if not using with Iodide.
if (window.iodide !== undefined) {
if (self.iodide !== undefined) {
// Load the custom CSS for Pyodide
let link = document.createElement('link');
link.rel = 'stylesheet';
......@@ -359,7 +374,7 @@ var languagePluginLoader = new Promise((resolve, reject) => {
document.getElementsByTagName('head')[0].appendChild(link);
// Add a custom output handler for Python objects
window.iodide.addOutputHandler({
self.iodide.addOutputHandler({
shouldHandle : (val) => {
return (typeof val === 'function' &&
pyodide._module.PyProxy.isPyProxy(val));
......
......@@ -159,10 +159,10 @@ EM_JS(int, pyproxy_init, (), {
get: function (jsobj, jskey) {
if (jskey === 'toString') {
return function() {
if (window.pyodide.repr === undefined) {
window.pyodide.repr = window.pyodide.pyimport('repr');
if (self.pyodide.repr === undefined) {
self.pyodide.repr = self.pyodide.pyimport('repr');
}
return window.pyodide.repr(jsobj);
return self.pyodide.repr(jsobj);
}
} else if (jskey === '$$') {
return jsobj['$$'];
......
......@@ -88,7 +88,7 @@ EM_JS(int, runpython_init_js, (), {
if (jsimports.length) {
var packageNames =
window.pyodide._module.packages.import_name_to_package_name;
self.pyodide._module.packages.import_name_to_package_name;
var packages = {};
for (var i = 0; i < jsimports.length; ++i) {
var name = jsimports[i];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment