Files
RedBear-OS/local/recipes/qt/qtbase/source/util/wasm/batchedtestrunner/qwasmjsruntime.js
T
vasilito f31522130f fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed
Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
2026-05-05 20:20:37 +01:00

240 lines
7.5 KiB
JavaScript

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// Exposes platform capabilities as static properties
export class AbortedError extends Error {
constructor(stdout) {
super(`The program has been aborted`)
this.stdout = stdout;
}
}
export class Platform {
static #webAssemblySupported = typeof WebAssembly !== 'undefined';
static #canCompileStreaming = WebAssembly.compileStreaming !== 'undefined';
static #webGLSupported = (() => {
// We expect that WebGL is supported if WebAssembly is; however
// the GPU may be blacklisted.
try {
const canvas = document.createElement('canvas');
return !!(
window.WebGLRenderingContext &&
(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
);
} catch (e) {
return false;
}
})();
static #canLoadQt = Platform.#webAssemblySupported && Platform.#webGLSupported;
static get webAssemblySupported() {
return this.#webAssemblySupported;
}
static get canCompileStreaming() {
return this.#canCompileStreaming;
}
static get webGLSupported() {
return this.#webGLSupported;
}
static get canLoadQt() {
return this.#canLoadQt;
}
}
// Locates a resource, based on its relative path
export class ResourceLocator {
#rootPath;
constructor(rootPath) {
this.#rootPath = rootPath;
if (rootPath.length > 0 && !rootPath.endsWith('/')) rootPath += '/';
}
locate(relativePath) {
return this.#rootPath + relativePath;
}
}
// Allows fetching of resources, such as text resources or wasm modules.
export class ResourceFetcher {
#locator;
constructor(locator) {
this.#locator = locator;
}
async fetchText(filePath) {
return (await this.#fetchRawResource(filePath)).text();
}
async fetchCompileWasm(filePath, onFetched) {
const fetchResponse = await this.#fetchRawResource(filePath);
onFetched?.();
if (Platform.canCompileStreaming) {
try {
return await WebAssembly.compileStreaming(fetchResponse);
} catch {
// NOOP - fallback to sequential fetching below
}
}
return WebAssembly.compile(await fetchResponse.arrayBuffer());
}
async #fetchRawResource(filePath) {
const response = await fetch(this.#locator.locate(filePath));
if (!response.ok)
throw new Error(
`${response.status} ${response.statusText} ${response.url}`
);
return response;
}
}
// Represents a WASM module, wrapping the instantiation and execution thereof.
export class CompiledModule {
#createQtAppInstanceFn;
#js;
#wasm;
#resourceLocator;
#qtContainerElements;
constructor(createQtAppInstanceFn, js, wasm, resourceLocator, qtContainerElements) {
this.#createQtAppInstanceFn = createQtAppInstanceFn;
this.#js = js;
this.#wasm = wasm;
this.#resourceLocator = resourceLocator;
this.#qtContainerElements = qtContainerElements;
}
static make(js, wasm, entryFunctionName, resourceLocator, qtContainerElements)
{
const exports = {};
const module = {};
eval(js);
if (!module.exports) {
throw new Error(
'${entryFunctionName} has not been exported by the main script'
);
}
return new CompiledModule(
module.exports, js, wasm, resourceLocator, qtContainerElements
);
}
async exec(parameters) {
return await new Promise(async (resolve, reject) => {
let instance = undefined;
let result = undefined;
let testFinished = false;
const testFinishedEvent = new CustomEvent('testFinished');
instance = await this.#createQtAppInstanceFn((() => {
const params = this.#makeDefaultExecParams({
onInstantiationError: (error) => { reject(error); },
});
params.arguments = parameters?.args;
let data = '';
params.print = (out) => {
parameters?.onStdout?.(out);
data += `${out}\n`;
};
params.printErr = () => { };
params.onAbort = () => reject(new AbortedError(data));
params.quit = (code, exception) => {
if (exception && exception.name !== 'ExitStatus')
reject(exception);
};
params.notifyTestFinished = (code) => {
result = { stdout: data, exitCode: code };
testFinished = true;
window.dispatchEvent(testFinishedEvent);
};
return params;
})());
if (!testFinished) {
await new Promise((resolve) => {
window.addEventListener('testFinished', () => {
resolve();
});
});
}
resolve({
stdout: result.stdout,
exitCode: result.exitCode,
instance,
});
});
}
#makeDefaultExecParams(params) {
const instanceParams = {};
instanceParams.instantiateWasm = async (imports, onDone) => {
try {
onDone(await WebAssembly.instantiate(this.#wasm, imports), this.#wasm);
} catch (e) {
params?.onInstantiationError?.(e);
}
};
instanceParams.locateFile = (filename) =>
this.#resourceLocator.locate(filename);
instanceParams.monitorRunDependencies = (name) => { };
instanceParams.print = (text) => true && console.log(text);
instanceParams.printErr = (text) => true && console.warn(text);
instanceParams.mainScriptUrlOrBlob = new Blob([this.#js], {
type: 'text/javascript',
});
// Add Qt container elements if provided
if (this.#qtContainerElements) {
instanceParams.qtContainerElements = this.#qtContainerElements;
}
return instanceParams;
}
}
// Streamlines loading of WASM modules.
export class ModuleLoader {
#fetcher;
#resourceLocator;
constructor(
fetcher,
resourceLocator
) {
this.#fetcher = fetcher;
this.#resourceLocator = resourceLocator;
}
// Loads an emscripten module named |moduleName| from the main resource path. Provides
// progress of 'downloading' and 'compiling' to the caller using the |onProgress| callback.
async loadEmscriptenModule(
moduleName, onProgress, qtContainerElements
) {
if (!Platform.webAssemblySupported)
throw new Error('Web assembly not supported');
if (!Platform.webGLSupported)
throw new Error('WebGL is not supported');
onProgress('downloading');
const jsLoadPromise = this.#fetcher.fetchText(`${moduleName}.js`);
const wasmLoadPromise = this.#fetcher.fetchCompileWasm(
`${moduleName}.wasm`,
() => {
onProgress('compiling');
}
);
const [js, wasm] = await Promise.all([jsLoadPromise, wasmLoadPromise]);
return CompiledModule.make(js, wasm, `${moduleName}_entry`, this.#resourceLocator, qtContainerElements);
}
}