1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2026-04-29 18:31:22 +00:00

Enable TeaVM WASM GC Backend

I'd tried this ages ago, and had some issues I utterly failed to find a
minimal reproducer for. Now it Just Works.
This commit is contained in:
Jonathan Coates
2025-12-15 14:17:03 +00:00
parent 16d934265a
commit 6702ab8f9d
7 changed files with 70 additions and 18 deletions

9
package-lock.json generated
View File

@@ -12,7 +12,8 @@
"@squid-dev/cc-web-term": "^2.0.0",
"preact": "^10.5.5",
"setimmediate": "^1.0.5",
"tslib": "^2.0.3"
"tslib": "^2.0.3",
"wasm-feature-detect": "^1.8.0"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.0",
@@ -3262,6 +3263,12 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/wasm-feature-detect": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
"integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
"license": "Apache-2.0"
},
"node_modules/web-namespaces": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",

View File

@@ -9,7 +9,8 @@
"@squid-dev/cc-web-term": "^2.0.0",
"preact": "^10.5.5",
"setimmediate": "^1.0.5",
"tslib": "^2.0.3"
"tslib": "^2.0.3",
"wasm-feature-detect": "^1.8.0"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.0",

View File

@@ -88,9 +88,9 @@ const ccTweaked = minify => {
}
},
/** @type {import("rollup").ResolveIdHook} */
async resolveId(source) {
if (source === "cct/classes") return path.resolve("build/teaVM/classes.js");
if (source === "cct/resources") return path.resolve("build/teaVM/resources.js");
if (source.startsWith("cct/")) return path.resolve("build/teaVM/" + source.substring(4));
return null;
},
@@ -124,7 +124,7 @@ export default args => ({
resolve({ browser: true }),
url({
include: ["**/*.dfpwm", "**/*.worker.js", "**/*.png"],
include: ["**/*.dfpwm", "**/*.worker.js", "**/*.png", "**/*.wasm"],
fileName: "[name]-[hash][extname]",
publicPath: "/",
limit: 0,

View File

@@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -70,14 +71,24 @@ public class Builder {
}
// Then finally start the compiler!
run(output, remapper, TeaVMTargetType.JAVASCRIPT, TeaVMOptimizationLevel.ADVANCED, minify);
run(output, remapper, TeaVMTargetType.WEBASSEMBLY_GC, TeaVMOptimizationLevel.SIMPLE, minify);
try (var runtime = Builder.class.getClassLoader().getResourceAsStream("org/teavm/backend/wasm/wasm-gc-module-runtime.js")) {
if (runtime == null) throw new IllegalStateException("Cannot find WASM runtime");
Files.copy(runtime, output.resolve("wasm-gc-runtime.js"), StandardCopyOption.REPLACE_EXISTING);
}
}
private static void run(Path output, ClassLoader classes, TeaVMTargetType target, TeaVMOptimizationLevel optimise, boolean minify) throws Exception {
var tool = new TeaVMTool();
tool.setTargetType(TeaVMTargetType.JAVASCRIPT);
tool.setTargetType(target);
tool.setJsModuleType(JSModuleType.ES2015);
tool.setTargetDirectory(output.toFile());
tool.setClassLoader(remapper);
tool.setClassLoader(classes);
tool.setMainClass("cc.tweaked.web.Main");
tool.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED);
tool.setOptimizationLevel(optimise);
tool.setObfuscated(minify);
tool.generate();

View File

@@ -4,11 +4,31 @@
import "setimmediate";
import type { ComputerDisplay, ComputerHandle } from "cct/classes";
export type { ComputerDisplay, ComputerHandle, PeripheralKind, Side } from "cct/classes";
import type { ComputerDisplay, ComputerHandle } from "cct/classes.js";
export type { ComputerDisplay, ComputerHandle, PeripheralKind, Side } from "cct/classes.js";
import { load as teaVMLoad } from "cct/wasm-gc-runtime.js";
import { exceptions, gc } from "wasm-feature-detect";
import wasmClasses from "cct/classes.wasm";
const loadClasses = async (): Promise<{ main: (args: string[]) => void }> => {
if (
typeof WebAssembly === "object" && typeof WebAssembly.compileStreaming === "function" &&
await exceptions() && await gc()
) {
try {
console.log("Loading WASM runtime");
return (await teaVMLoad(wasmClasses)).exports;
} catch (e) {
console.error("Failed to load WebAssembly runtime", e);
}
}
console.log("Using JS runtime");
return await import("cct/classes.js");
}
const load = async (): Promise<(computer: ComputerDisplay) => ComputerHandle> => {
const [classes, { version, resources }] = await Promise.all([import("cct/classes"), import("cct/resources")]);
const [classes, { version, resources }] = await Promise.all([loadClasses(), import("cct/resources.js")]);
let addComputer: ((computer: ComputerDisplay) => ComputerHandle) | null = null;
const encoder = new TextEncoder();
@@ -18,7 +38,7 @@ const load = async (): Promise<(computer: ComputerDisplay) => ComputerHandle> =>
listResources: () => Object.keys(resources),
getResource: path => new Int8Array(encoder.encode(resources[path]))
};
classes.main();
classes.main([]);
if (!addComputer) throw new Error("Callbacks.setup was never called");
return addComputer;

View File

@@ -28,16 +28,21 @@ declare module "*.license" {
}
declare module "*.dfpwm" {
const contents: string;
export default contents;
const url: string;
export default url;
}
declare module "cct/resources" {
declare module "*.wasm" {
const url: string;
export default url;
}
declare module "cct/resources.js" {
export const version: string;
export const resources: Record<string, string>;
}
declare module "cct/classes" {
declare module "cct/classes.js" {
export const main: () => void;
export type Side = "up" | "down" | "left" | "right" | "front" | "back";
@@ -169,10 +174,18 @@ declare module "cct/classes" {
}
}
declare module "cct/wasm-gc-runtime.js" {
export const load: (url: string, options?: any) => Promise<{
exports: { main: (args: string[]) => void },
instance: WebAssembly.Instance,
module: WebAssembly.Module,
}>
}
declare namespace JSX {
export type Element = import("preact").JSX.Element;
export type IntrinsicElements = import("preact").JSX.IntrinsicElements;
export type ElementClass = import("preact").JSX.ElementClass;
}
declare var $javaCallbacks: import("cct/classes").Callbacks; // eslint-disable-line no-var
declare var $javaCallbacks: import("cct/classes.js").Callbacks; // eslint-disable-line no-var

View File

@@ -54,7 +54,7 @@ public class Callbacks {
* @param resource The path to the resource to load.
* @return The loaded resource.
*/
@JSByRef
@JSByRef(optional = true)
@JSBody(params = "name", script = "return $javaCallbacks.getResource(name);")
public static native byte[] getResource(String resource);