Home Reference Source Test

src/main/generic/utils/WasmHelper.js

class WasmHelper {

    static async doImportBrowser() {
        if (PlatformUtils.isNodeJs()) return;
        if (WasmHelper._importStarted) {
            Log.e(WasmHelper, 'doImportBrowser invoked twice');
            return;
        }
        WasmHelper._importStarted = true;
        if (await WasmHelper.importWasmBrowser('worker-wasm.wasm')) {
            await WasmHelper.importScriptBrowser('worker-wasm.js');
        } else {
            await WasmHelper.importScriptBrowser('worker-js.js');
        }
        WasmHelper._importFinished = true;
    }

    static doImportNodeJs() {
        if (!PlatformUtils.isNodeJs()) return;
        if (WasmHelper.importWasmNodeJs('worker-wasm.wasm')) {
            WasmHelper.importScriptNodeJs('worker-wasm.js');
        } else {
            WasmHelper.importScriptNodeJs('worker-js.js');
        }
    }

    /**
     * @param {string} wasm
     * @param {string} module
     * @returns {Promise.<boolean>}
     */
    static importWasmBrowser(wasm, module = 'Module') {
        wasm = WasmHelper._adjustWasmPath(wasm);
        if (!WasmHelper._global.WebAssembly) {
            Log.w(WasmHelper, 'No support for WebAssembly available.');
            return Promise.resolve(false);
        }

        return new Promise((resolve) => {
            try {
                const xhr = new XMLHttpRequest();
                xhr.open('GET', wasm, true);
                xhr.responseType = 'arraybuffer';
                xhr.onload = function () {
                    WasmHelper._global[module] = WasmHelper._global[module] || {};
                    WasmHelper._global[module].wasmBinary = xhr.response;
                    resolve(true);
                };
                xhr.onerror = function () {
                    Log.w(WasmHelper, `Failed to access WebAssembly module ${wasm}`);
                    resolve(false);
                };
                xhr.send(null);
            } catch (e) {
                Log.w(WasmHelper, `Failed to access WebAssembly module ${wasm}`);
                resolve(false);
            }
        });
    }

    static importWasmNodeJs(wasm, module = 'Module') {
        wasm = WasmHelper._adjustWasmPath(wasm);
        if (!WasmHelper._global.WebAssembly) {
            Log.w(WasmHelper, 'No support for WebAssembly available.');
            return false;
        }

        const toUint8Array = function (buf) {
            const u = new Uint8Array(buf.length);
            for (let i = 0; i < buf.length; ++i) {
                u[i] = buf[i];
            }
            return u;
        };
        const fs = require('fs');
        try {
            const data = fs.readFileSync(wasm);
            WasmHelper._global[module] = WasmHelper._global[module] || {};
            WasmHelper._global[module].wasmBinary = toUint8Array(data);
            return true;
        } catch (e) {
            Log.w(WasmHelper, `Failed to access WebAssembly module ${wasm}: ${e}`);
            return false;
        }
    }

    static importScriptBrowser(script, module = 'Module') {
        if (module && WasmHelper._global[module] && WasmHelper._global[module].asm) return false;
        script = WasmHelper._adjustScriptPath(script);

        const moduleSettings = WasmHelper._global[module] || {};
        return new Promise(async (resolve, reject) => {
            if (module) {
                moduleSettings.onRuntimeInitialized = () => resolve(true);
            }
            if (typeof importScripts === 'function') {
                await new Promise((resolve) => {
                    WasmHelper._moduleLoadedCallbacks[module] = resolve;
                    importScripts(script);
                });
                WasmHelper._global[module] = WasmHelper._global[module](moduleSettings);
                if (!module) resolve(true);
            } else if (typeof window === 'object') {
                await new Promise((resolve) => {
                    WasmHelper._moduleLoadedCallbacks[module] = resolve;
                    WasmHelper._loadBrowserScript(script);
                });
                WasmHelper._global[module] = WasmHelper._global[module](moduleSettings);
                if (!module) resolve(true);
            } else if (typeof require === 'function') {
                WasmHelper._global[module] = require(script)(moduleSettings);
                if (!module) resolve(true);
            } else {
                reject('No way to load scripts.');
            }
        });
    }

    static importScriptNodeJs(script, module = 'Module') {
        if (module && WasmHelper._global[module] && WasmHelper._global[module].asm) return false;
        script = WasmHelper._adjustScriptPath(script);

        const moduleSettings = WasmHelper._global[module] || {};

        if (typeof require === 'function') {
            WasmHelper._global[module] = require(script)(moduleSettings);
            if (!module) return true;
        }
        return false;
    }

    static fireModuleLoaded(module = 'Module') {
        if (typeof WasmHelper._moduleLoadedCallbacks[module] === 'function') {
            WasmHelper._moduleLoadedCallbacks[module]();
            WasmHelper._moduleLoadedCallbacks[module] = null;
        }
    }

    static _loadBrowserScript(url) {
        const head = document.getElementsByTagName('head')[0];
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url;
        head.appendChild(script);
    }

    static _adjustWasmPath(wasm) {
        if (typeof Nimiq !== 'undefined' && Nimiq._path) wasm = `${Nimiq._path}${wasm}`;
        if (typeof __dirname === 'string' && wasm.indexOf('/') === -1) wasm = `${__dirname}/${wasm}`;
        return wasm;
    }

    static _adjustScriptPath(script) {
        if (typeof Nimiq !== 'undefined' && Nimiq._path) script = `${Nimiq._path}${script}`;
        if (typeof __dirname === 'string' && script.indexOf('/') === -1) script = `${__dirname}/${script}`;
        return script;
    }

    static get _global() {
        return typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : null;
    }
}
WasmHelper._moduleLoadedCallbacks = {};

Class.register(WasmHelper);