(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JavaScriptKit = {}));
})(this, (function (exports) { 'use strict';

    /// Memory lifetime of closures in Swift are managed by Swift side
    class SwiftClosureDeallocator {
        constructor(exports) {
            if (typeof FinalizationRegistry === "undefined") {
                throw new Error("The Swift part of JavaScriptKit was configured to require " +
                    "the availability of JavaScript WeakRefs. Please build " +
                    "with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to " +
                    "disable features that use WeakRefs.");
            }
            this.functionRegistry = new FinalizationRegistry((id) => {
                exports.swjs_free_host_function(id);
            });
        }
        track(func, func_ref) {
            this.functionRegistry.register(func, func_ref);
        }
    }

    function assertNever(x, message) {
        throw new Error(message);
    }

    const decode = (kind, payload1, payload2, memory) => {
        switch (kind) {
            case 0 /* Boolean */:
                switch (payload1) {
                    case 0:
                        return false;
                    case 1:
                        return true;
                }
            case 2 /* Number */:
                return payload2;
            case 1 /* String */:
            case 3 /* Object */:
            case 6 /* Function */:
            case 7 /* Symbol */:
            case 8 /* BigInt */:
                return memory.getObject(payload1);
            case 4 /* Null */:
                return null;
            case 5 /* Undefined */:
                return undefined;
            default:
                assertNever(kind, `JSValue Type kind "${kind}" is not supported`);
        }
    };
    // Note:
    // `decodeValues` assumes that the size of RawJSValue is 16.
    const decodeArray = (ptr, length, memory) => {
        // fast path for empty array
        if (length === 0) {
            return [];
        }
        let result = [];
        // It's safe to hold DataView here because WebAssembly.Memory.buffer won't
        // change within this function.
        const view = memory.dataView();
        for (let index = 0; index < length; index++) {
            const base = ptr + 16 * index;
            const kind = view.getUint32(base, true);
            const payload1 = view.getUint32(base + 4, true);
            const payload2 = view.getFloat64(base + 8, true);
            result.push(decode(kind, payload1, payload2, memory));
        }
        return result;
    };
    // A helper function to encode a RawJSValue into a pointers.
    // Please prefer to use `writeAndReturnKindBits` to avoid unnecessary
    // memory stores.
    // This function should be used only when kind flag is stored in memory.
    const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => {
        const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
        memory.writeUint32(kind_ptr, kind);
    };
    const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
        const exceptionBit = (is_exception ? 1 : 0) << 31;
        if (value === null) {
            return exceptionBit | 4 /* Null */;
        }
        const writeRef = (kind) => {
            memory.writeUint32(payload1_ptr, memory.retain(value));
            return exceptionBit | kind;
        };
        const type = typeof value;
        switch (type) {
            case "boolean": {
                memory.writeUint32(payload1_ptr, value ? 1 : 0);
                return exceptionBit | 0 /* Boolean */;
            }
            case "number": {
                memory.writeFloat64(payload2_ptr, value);
                return exceptionBit | 2 /* Number */;
            }
            case "string": {
                return writeRef(1 /* String */);
            }
            case "undefined": {
                return exceptionBit | 5 /* Undefined */;
            }
            case "object": {
                return writeRef(3 /* Object */);
            }
            case "function": {
                return writeRef(6 /* Function */);
            }
            case "symbol": {
                return writeRef(7 /* Symbol */);
            }
            case "bigint": {
                return writeRef(8 /* BigInt */);
            }
            default:
                assertNever(type, `Type "${type}" is not supported yet`);
        }
        throw new Error("Unreachable");
    };

    let globalVariable;
    if (typeof globalThis !== "undefined") {
        globalVariable = globalThis;
    }
    else if (typeof window !== "undefined") {
        globalVariable = window;
    }
    else if (typeof global !== "undefined") {
        globalVariable = global;
    }
    else if (typeof self !== "undefined") {
        globalVariable = self;
    }

    class SwiftRuntimeHeap {
        constructor() {
            this._heapValueById = new Map();
            this._heapValueById.set(0, globalVariable);
            this._heapEntryByValue = new Map();
            this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 });
            // Note: 0 is preserved for global
            this._heapNextKey = 1;
        }
        retain(value) {
            const entry = this._heapEntryByValue.get(value);
            if (entry) {
                entry.rc++;
                return entry.id;
            }
            const id = this._heapNextKey++;
            this._heapValueById.set(id, value);
            this._heapEntryByValue.set(value, { id: id, rc: 1 });
            return id;
        }
        release(ref) {
            const value = this._heapValueById.get(ref);
            const entry = this._heapEntryByValue.get(value);
            entry.rc--;
            if (entry.rc != 0)
                return;
            this._heapEntryByValue.delete(value);
            this._heapValueById.delete(ref);
        }
        referenceHeap(ref) {
            const value = this._heapValueById.get(ref);
            if (value === undefined) {
                throw new ReferenceError("Attempted to read invalid reference " + ref);
            }
            return value;
        }
    }

    class Memory {
        constructor(exports) {
            this.heap = new SwiftRuntimeHeap();
            this.retain = (value) => this.heap.retain(value);
            this.getObject = (ref) => this.heap.referenceHeap(ref);
            this.release = (ref) => this.heap.release(ref);
            this.bytes = () => new Uint8Array(this.rawMemory.buffer);
            this.dataView = () => new DataView(this.rawMemory.buffer);
            this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr);
            this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true);
            this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true);
            this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true);
            this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true);
            this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true);
            this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true);
            this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true);
            this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true);
            this.rawMemory = exports.memory;
        }
    }

    class SwiftRuntime {
        constructor(options) {
            this.version = 708;
            this.textDecoder = new TextDecoder("utf-8");
            this.textEncoder = new TextEncoder(); // Only support utf-8
            /** @deprecated Use `wasmImports` instead */
            this.importObjects = () => this.wasmImports;
            this._instance = null;
            this._memory = null;
            this._closureDeallocator = null;
            this.tid = null;
            this.options = options || {};
        }
        setInstance(instance) {
            this._instance = instance;
            if (typeof this.exports._start === "function") {
                throw new Error(`JavaScriptKit supports only WASI reactor ABI.
                Please make sure you are building with:
                -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor
                `);
            }
            if (this.exports.swjs_library_version() != this.version) {
                throw new Error(`The versions of JavaScriptKit are incompatible.
                WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`);
            }
        }
        main() {
            const instance = this.instance;
            try {
                if (typeof instance.exports.main === "function") {
                    instance.exports.main();
                }
                else if (typeof instance.exports.__main_argc_argv === "function") {
                    // Swift 6.0 and later use `__main_argc_argv` instead of `main`.
                    instance.exports.__main_argc_argv(0, 0);
                }
            }
            catch (error) {
                if (error instanceof UnsafeEventLoopYield) {
                    // Ignore the error
                    return;
                }
                // Rethrow other errors
                throw error;
            }
        }
        /**
         * Start a new thread with the given `tid` and `startArg`, which
         * is forwarded to the `wasi_thread_start` function.
         * This function is expected to be called from the spawned Web Worker thread.
         */
        startThread(tid, startArg) {
            this.tid = tid;
            const instance = this.instance;
            try {
                if (typeof instance.exports.wasi_thread_start === "function") {
                    instance.exports.wasi_thread_start(tid, startArg);
                }
                else {
                    throw new Error(`The WebAssembly module is not built for wasm32-unknown-wasip1-threads target.`);
                }
            }
            catch (error) {
                if (error instanceof UnsafeEventLoopYield) {
                    // Ignore the error
                    return;
                }
                // Rethrow other errors
                throw error;
            }
        }
        get instance() {
            if (!this._instance)
                throw new Error("WebAssembly instance is not set yet");
            return this._instance;
        }
        get exports() {
            return this.instance.exports;
        }
        get memory() {
            if (!this._memory) {
                this._memory = new Memory(this.instance.exports);
            }
            return this._memory;
        }
        get closureDeallocator() {
            if (this._closureDeallocator)
                return this._closureDeallocator;
            const features = this.exports.swjs_library_features();
            const librarySupportsWeakRef = (features & 1 /* WeakRefs */) != 0;
            if (librarySupportsWeakRef) {
                this._closureDeallocator = new SwiftClosureDeallocator(this.exports);
            }
            return this._closureDeallocator;
        }
        callHostFunction(host_func_id, line, file, args) {
            const argc = args.length;
            const argv = this.exports.swjs_prepare_host_function_call(argc);
            const memory = this.memory;
            for (let index = 0; index < args.length; index++) {
                const argument = args[index];
                const base = argv + 16 * index;
                write(argument, base, base + 4, base + 8, false, memory);
            }
            let output;
            // This ref is released by the swjs_call_host_function implementation
            const callback_func_ref = memory.retain((result) => {
                output = result;
            });
            const alreadyReleased = this.exports.swjs_call_host_function(host_func_id, argv, argc, callback_func_ref);
            if (alreadyReleased) {
                throw new Error(`The JSClosure has been already released by Swift side. The closure is created at ${file}:${line}`);
            }
            this.exports.swjs_cleanup_host_function_call(argv);
            return output;
        }
        get wasmImports() {
            return {
                swjs_set_prop: (ref, name, kind, payload1, payload2) => {
                    const memory = this.memory;
                    const obj = memory.getObject(ref);
                    const key = memory.getObject(name);
                    const value = decode(kind, payload1, payload2, memory);
                    obj[key] = value;
                },
                swjs_get_prop: (ref, name, payload1_ptr, payload2_ptr) => {
                    const memory = this.memory;
                    const obj = memory.getObject(ref);
                    const key = memory.getObject(name);
                    const result = obj[key];
                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory);
                },
                swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
                    const memory = this.memory;
                    const obj = memory.getObject(ref);
                    const value = decode(kind, payload1, payload2, memory);
                    obj[index] = value;
                },
                swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => {
                    const obj = this.memory.getObject(ref);
                    const result = obj[index];
                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                },
                swjs_encode_string: (ref, bytes_ptr_result) => {
                    const memory = this.memory;
                    const bytes = this.textEncoder.encode(memory.getObject(ref));
                    const bytes_ptr = memory.retain(bytes);
                    memory.writeUint32(bytes_ptr_result, bytes_ptr);
                    return bytes.length;
                },
                swjs_decode_string: (
                // NOTE: TextDecoder can't decode typed arrays backed by SharedArrayBuffer
                this.options.sharedMemory == true
                    ? ((bytes_ptr, length) => {
                        const memory = this.memory;
                        const bytes = memory
                            .bytes()
                            .slice(bytes_ptr, bytes_ptr + length);
                        const string = this.textDecoder.decode(bytes);
                        return memory.retain(string);
                    })
                    : ((bytes_ptr, length) => {
                        const memory = this.memory;
                        const bytes = memory
                            .bytes()
                            .subarray(bytes_ptr, bytes_ptr + length);
                        const string = this.textDecoder.decode(bytes);
                        return memory.retain(string);
                    })),
                swjs_load_string: (ref, buffer) => {
                    const memory = this.memory;
                    const bytes = memory.getObject(ref);
                    memory.writeBytes(buffer, bytes);
                },
                swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                    const memory = this.memory;
                    const func = memory.getObject(ref);
                    let result = undefined;
                    try {
                        const args = decodeArray(argv, argc, memory);
                        result = func(...args);
                    }
                    catch (error) {
                        return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory);
                    }
                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                },
                swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                    const memory = this.memory;
                    const func = memory.getObject(ref);
                    const args = decodeArray(argv, argc, memory);
                    const result = func(...args);
                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                },
                swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                    const memory = this.memory;
                    const obj = memory.getObject(obj_ref);
                    const func = memory.getObject(func_ref);
                    let result;
                    try {
                        const args = decodeArray(argv, argc, memory);
                        result = func.apply(obj, args);
                    }
                    catch (error) {
                        return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory);
                    }
                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                },
                swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                    const memory = this.memory;
                    const obj = memory.getObject(obj_ref);
                    const func = memory.getObject(func_ref);
                    let result = undefined;
                    const args = decodeArray(argv, argc, memory);
                    result = func.apply(obj, args);
                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                },
                swjs_call_new: (ref, argv, argc) => {
                    const memory = this.memory;
                    const constructor = memory.getObject(ref);
                    const args = decodeArray(argv, argc, memory);
                    const instance = new constructor(...args);
                    return this.memory.retain(instance);
                },
                swjs_call_throwing_new: (ref, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => {
                    let memory = this.memory;
                    const constructor = memory.getObject(ref);
                    let result;
                    try {
                        const args = decodeArray(argv, argc, memory);
                        result = new constructor(...args);
                    }
                    catch (error) {
                        write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory);
                        return -1;
                    }
                    memory = this.memory;
                    write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, memory);
                    return memory.retain(result);
                },
                swjs_instanceof: (obj_ref, constructor_ref) => {
                    const memory = this.memory;
                    const obj = memory.getObject(obj_ref);
                    const constructor = memory.getObject(constructor_ref);
                    return obj instanceof constructor;
                },
                swjs_create_function: (host_func_id, line, file) => {
                    var _a;
                    const fileString = this.memory.getObject(file);
                    const func = (...args) => this.callHostFunction(host_func_id, line, fileString, args);
                    const func_ref = this.memory.retain(func);
                    (_a = this.closureDeallocator) === null || _a === void 0 ? void 0 : _a.track(func, func_ref);
                    return func_ref;
                },
                swjs_create_typed_array: (constructor_ref, elementsPtr, length) => {
                    const ArrayType = this.memory.getObject(constructor_ref);
                    const array = new ArrayType(this.memory.rawMemory.buffer, elementsPtr, length);
                    // Call `.slice()` to copy the memory
                    return this.memory.retain(array.slice());
                },
                swjs_load_typed_array: (ref, buffer) => {
                    const memory = this.memory;
                    const typedArray = memory.getObject(ref);
                    const bytes = new Uint8Array(typedArray.buffer);
                    memory.writeBytes(buffer, bytes);
                },
                swjs_release: (ref) => {
                    this.memory.release(ref);
                },
                swjs_i64_to_bigint: (value, signed) => {
                    return this.memory.retain(signed ? value : BigInt.asUintN(64, value));
                },
                swjs_bigint_to_i64: (ref, signed) => {
                    const object = this.memory.getObject(ref);
                    if (typeof object !== "bigint") {
                        throw new Error(`Expected a BigInt, but got ${typeof object}`);
                    }
                    if (signed) {
                        return object;
                    }
                    else {
                        if (object < BigInt(0)) {
                            return BigInt(0);
                        }
                        return BigInt.asIntN(64, object);
                    }
                },
                swjs_i64_to_bigint_slow: (lower, upper, signed) => {
                    const value = BigInt.asUintN(32, BigInt(lower)) +
                        (BigInt.asUintN(32, BigInt(upper)) << BigInt(32));
                    return this.memory.retain(signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value));
                },
                swjs_unsafe_event_loop_yield: () => {
                    throw new UnsafeEventLoopYield();
                },
                swjs_send_job_to_main_thread: (unowned_job) => {
                    this.postMessageToMainThread({ type: "job", data: unowned_job });
                },
                swjs_listen_message_from_main_thread: () => {
                    const threadChannel = this.options.threadChannel;
                    if (!(threadChannel && "listenMessageFromMainThread" in threadChannel)) {
                        throw new Error("listenMessageFromMainThread is not set in options given to SwiftRuntime. Please set it to listen to wake events from the main thread.");
                    }
                    threadChannel.listenMessageFromMainThread((message) => {
                        switch (message.type) {
                            case "wake":
                                this.exports.swjs_wake_worker_thread();
                                break;
                            default:
                                const unknownMessage = message.type;
                                throw new Error(`Unknown message type: ${unknownMessage}`);
                        }
                    });
                },
                swjs_wake_up_worker_thread: (tid) => {
                    this.postMessageToWorkerThread(tid, { type: "wake" });
                },
                swjs_listen_message_from_worker_thread: (tid) => {
                    const threadChannel = this.options.threadChannel;
                    if (!(threadChannel && "listenMessageFromWorkerThread" in threadChannel)) {
                        throw new Error("listenMessageFromWorkerThread is not set in options given to SwiftRuntime. Please set it to listen to jobs from worker threads.");
                    }
                    threadChannel.listenMessageFromWorkerThread(tid, (message) => {
                        switch (message.type) {
                            case "job":
                                this.exports.swjs_enqueue_main_job_from_worker(message.data);
                                break;
                            default:
                                const unknownMessage = message.type;
                                throw new Error(`Unknown message type: ${unknownMessage}`);
                        }
                    });
                },
                swjs_terminate_worker_thread: (tid) => {
                    var _a;
                    const threadChannel = this.options.threadChannel;
                    if (threadChannel && "terminateWorkerThread" in threadChannel) {
                        (_a = threadChannel.terminateWorkerThread) === null || _a === void 0 ? void 0 : _a.call(threadChannel, tid);
                    } // Otherwise, just ignore the termination request
                },
                swjs_get_worker_thread_id: () => {
                    // Main thread's tid is always -1
                    return this.tid || -1;
                },
            };
        }
        postMessageToMainThread(message) {
            const threadChannel = this.options.threadChannel;
            if (!(threadChannel && "postMessageToMainThread" in threadChannel)) {
                throw new Error("postMessageToMainThread is not set in options given to SwiftRuntime. Please set it to send messages to the main thread.");
            }
            threadChannel.postMessageToMainThread(message);
        }
        postMessageToWorkerThread(tid, message) {
            const threadChannel = this.options.threadChannel;
            if (!(threadChannel && "postMessageToWorkerThread" in threadChannel)) {
                throw new Error("postMessageToWorkerThread is not set in options given to SwiftRuntime. Please set it to send messages to worker threads.");
            }
            threadChannel.postMessageToWorkerThread(tid, message);
        }
    }
    /// This error is thrown when yielding event loop control from `swift_task_asyncMainDrainQueue`
    /// to JavaScript. This is usually thrown when:
    /// - The entry point of the Swift program is `func main() async`
    /// - The Swift Concurrency's global executor is hooked by `JavaScriptEventLoop.installGlobalExecutor()`
    /// - Calling exported `main` or `__main_argc_argv` function from JavaScript
    ///
    /// This exception must be caught by the caller of the exported function and the caller should
    /// catch this exception and just ignore it.
    ///
    /// FAQ: Why this error is thrown?
    /// This error is thrown to unwind the call stack of the Swift program and return the control to
    /// the JavaScript side. Otherwise, the `swift_task_asyncMainDrainQueue` ends up with `abort()`
    /// because the event loop expects `exit()` call before the end of the event loop.
    class UnsafeEventLoopYield extends Error {
    }

    exports.SwiftRuntime = SwiftRuntime;

    Object.defineProperty(exports, '__esModule', { value: true });

}));
