0

I currently have been trying to use WebContainers + emscripten for my compilation, and i want to know if it is possible, and if not other ways to do it. hello.js

import { WebContainer } from 'https://cdn.jsdelivr.net/npm/@webcontainer/api/dist/index.js';

let wc;

document.addEventListener('DOMContentLoaded', async () => {
    const out = document.getElementById('out');
    const codeEl = document.getElementById('code-editor');

    wc = await WebContainer.boot({ coep: 'require-corp' });

    const runner = new Worker('./run-worker.js', { type: 'module' });

    runner.onmessage = (m) => {
        const { stdout, stderr, exitCode } = m.data || {};
        out.textContent += stdout + (stderr ? '\n[stderr]\n' + stderr : '') + '\n[exit ' + exitCode + ']';
    };

    document.getElementById('run').onclick = async () => {
        try {
            out.textContent = 'Compiling...\n';
            const code = codeEl.value || '#include <iostream>\nint main(){std::cout<<"Hi\\n";}\n';

            await wc.mount({
                'package.json': {
                    file: {
                        contents: JSON.stringify({
                            name: 'cpp-wasm',
                            scripts: { build: 'emcc main.cpp -sSTANDALONE_WASM=1 -O2 -o a.wasm' },
                            devDependencies: { emscripten: 'latest' }
                        })
                    }
                },
                'main.cpp': {
                    file: { contents: code }
                }
            });

            
            out.textContent += 'Installing emscripten...\n';
            const install = await wc.spawn('npm', ['i']);
            install.output.pipeTo(new WritableStream({
                write(chunk) {
                    console.log('npm install:', chunk);
                }
            }));
            const installCode = await install.exit;
            console.log('npm i exit code:', installCode);

            out.textContent += 'Building WASM...\n';
            const build = await wc.spawn('npm', ['run', 'build']);
            build.output.pipeTo(new WritableStream({
                write(chunk) {
                    console.log('npm build:', chunk);
                }
            }));
            const buildCode = await build.exit;
            console.log('npm run build exit code:', buildCode);

            const rootFiles = await wc.fs.readdir('/');
            console.log('root files:', rootFiles);

            const fsData = await wc.fs.readFile('/a.wasm');
            const buf = fsData.buffer.slice(fsData.byteOffset, fsData.byteOffset + fsData.byteLength);

            out.textContent += 'Running...\n';
            runner.postMessage({ type: 'runWasi', wasm: buf }, [buf]);
        } catch (err) {
            out.textContent += 'Compile error: ' + String(err) + '\n';
            console.error(err);
        }
    };
});

run worker.js

self.onmessage = async (e) => {
    if (e.data.type !== 'runWasi') return;
    const wasm = e.data.wasm;
    const dec = new TextDecoder();
    let out = '';
    let inst;

    function fd_write(fd, iov, iovcnt, pnum) {
        const m = new DataView(inst.exports.memory.buffer);
        let n = 0;
        for (let i = 0; i < iovcnt; i++) {
            const p = m.getUint32(iov + i * 8, true);
            const l = m.getUint32(iov + i * 8 + 4, true);
            const bytes = new Uint8Array(inst.exports.memory.buffer, p, l);
            const t = dec.decode(bytes);
            if (fd === 1 || fd === 2) out += t;
            n += l;
        }
        m.setUint32(pnum, n, true);
        return 0;
    }

    const imports = {
        wasi_snapshot_preview1: {
            fd_write,
            fd_read: () => 0,
            fd_close: () => 0,
            fd_seek: () => 0,
            fd_fdstat_get: () => 0,
            environ_sizes_get: (pc, pb) => {
                const m = new DataView(inst.exports.memory.buffer);
                m.setUint32(pc, 0, true);
                m.setUint32(pb, 0, true);
                return 0;
            },
            environ_get: () => 0,
            args_sizes_get: (pc, pb) => {
                const m = new DataView(inst.exports.memory.buffer);
                m.setUint32(pc, 0, true);
                m.setUint32(pb, 0, true);
                return 0;
            },
            args_get: () => 0,
            clock_time_get: () => 0,
            random_get: (p, l) => {
                crypto.getRandomValues(new Uint8Array(inst.exports.memory.buffer, p, l));
                return 0;
            },
            proc_exit: (c) => { throw { _wasi_exit: true, code: c }; }
        }
    };

    try {
        const { instance } = await WebAssembly.instantiate(wasm, imports);
        inst = instance;
        let code = 0;
        try { (inst.exports._main || inst.exports.main)?.(); }
        catch (ex) { if (!(ex && ex._wasi_exit)) throw ex; code = ex.code >>> 0; }
        postMessage({ stdout: out, stderr: '', exitCode: code });
    } catch (err) {
        postMessage({ stdout: out, stderr: String(err), exitCode: 1 });
    }
};

html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Scrollable Boxes</title>
    <link rel="stylesheet" href="hello.css">
    <script type="module" src="./hello.js" defer></script>
</head>

<body>
        <div class="box code-box">
            <textarea id="code-editor" class="code-editor" spellcheck="false">
#include <iostream>

int main() {
    std::cout << "Hello, World!" << '\n'; 
    return 0; 
}
            </textarea>
            <br>
            <button id="run">Build & Run</button>
            <pre id="out"></pre>
        </div>
    </div>
</body>

</html>

serving with Cross-Origin-Opener-Policy, same-origin Cross-Origin-Embedder-Policy, require-corp most recent output Compiling... Installing emscripten... Building WASM... Compile error: Error: ENOENT: no such file or directory, open '/home/uetuarq4gdsoqhfukc73ydw1enf14q-fg04/a.wasm'

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.