Hi All,
I’m trying to wrap a module that I generated using Emscripten. Originally, I compiled with default options and could import using the following:
libtn93 = new Promise(resolve => {
require("https:/cdn.rawgit.com/sdwfrost/libtn93/master/emscripten/libtn93.js").catch(()=>resolve(window.Module));
})
However, this strategy didn’t allow me to load multiple modules, so I now compile with -s EXPORT_NAME="'libtn93'" -s MODULARIZE=1
, which results in all of the generated JavaScript in a function.
var libtn93 = function(libtn93) {
libtn93 = libtn93 || {};
var Module=typeof libtn93!=="undefined"?libtn93:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=(function(status,toThrow){throw toThrow});Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER}if(ENVIRONMENT_IS_NODE){var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){var ret;ret=tryParseAsDataURI(filename);if(!ret){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);ret=nodeFS["readFileSync"](filename)}return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["arguments"]=process["argv"].slice(2);process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));process["on"]("unhandledRejection",(function(reason,p){Module["printErr"]("node.js exiting due to unhandled promise rejection");process["exit"](1)}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){var data=tryParseAsDataURI(f);if(data){return intArrayToString(data)}return read(f)}}Module["readBinary"]=function readBinary(f){var data;data=tryParseAsDataURI(f);if(data){return data}if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status,toThrow){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function shell_read(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText}catch(err){var data=tryParseAsDataURI(url);if(data){return intArrayToString(data)}throw err}};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}catch(err){var data=tryParseAsDataURI(url);if(data){return data}throw err}}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}var data=tryParseAsDataURI(url);if(data){onload(data.buffer);return}onerror()};xhr.onerror=onerror;xhr.send(null)};if(typeof arguments!="undefined"){Module["arguments"]=arguments}Module["setWindowTitle"]=(function(title){document.title=title})}else{throw new Error("unknown runtime environment")}Module["print"]=typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null;Module["printErr"]=typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||Module["print"];Module.print=Module["print"];Module.printErr=Module["printErr"];for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var STACK_ALIGN=16;stackSave=stackRestore=stackAlloc=setTempRet0=getTempRet0=(function(){abort("cannot use the stack before compiled code is ready to run, and has provided stack access")});function staticAlloc(size){assert(!staticSealed);var ret=STATICTOP;STATICTOP=STATICTOP+size+15&-16;return ret}function dynamicAlloc(size){assert(DYNAMICTOP_PTR);var ret=HEAP32[DYNAMICTOP_PTR>>2];var end=ret+size+15&-16;HEAP32[DYNAMICTOP_PTR>>2]=end;if(end>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){HEAP32[DYNAMICTOP_PTR>>2]=ret;return 0}}return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;var ret=size=Math.ceil(size/factor)*factor;retu
This file has been truncated. show original
In Node, I can just do this:
libtn93 = require("./libtn93.js")();
I also have a WASM file that requires slightly different treatment as the WebAssembly is loaded asynchronously:
libtn93 = require("./libtn93wasm.js")().then(function(Module){
});
I’ve tried everything I can think of to get either of these imported into Observable, but so far, I’ve had no luck. Any tips?
It appears to “just work”:
libtn93 = require("https://cdn.rawgit.com/sdwfrost/libtn93/master/emscripten/libtn93.js")
However, calling the libtn93
function appears to hang the browser. (And also, the way the script is defined, it also leaks a libtn93
global because the script isn’t wrapped in an immediately-invoked function expression .)
By the way, you can shorten your original code as follows:
libtn93 = require("https:/cdn.rawgit.com/sdwfrost/libtn93/master/emscripten/libtn93.js").catch(() => window.Module)
Sorry for not being clear; my original code does ‘just work’; however, this defines a global ‘Module’ object that prevents me from having multiple Emscripten modules. The new modularised version doesn’t work, however, and though I’m sure I’m making a silly mistake, the following gives Error: unable to load module
libtn93 = require("https:/rawgit.com/sdwfrost/libtn93/master/emscripten/libtn93.js")
and this gives undefined
:
libtn93 = require("https:/rawgit.com/sdwfrost/libtn93/master/emscripten/libtn93.js").catch(()=>{window.libtn93})
This has happened before when I didn’t export the correct name, but this looks fine in the JS…
var libtn93 = function(libtn93) {
libtn93 = libtn93 || {};
...
return libtn93;
};
if (typeof exports === 'object' && typeof module === 'object')
module.exports = libtn93;
else if (typeof define === 'function' && define['amd'])
define([], function() { return libtn93; });
else if (typeof exports === 'object')
exports["libtn93"] = libtn93;
I am sort of having similar problems with importing an emscripten-compiled module into an observable notebook. I paraphrased your code in the following test notebook but for some reason, the module only becomes available if I wait a few milliseconds (see cell m
). Indeed, this is a bit tricky because once the module is loaded, everything works, but if I comment out the line
await Promises.delay(100);
and then reload the notebook, the last cell fails with error “TypeError: Cannot read property ‘apply’ of undefined”.
Incidentally, have you found out a solution for your problem?
You need to wait for the runtime to be ready. Details here:
It works! But I am feeling extremely dense today, as I cannot see how to follow your hint about setting MODULARIZE=1. I followed the FAQ instructions and recompiled my module with
-s MODULARIZE=1 -s 'EXPORT_NAME="MyCode"'
The compiled module is available at [https://www.lcg.ufrj.br/~bfcosta/test4.js ]. How should I require it then?
My tentative notebook is at https://beta.observablehq.com/@bfcosta/webassembly-emscripten-with-modularize
Thanks!