"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var e_1, _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WasmInstance = void 0;
var hof_1 = require("async-await-utils/hof");
var wasi_1 = require("@wasmer/wasi");
var fs_1 = require("fs");
var util_1 = require("util");
var path_1 = require("path");
var callsite_1 = __importDefault(require("callsite"));
var fs = __importStar(require("fs"));
var nodeBindings = __importStar(require("@wasmer/wasi/lib/bindings/node"));
var readFile = (0, util_1.promisify)(fs_1.readFile);
var STUBS = "main raise setjmp longjmp pclose popen getuid getpid getpwuid getpwnam geteuid system dlerror dlsym dlopen signal clock pthread_condattr_init pthread_condattr_setclock pthread_cond_init pthread_attr_init pthread_attr_setstacksize pthread_attr_destroy pthread_create pthread_detach pthread_self pthread_exit pthread_mutex_init pthread_cond_destroy pthread_mutex_destroy pthread_mutex_trylock pthread_mutex_lock pthread_cond_timedwait pthread_cond_wait pthread_mutex_unlock pthread_cond_signal pthread_key_create pthread_key_delete pthread_setspecific pthread_getspecific pthread_kill __SIG_IGN __SIG_ERR setitimer getitimer strsignal siginterrupt pause sigemptyset  sigaddset  ttyname_r  lchflags  chflags  fchmod  lchmod  fchmodat  chmod  fchown  lchown  fchownat  chown  ctermid  nice  getpriority  setpriority  copy_file_range  system  umask  times  execv  fexecve  execve  fork  sched_get_priority_max  sched_get_priority_min  sched_getparam  sched_getscheduler  sched_rr_get_interval  sched_setparam  sched_setscheduler  openpty  forkpty  getegid  geteuid  getgid  getgrouplist  getgroups alarm getpgrp getppid getlogin kill killpg plock setuid seteuid setreuid setgid setegid setregid setgroups initgroups getpgid setpgrp wait waitid waitpid getsid setsid setpgid tcgetpgrp tcsetpgrp fdwalk dup3 dup2 lockf preadv2 pwritev2 sendfile pipe2 pipe mkfifoat mkfifo mknodat mknod flockfile funlockfile sync fstatvfs statvfs getloadavg  setresuid setresgid getresuid getresgid memfd_create posix_spawn_file_actions_init posix_spawn_file_actions_addopen posix_spawn_file_actions_addclose posix_spawn_file_actions_adddup2 posix_spawnattr_init posix_spawnattr_setpgroup posix_spawnattr_setsigmask posix_spawnattr_setsigdefault posix_spawnattr_setschedpolicy posix_spawnattr_setschedparam posix_spawnattr_setflags posix_spawnp posix_spawn posix_spawnattr_destroy posix_spawn_file_actions_destroy pthread_sigmask sigpending sigwait sigwaitinfo sigtimedwait sigfillset sigismember getpwuid_r getpwnam_r setpwent getpwent endpwent clock_settime pthread_getcpuclockid getrusage clock gettext dgettext dcgettext textdomain bindtextdomain bind_textdomain_codeset sigaction sigaltstack getrlimit setrlimit dlsym dlopen dlerror realpath dup";
function stub(_name) {
    return function () {
        // console.log(`stub.${_name}`, arguments);
        return "0";
    };
}
var wasmEnv = {};
try {
    for (var _b = __values(STUBS.split(" ")), _c = _b.next(); !_c.done; _c = _b.next()) {
        var name_1 = _c.value;
        if (wasmEnv[name_1] != null)
            continue;
        wasmEnv[name_1] = stub(name_1);
    }
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
    try {
        if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
    }
    finally { if (e_1) throw e_1.error; }
}
var textDecoder = new TextDecoder();
function recvString(wasm, ptr, len) {
    var slice = wasm.exports.memory.buffer.slice(ptr, ptr + len);
    return textDecoder.decode(slice);
}
var cache = {};
var wasmImport = (0, hof_1.reuseInFlight)(function wasmImport(name, options) {
    var _a, _b;
    if (options === void 0) { options = {}; }
    return __awaiter(this, void 0, void 0, function () {
        var t, pathToWasm, wasmOpts, wasm, wasi, opts, source, typedArray, result;
        var _c;
        return __generator(this, function (_d) {
            switch (_d.label) {
                case 0:
                    if (!options.noCache) {
                        if (cache[name] != null) {
                            return [2 /*return*/, cache[name]];
                        }
                    }
                    t = new Date().valueOf();
                    if (!name.startsWith("/")) {
                        name = (0, path_1.join)((0, path_1.dirname)((_b = (_a = (0, callsite_1.default)()[1]) === null || _a === void 0 ? void 0 : _a.getFileName()) !== null && _b !== void 0 ? _b : ""), name);
                    }
                    pathToWasm = "" + name + (name.endsWith(".wasm") ? "" : ".wasm");
                    wasmEnv.reportError = function (ptr, len) {
                        // @ts-ignore
                        var slice = result.instance.exports.memory.buffer.slice(ptr, ptr + len);
                        var textDecoder = new TextDecoder();
                        throw Error(textDecoder.decode(slice));
                    };
                    wasmOpts = { env: __assign(__assign({}, wasmEnv), options.env) };
                    if (wasmOpts.env.wasmSendString == null) {
                        wasmOpts.env.wasmSendString = function (ptr, len) {
                            wasm.result = recvString(wasm, ptr, len);
                        };
                    }
                    wasi = undefined;
                    if (!(options === null || options === void 0 ? void 0 : options.noWasi)) {
                        opts = {
                            args: process.argv,
                            env: process.env,
                            traceSyscalls: options.traceSyscalls,
                        };
                        if (options.dir === null) {
                            // sandbox -- don't give any fs access
                        }
                        else {
                            opts.bindings = __assign(__assign({}, (nodeBindings.default || nodeBindings)), { fs: fs });
                            if (options.dir !== undefined) {
                                // something explicit
                                opts.preopenDirectories = (_c = {}, _c[options.dir] = options.dir, _c);
                            }
                            else {
                                // just give full access; security of fs access isn't
                                // really relevant for us at this point
                                opts.preopenDirectories = { "/": "/" };
                            }
                        }
                        wasi = new wasi_1.WASI(opts);
                        wasmOpts.wasi_snapshot_preview1 = wasi.wasiImport;
                        // It's very important that this actually work properly.
                        // E.g., if you just return 0, then Python startup
                        // hangs at py_getrandom in bootstrap_hash.c.
                        // The wasmer code for random_get is broken in that it returns
                        // 0 for success, but getrandom is supposed to return the number
                        // of random bytes.  It does randomize the buffer though.
                        // I couldn't find this reported upstream.
                        wasmOpts.env.getrandom = function (bufPtr, bufLen, _flags) {
                            //console.log("getrandom", bufPtr, bufLen, _flags);
                            wasi.wasiImport.random_get(bufPtr, bufLen);
                            return bufLen; // what we actually did!
                        };
                    }
                    return [4 /*yield*/, readFile(pathToWasm)];
                case 1:
                    source = _d.sent();
                    typedArray = new Uint8Array(source);
                    return [4 /*yield*/, WebAssembly.instantiate(typedArray, wasmOpts)];
                case 2:
                    result = _d.sent();
                    if (wasi != null) {
                        wasi.start(result.instance);
                    }
                    if (result.instance.exports.__wasm_call_ctors != null) {
                        // We also **MUST** explicitly call the WASM constructors. This is
                        // a library function that is part of the zig libc code.  We have
                        // to call this because the wasm file is built using build-lib, so
                        // there is no main that does this.  This call does things like
                        // setup the filesystem mapping.    Yes, it took me **days**
                        // to figure this out, including reading a lot of assembly code. :shrug:
                        result.instance.exports.__wasm_call_ctors();
                    }
                    wasm = new WasmInstance(result.instance.exports);
                    if (!(options.init != null)) return [3 /*break*/, 4];
                    return [4 /*yield*/, options.init(wasm)];
                case 3:
                    _d.sent();
                    _d.label = 4;
                case 4:
                    if (!options.noCache) {
                        cache[name] = wasm;
                    }
                    if (options.time) {
                        console.log("imported " + name + " in " + (new Date().valueOf() - t) + "ms");
                    }
                    return [2 /*return*/, wasm];
            }
        });
    });
}, { createKey: function (args) { return args[0]; } });
exports.default = wasmImport;
var encoder = new TextEncoder();
var WasmInstance = /** @class */ (function () {
    function WasmInstance(exports) {
        this.result = undefined;
        this.exports = exports;
    }
    WasmInstance.prototype.stringToCharStar = function (str) {
        // Caller MUST free the returned char* from stringToU8 using wasm.free!
        var strAsArray = encoder.encode(str);
        var len = strAsArray.length + 1;
        var ptr = this.exports.malloc(len);
        var array = new Int8Array(this.exports.memory.buffer, ptr, len);
        array.set(strAsArray);
        array[len - 1] = 0;
        return ptr;
    };
    WasmInstance.prototype.callWithString = function (name, str) {
        var _a;
        var args = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            args[_i - 2] = arguments[_i];
        }
        this.result = undefined;
        var ptr = this.stringToCharStar(str);
        var r;
        try {
            var f = this.exports[name];
            if (f == null) {
                throw Error("no function " + name + " defined in wasm module");
            }
            // @ts-ignore
            r = f.apply(void 0, __spreadArray([ptr], __read(args), false));
        }
        finally {
            // @ts-ignore
            this.exports.free(ptr);
        }
        return (_a = this.result) !== null && _a !== void 0 ? _a : r;
    };
    return WasmInstance;
}());
exports.WasmInstance = WasmInstance;
//# sourceMappingURL=wasm.js.map