wasm-idle 1.0.11 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -64
- package/dist/playground/aheui.d.ts +18 -0
- package/dist/playground/aheui.js +21 -0
- package/dist/playground/apecode.d.ts +18 -0
- package/dist/playground/apecode.js +21 -0
- package/dist/playground/applicationAssets.js +2 -0
- package/dist/playground/assets.d.ts +18 -0
- package/dist/playground/assets.js +28 -0
- package/dist/playground/commonlisp.d.ts +8 -0
- package/dist/playground/commonlisp.js +33 -0
- package/dist/playground/commonlispAssets.d.ts +30 -0
- package/dist/playground/commonlispAssets.js +184 -0
- package/dist/playground/golfscript.d.ts +6 -0
- package/dist/playground/golfscript.js +8 -0
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +95 -0
- package/dist/playground/javaRuntimeStdin.d.ts +9 -0
- package/dist/playground/javaRuntimeStdin.js +145 -0
- package/dist/playground/lean4.d.ts +13 -0
- package/dist/playground/lean4.js +50 -0
- package/dist/playground/lean4Assets.d.ts +25 -0
- package/dist/playground/lean4Assets.js +123 -0
- package/dist/playground/loadingProgress.js +4 -1
- package/dist/playground/python.d.ts +6 -2
- package/dist/playground/python.js +81 -15
- package/dist/playground/ruby.d.ts +6 -0
- package/dist/playground/ruby.js +108 -56
- package/dist/playground/wasiInterpreters.d.ts +15 -0
- package/dist/playground/wasiInterpreters.js +67 -0
- package/dist/playground/wasm.d.ts +3 -0
- package/dist/playground/wasm.js +44 -3
- package/dist/playground/wasmAheuiVersion.d.ts +7 -0
- package/dist/playground/wasmAheuiVersion.js +10 -0
- package/dist/playground/wasmApecodeVersion.d.ts +7 -0
- package/dist/playground/wasmApecodeVersion.js +9 -0
- package/dist/playground/wasmCommonLispVersion.d.ts +32 -0
- package/dist/playground/wasmCommonLispVersion.js +33 -0
- package/dist/playground/wasmGolfscriptVersion.d.ts +10 -0
- package/dist/playground/wasmGolfscriptVersion.js +10 -0
- package/dist/playground/wasmLean4Version.d.ts +110 -0
- package/dist/playground/wasmLean4Version.js +111 -0
- package/dist/playground/worker/golfscriptMount.d.ts +45 -0
- package/dist/playground/worker/golfscriptMount.js +79 -0
- package/dist/playground/worker/java.js +17 -29
- package/dist/playground/worker/python.js +311 -155
- package/dist/playground/worker/python.runtime.cases.d.ts +1 -0
- package/dist/playground/worker/python.runtime.cases.js +374 -0
- package/dist/playground/worker/pythonDebugPreview.d.ts +2 -0
- package/dist/playground/worker/pythonDebugPreview.js +55 -0
- package/dist/playground/worker/pythonExecution.d.ts +8 -0
- package/dist/playground/worker/pythonExecution.js +167 -0
- package/dist/playground/worker/pythonStdio.d.ts +32 -0
- package/dist/playground/worker/pythonStdio.js +253 -0
- package/dist/playground/worker/ruby.js +151 -15
- package/dist/playground/worker/wasm.js +106 -7
- package/package.json +32 -10
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { AssetIntegrityError, defineRuntimeRegistryManifest, UnsupportedBrowserFeatureError, preflightRuntimeAssets, ResourceLimitError, RuntimeConfigurationError, verifyRuntimeAssetIntegrity } from '@wasm-idle/core';
|
|
2
|
+
import { WASM_COMMONLISP_PROFILE as profile } from './wasmCommonLispVersion';
|
|
3
|
+
export const COMMONLISP_INITIAL_MEMORY_BYTES = 64 * 1024 * 1024;
|
|
4
|
+
/** ecl.wasm is delivered as the producer's pinned gzip bytes; `.bin` keeps hosts from adding
|
|
5
|
+
* Content-Encoding, so the browser never transparently decodes it before verification. */
|
|
6
|
+
export const COMMONLISP_WASM_STORAGE_PATH = 'ecl.wasm.gz.bin';
|
|
7
|
+
const identityAsset = (name, mediaType) => ({
|
|
8
|
+
key: name,
|
|
9
|
+
path: name,
|
|
10
|
+
compressedSha256: profile.assets[name].sha256,
|
|
11
|
+
uncompressedSha256: profile.assets[name].sha256,
|
|
12
|
+
compressedBytes: profile.assets[name].bytes,
|
|
13
|
+
uncompressedBytes: profile.assets[name].bytes,
|
|
14
|
+
encoding: 'identity',
|
|
15
|
+
mediaType
|
|
16
|
+
});
|
|
17
|
+
const deliveryAssets = [
|
|
18
|
+
identityAsset('producer-receipt.json', 'application/json'),
|
|
19
|
+
identityAsset('ecl.mjs', 'text/javascript'),
|
|
20
|
+
{
|
|
21
|
+
key: 'ecl.wasm',
|
|
22
|
+
path: COMMONLISP_WASM_STORAGE_PATH,
|
|
23
|
+
compressedSha256: profile.assets['ecl.wasm.gz'].sha256,
|
|
24
|
+
uncompressedSha256: profile.runtime['ecl.wasm'].sha256,
|
|
25
|
+
compressedBytes: profile.assets['ecl.wasm.gz'].bytes,
|
|
26
|
+
uncompressedBytes: profile.runtime['ecl.wasm'].bytes,
|
|
27
|
+
encoding: 'gzip',
|
|
28
|
+
mediaType: 'application/wasm'
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
export const COMMONLISP_RUNTIME_MANIFEST = defineRuntimeRegistryManifest({
|
|
32
|
+
schemaVersion: 2,
|
|
33
|
+
manifestId: 'wasm-idle/commonlisp',
|
|
34
|
+
revision: profile.assets['producer-receipt.json'].sha256,
|
|
35
|
+
runtimes: [
|
|
36
|
+
{
|
|
37
|
+
runtimeId: 'COMMONLISP',
|
|
38
|
+
identity: {
|
|
39
|
+
languageId: 'COMMONLISP',
|
|
40
|
+
implementationId: 'ecl-emscripten',
|
|
41
|
+
implementationVersion: profile.version,
|
|
42
|
+
profile: {
|
|
43
|
+
profileId: profile.profileId,
|
|
44
|
+
manifestSchemaVersion: 1,
|
|
45
|
+
manifestSha256: profile.assets['producer-receipt.json'].sha256,
|
|
46
|
+
protocolVersion: 1,
|
|
47
|
+
trustProfileId: profile.profileId,
|
|
48
|
+
trustProfileSchemaVersion: 1
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
capabilities: {
|
|
52
|
+
stdin: 'streaming',
|
|
53
|
+
workspace: true,
|
|
54
|
+
abort: true,
|
|
55
|
+
artifacts: false,
|
|
56
|
+
streamingOutput: true
|
|
57
|
+
},
|
|
58
|
+
workerLifetime: { mode: 'per-run' },
|
|
59
|
+
requiredBrowserFeatures: ['wasm', 'wasm-exceptions', 'decompression-stream'],
|
|
60
|
+
assetRoot: '.',
|
|
61
|
+
assets: deliveryAssets,
|
|
62
|
+
contracts: {
|
|
63
|
+
routeId: 'COMMONLISP',
|
|
64
|
+
runtimeAssetKey: 'commonlisp',
|
|
65
|
+
documentationId: 'commonlisp',
|
|
66
|
+
syncTarget: 'wasm-commonlisp',
|
|
67
|
+
browserTestId: 'COMMONLISP'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
});
|
|
72
|
+
export function resolveCommonLispRuntimeAssetConfig(assets, currentUrl) {
|
|
73
|
+
const root = typeof assets === 'string' ? assets : assets.rootUrl || '';
|
|
74
|
+
const override = typeof assets === 'string' ? undefined : assets.commonlisp?.baseUrl;
|
|
75
|
+
const baseUrl = new URL(override ?? `${root.replace(/\/+$/, '')}/wasm-commonlisp/`, currentUrl);
|
|
76
|
+
if (!['http:', 'https:'].includes(baseUrl.protocol) ||
|
|
77
|
+
baseUrl.username ||
|
|
78
|
+
baseUrl.password ||
|
|
79
|
+
baseUrl.search ||
|
|
80
|
+
baseUrl.hash) {
|
|
81
|
+
throw new RuntimeConfigurationError('Common Lisp requires a credential-free HTTP(S) asset directory.', { runtimeId: 'COMMONLISP' });
|
|
82
|
+
}
|
|
83
|
+
if (!baseUrl.pathname.endsWith('/'))
|
|
84
|
+
baseUrl.pathname += '/';
|
|
85
|
+
const workerUrl = new URL('runner-worker.js', baseUrl);
|
|
86
|
+
workerUrl.searchParams.set('v', profile.workerReceipt.sha256);
|
|
87
|
+
return {
|
|
88
|
+
baseUrl: baseUrl.href,
|
|
89
|
+
workerUrl: workerUrl.href,
|
|
90
|
+
manifestFingerprint: profile.assets['producer-receipt.json'].sha256,
|
|
91
|
+
workerReceipt: profile.workerReceipt
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export async function preflightCommonLispRuntimeAssets(baseUrl, options) {
|
|
95
|
+
if (options.limits.maxWasmMemoryBytes < COMMONLISP_INITIAL_MEMORY_BYTES) {
|
|
96
|
+
throw new ResourceLimitError('Common Lisp needs at least 64 MiB of Wasm memory.', {
|
|
97
|
+
runtimeId: 'COMMONLISP',
|
|
98
|
+
phase: 'asset',
|
|
99
|
+
resource: 'wasm-memory',
|
|
100
|
+
actual: COMMONLISP_INITIAL_MEMORY_BYTES,
|
|
101
|
+
limit: options.limits.maxWasmMemoryBytes
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const loaded = new Map();
|
|
105
|
+
const total = deliveryAssets.reduce((sum, asset) => sum + asset.compressedBytes, 0);
|
|
106
|
+
const result = await preflightRuntimeAssets({
|
|
107
|
+
manifest: COMMONLISP_RUNTIME_MANIFEST,
|
|
108
|
+
runtimeId: 'COMMONLISP',
|
|
109
|
+
rootUrl: baseUrl,
|
|
110
|
+
assetUrls: Object.fromEntries(deliveryAssets.map((asset) => [
|
|
111
|
+
asset.key,
|
|
112
|
+
new URL(`${asset.path}?v=${asset.compressedSha256}`, baseUrl)
|
|
113
|
+
])),
|
|
114
|
+
limits: options.limits,
|
|
115
|
+
persistentCache: options.persistentCache,
|
|
116
|
+
signal: options.signal,
|
|
117
|
+
fetch: options.fetch,
|
|
118
|
+
redirect: 'error',
|
|
119
|
+
requireExactResponseUrl: true,
|
|
120
|
+
maxConcurrentDownloads: 2,
|
|
121
|
+
maxTotalDeliveryBytes: total,
|
|
122
|
+
reportProgress(progress) {
|
|
123
|
+
loaded.set(progress.assetKey, progress.loadedBytes);
|
|
124
|
+
options.reportProgress?.(0.04 + (0.13 * [...loaded.values()].reduce((a, b) => a + b, 0)) / total, `Verifying ECL ${progress.assetKey}`);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const wasm = await decompressCommonLispWasm(result.assets['ecl.wasm'].bytes, options.signal);
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
fingerprint: profile.assets['producer-receipt.json'].sha256,
|
|
130
|
+
'producer-receipt.json': result.assets['producer-receipt.json'].bytes,
|
|
131
|
+
'ecl.mjs': result.assets['ecl.mjs'].bytes,
|
|
132
|
+
'ecl.wasm': wasm
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/** Decode the verified gzip delivery into an exactly sized buffer and verify the logical receipt. */
|
|
136
|
+
export async function decompressCommonLispWasm(compressed, signal) {
|
|
137
|
+
const context = { runtimeId: 'COMMONLISP', profileId: profile.profileId };
|
|
138
|
+
if (typeof DecompressionStream !== 'function')
|
|
139
|
+
throw new UnsupportedBrowserFeatureError('DecompressionStream(gzip)', {
|
|
140
|
+
...context,
|
|
141
|
+
phase: 'asset'
|
|
142
|
+
});
|
|
143
|
+
const expected = profile.runtime['ecl.wasm'];
|
|
144
|
+
const body = new Response(Uint8Array.from(compressed)).body;
|
|
145
|
+
const reader = body.pipeThrough(new DecompressionStream('gzip')).getReader();
|
|
146
|
+
const wasm = new Uint8Array(expected.bytes);
|
|
147
|
+
let offset = 0;
|
|
148
|
+
try {
|
|
149
|
+
for (;;) {
|
|
150
|
+
signal?.throwIfAborted();
|
|
151
|
+
const { done, value } = await reader.read();
|
|
152
|
+
if (done)
|
|
153
|
+
break;
|
|
154
|
+
if (offset + value.byteLength > wasm.byteLength)
|
|
155
|
+
throw new AssetIntegrityError('ECL ecl.wasm gzip output exceeds its receipt', context);
|
|
156
|
+
wasm.set(value, offset);
|
|
157
|
+
offset += value.byteLength;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
void reader.cancel().catch(() => undefined);
|
|
162
|
+
if (error instanceof AssetIntegrityError || signal?.aborted)
|
|
163
|
+
throw error;
|
|
164
|
+
throw new AssetIntegrityError('ECL ecl.wasm gzip decompression failed', {
|
|
165
|
+
...context,
|
|
166
|
+
cause: error
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (offset !== wasm.byteLength)
|
|
170
|
+
throw new AssetIntegrityError('ECL ecl.wasm gzip output is truncated', context);
|
|
171
|
+
await verifyRuntimeAssetIntegrity({
|
|
172
|
+
asset: 'ecl.wasm',
|
|
173
|
+
bytes: wasm,
|
|
174
|
+
expected: {
|
|
175
|
+
...expected,
|
|
176
|
+
uncompressedSha256: expected.sha256,
|
|
177
|
+
uncompressedBytes: expected.bytes
|
|
178
|
+
},
|
|
179
|
+
stage: 'uncompressed',
|
|
180
|
+
mimeType: 'application/wasm',
|
|
181
|
+
...context
|
|
182
|
+
});
|
|
183
|
+
return wasm;
|
|
184
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BoundSandbox, PlaygroundBinding, Sandbox, SandboxRuntimeAssets } from './sandbox';
|
|
2
2
|
import { type PlaygroundBindingOptions } from '@wasm-idle/core';
|
|
3
|
-
export declare const supportedLanguages: ("C" | "CPP" | "OBJC" | "OBJECTIVECXX" | "D" | "TINYGO" | "RUBY" | "PERL" | "TCL" | "AWK" | "PASCAL" | "JANET" | "JULIA" | "NIM" | "BASH" | "BQN" | "CLOJURESCRIPT" | "FORTH" | "J" | "PROLOG" | "RESCRIPT" | "C3" | "
|
|
3
|
+
export declare const supportedLanguages: ("C" | "CPP" | "OBJC" | "OBJECTIVECXX" | "D" | "TINYGO" | "RUBY" | "PERL" | "TCL" | "AWK" | "PASCAL" | "JANET" | "JULIA" | "NIM" | "BASH" | "GOLFSCRIPT" | "APECODE" | "AHEUI" | "PYTHON3" | "BRAINFUCK" | "BEFUNGE93" | "WHITESPACE" | "MALBOLGE" | "UHMLANG" | "LOLCODE" | "WASM" | "BQN" | "CLOJURESCRIPT" | "FORTH" | "J" | "PROLOG" | "RESCRIPT" | "C3" | "JAVA" | "RUST" | "GO" | "CSHARP" | "FSHARP" | "VBNET" | "ELIXIR" | "ERLANG" | "GLEAM" | "GRAIN" | "HY" | "FORTRAN" | "LFORTRAN" | "LEAN4" | "COBOL" | "V" | "OCAML" | "JAVASCRIPT" | "TYPESCRIPT" | "ASSEMBLYSCRIPT" | "WAT" | "LUA" | "FENNEL" | "ZIG" | "LISP" | "COMMONLISP" | "HASKELL" | "R" | "OCTAVE" | "DUCKDB" | "SQLITE" | "POSTGRESQL" | "PHP")[];
|
|
4
4
|
export declare function createPlaygroundBinding(runtimeAssets: SandboxRuntimeAssets, options?: PlaygroundBindingOptions): PlaygroundBinding;
|
|
5
5
|
declare function playground(language: string): Promise<Sandbox>;
|
|
6
6
|
declare function playground(language: string, runtimeAssets: SandboxRuntimeAssets, options?: PlaygroundBindingOptions): Promise<BoundSandbox>;
|
package/dist/playground/index.js
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
import { createPlaygroundBinding as createCorePlaygroundBinding, isSupportedLanguageId, normalizeLanguageId, supportedLanguageIds } from '@wasm-idle/core';
|
|
2
2
|
const sandboxRoutes = [
|
|
3
|
+
{
|
|
4
|
+
languageId: 'GOLFSCRIPT',
|
|
5
|
+
load: async () => {
|
|
6
|
+
const { default: Golfscript } = await import('./golfscript');
|
|
7
|
+
return new Golfscript();
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
languageId: 'APECODE',
|
|
12
|
+
load: async () => {
|
|
13
|
+
const { default: Apecode } = await import('./apecode');
|
|
14
|
+
return new Apecode();
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
languageId: 'LOLCODE',
|
|
19
|
+
load: async () => {
|
|
20
|
+
const [{ default: Wasm }, { WASI_INTERPRETERS }] = await Promise.all([
|
|
21
|
+
import('./wasm'),
|
|
22
|
+
import('./wasiInterpreters')
|
|
23
|
+
]);
|
|
24
|
+
return new Wasm(WASI_INTERPRETERS.LOLCODE);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
languageId: 'UHMLANG',
|
|
29
|
+
load: async () => {
|
|
30
|
+
const [{ default: Wasm }, { WASI_INTERPRETERS }] = await Promise.all([
|
|
31
|
+
import('./wasm'),
|
|
32
|
+
import('./wasiInterpreters')
|
|
33
|
+
]);
|
|
34
|
+
return new Wasm(WASI_INTERPRETERS.UHMLANG);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
languageId: 'AHEUI',
|
|
39
|
+
load: async () => {
|
|
40
|
+
const { default: Aheui } = await import('./aheui');
|
|
41
|
+
return new Aheui();
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
languageId: 'MALBOLGE',
|
|
46
|
+
load: async () => {
|
|
47
|
+
const [{ default: Wasm }, { WASI_INTERPRETERS }] = await Promise.all([
|
|
48
|
+
import('./wasm'),
|
|
49
|
+
import('./wasiInterpreters')
|
|
50
|
+
]);
|
|
51
|
+
return new Wasm(WASI_INTERPRETERS.MALBOLGE);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
languageId: 'WHITESPACE',
|
|
56
|
+
load: async () => {
|
|
57
|
+
const [{ default: Wasm }, { WASI_INTERPRETERS }] = await Promise.all([
|
|
58
|
+
import('./wasm'),
|
|
59
|
+
import('./wasiInterpreters')
|
|
60
|
+
]);
|
|
61
|
+
return new Wasm(WASI_INTERPRETERS.WHITESPACE);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
languageId: 'BEFUNGE93',
|
|
66
|
+
load: async () => {
|
|
67
|
+
const [{ default: Wasm }, { WASI_INTERPRETERS }] = await Promise.all([
|
|
68
|
+
import('./wasm'),
|
|
69
|
+
import('./wasiInterpreters')
|
|
70
|
+
]);
|
|
71
|
+
return new Wasm(WASI_INTERPRETERS.BEFUNGE93);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
languageId: 'BRAINFUCK',
|
|
76
|
+
load: async () => {
|
|
77
|
+
const [{ default: Wasm }, { WASI_INTERPRETERS }] = await Promise.all([
|
|
78
|
+
import('./wasm'),
|
|
79
|
+
import('./wasiInterpreters')
|
|
80
|
+
]);
|
|
81
|
+
return new Wasm(WASI_INTERPRETERS.BRAINFUCK);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
3
84
|
{
|
|
4
85
|
languageId: 'C3',
|
|
5
86
|
load: async () => {
|
|
@@ -231,6 +312,13 @@ const sandboxRoutes = [
|
|
|
231
312
|
return new LFortran();
|
|
232
313
|
}
|
|
233
314
|
},
|
|
315
|
+
{
|
|
316
|
+
languageId: 'LEAN4',
|
|
317
|
+
load: async () => {
|
|
318
|
+
const { default: Lean4 } = await import('./lean4');
|
|
319
|
+
return new Lean4();
|
|
320
|
+
}
|
|
321
|
+
},
|
|
234
322
|
{
|
|
235
323
|
languageId: 'FORTRAN',
|
|
236
324
|
load: async () => {
|
|
@@ -329,6 +417,13 @@ const sandboxRoutes = [
|
|
|
329
417
|
return new Lisp();
|
|
330
418
|
}
|
|
331
419
|
},
|
|
420
|
+
{
|
|
421
|
+
languageId: 'COMMONLISP',
|
|
422
|
+
load: async () => {
|
|
423
|
+
const { default: CommonLisp } = await import('./commonlisp');
|
|
424
|
+
return new CommonLisp();
|
|
425
|
+
}
|
|
426
|
+
},
|
|
332
427
|
{
|
|
333
428
|
languageId: 'RUBY',
|
|
334
429
|
load: async () => {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type PreparedJavaStdinInjection } from './javaStdin';
|
|
2
|
+
/** Browser execution input is runtime data. Retain the snapshot API for standalone callers. */
|
|
3
|
+
export declare function prepareJavaRuntimeStdinInjection(code: string): PreparedJavaStdinInjection;
|
|
4
|
+
/** Single cursor shared by compatibility byte reads and bounded block reads. */
|
|
5
|
+
export declare function createJavaStdinBridge(initialInput: string, explicit: boolean, readInput: () => string | null, maxChunkBytes?: number): {
|
|
6
|
+
readByte(): number;
|
|
7
|
+
readChunk(): Int8Array | null;
|
|
8
|
+
dispose(): void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { prepareJavaStdinInjection } from './javaStdin';
|
|
2
|
+
/** Browser execution input is runtime data. Retain the snapshot API for standalone callers. */
|
|
3
|
+
export function prepareJavaRuntimeStdinInjection(code) {
|
|
4
|
+
const injection = prepareJavaStdinInjection(code, '', false);
|
|
5
|
+
if (!injection.usesStdin || !injection.helperSourcePath)
|
|
6
|
+
return injection;
|
|
7
|
+
const parts = injection.helperSourcePath.split('/');
|
|
8
|
+
const className = parts.pop().replace(/\.java$/, '');
|
|
9
|
+
const packageName = parts.join('.');
|
|
10
|
+
return {
|
|
11
|
+
...injection,
|
|
12
|
+
stdinCacheKey: 'host-chunks-v2-jso',
|
|
13
|
+
helperSource: `${packageName ? `package ${packageName};\n\n` : ''}import java.io.InputStream;
|
|
14
|
+
import org.teavm.jso.JSObject;
|
|
15
|
+
import org.teavm.jso.browser.Window;
|
|
16
|
+
import org.teavm.jso.core.JSFunction;
|
|
17
|
+
import org.teavm.jso.core.JSMapLike;
|
|
18
|
+
import org.teavm.jso.core.JSObjects;
|
|
19
|
+
import org.teavm.jso.typedarrays.Int8Array;
|
|
20
|
+
|
|
21
|
+
final class ${className} extends InputStream {
|
|
22
|
+
private static final ${className} INSTANCE = new ${className}();
|
|
23
|
+
private byte[] chunk = new byte[0];
|
|
24
|
+
private int position = 0;
|
|
25
|
+
private boolean ended = false;
|
|
26
|
+
|
|
27
|
+
private ${className}() {}
|
|
28
|
+
|
|
29
|
+
static InputStream open() {
|
|
30
|
+
return INSTANCE;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Use classlib overlay methods: the pinned source compiler cannot compile @JSBody.
|
|
34
|
+
// A host call obtains a block; copying its bytes keeps Java array ownership explicit.
|
|
35
|
+
private static byte[] readFromHost() {
|
|
36
|
+
Window current = Window.current();
|
|
37
|
+
if (current == null) return null;
|
|
38
|
+
JSMapLike<JSObject> globals = current.cast();
|
|
39
|
+
JSObject stdin = globals.get("wasmIdleJavaStdin");
|
|
40
|
+
if (stdin == null) return null;
|
|
41
|
+
JSMapLike<JSObject> input = stdin.cast();
|
|
42
|
+
if (JSObjects.hasProperty(stdin, "readChunk")) {
|
|
43
|
+
JSFunction readChunk = input.get("readChunk").cast();
|
|
44
|
+
JSObject result = (JSObject) readChunk.call(stdin);
|
|
45
|
+
if (result == null) return null;
|
|
46
|
+
Int8Array array = result.cast();
|
|
47
|
+
byte[] next = new byte[array.getLength()];
|
|
48
|
+
for (int i = 0; i < next.length; i++) next[i] = array.get(i);
|
|
49
|
+
return next;
|
|
50
|
+
}
|
|
51
|
+
JSFunction readByte = input.get("readByte").cast();
|
|
52
|
+
Object value = readByte.call(stdin);
|
|
53
|
+
int next = value != null ? Integer.parseInt(value.toString()) : -1;
|
|
54
|
+
return next < 0 ? null : new byte[] { (byte) next };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private boolean ensureChunk() {
|
|
58
|
+
while (position == chunk.length && !ended) {
|
|
59
|
+
byte[] next = readFromHost();
|
|
60
|
+
position = 0;
|
|
61
|
+
if (next == null) {
|
|
62
|
+
chunk = new byte[0];
|
|
63
|
+
ended = true;
|
|
64
|
+
} else {
|
|
65
|
+
chunk = next;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return position < chunk.length;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Override
|
|
72
|
+
public int read() {
|
|
73
|
+
return ensureChunk() ? chunk[position++] & 0xff : -1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Override
|
|
77
|
+
public int read(byte[] bytes, int offset, int length) {
|
|
78
|
+
if (bytes == null) throw new NullPointerException();
|
|
79
|
+
if (offset < 0 || length < 0 || length > bytes.length - offset) {
|
|
80
|
+
throw new IndexOutOfBoundsException();
|
|
81
|
+
}
|
|
82
|
+
if (length == 0) return 0;
|
|
83
|
+
if (!ensureChunk()) return -1;
|
|
84
|
+
int count = Math.min(length, chunk.length - position);
|
|
85
|
+
System.arraycopy(chunk, position, bytes, offset, count);
|
|
86
|
+
position += count;
|
|
87
|
+
return count;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Override
|
|
91
|
+
public int available() {
|
|
92
|
+
return chunk.length - position;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Single cursor shared by compatibility byte reads and bounded block reads. */
|
|
99
|
+
export function createJavaStdinBridge(initialInput, explicit, readInput, maxChunkBytes = 4096) {
|
|
100
|
+
if (!Number.isSafeInteger(maxChunkBytes) || maxChunkBytes < 1) {
|
|
101
|
+
throw new RangeError('Java stdin chunk size must be a positive safe integer');
|
|
102
|
+
}
|
|
103
|
+
const encoder = new TextEncoder();
|
|
104
|
+
let bytes = encoder.encode(initialInput);
|
|
105
|
+
let offset = 0;
|
|
106
|
+
let ended = false;
|
|
107
|
+
function ensureBytes() {
|
|
108
|
+
while (offset === bytes.length && !ended) {
|
|
109
|
+
const input = explicit ? null : readInput();
|
|
110
|
+
bytes = input === null ? new Uint8Array(0) : encoder.encode(input);
|
|
111
|
+
offset = 0;
|
|
112
|
+
ended = input === null;
|
|
113
|
+
}
|
|
114
|
+
return offset < bytes.length;
|
|
115
|
+
}
|
|
116
|
+
function releaseConsumed() {
|
|
117
|
+
if (offset === bytes.length) {
|
|
118
|
+
bytes = new Uint8Array(0);
|
|
119
|
+
offset = 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
readByte() {
|
|
124
|
+
if (!ensureBytes())
|
|
125
|
+
return -1;
|
|
126
|
+
const value = bytes[offset++];
|
|
127
|
+
releaseConsumed();
|
|
128
|
+
return value;
|
|
129
|
+
},
|
|
130
|
+
readChunk() {
|
|
131
|
+
if (!ensureBytes())
|
|
132
|
+
return null;
|
|
133
|
+
const count = Math.min(maxChunkBytes, bytes.length - offset);
|
|
134
|
+
const chunk = new Int8Array(bytes.buffer, bytes.byteOffset + offset, count);
|
|
135
|
+
offset += count;
|
|
136
|
+
releaseConsumed();
|
|
137
|
+
return chunk;
|
|
138
|
+
},
|
|
139
|
+
dispose() {
|
|
140
|
+
bytes = new Uint8Array(0);
|
|
141
|
+
offset = 0;
|
|
142
|
+
ended = true;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StaticWorkerRuntimeSandbox } from './staticWorkerRuntime';
|
|
2
|
+
/** Upstream Lean 4 frontend and IR interpreter (`lean --run`) compiled to wasm32 Emscripten. */
|
|
3
|
+
export default class Lean4 extends StaticWorkerRuntimeSandbox {
|
|
4
|
+
readonly defaultExecutionLimits: Readonly<{
|
|
5
|
+
maxWasmMemoryBytes: number;
|
|
6
|
+
}>;
|
|
7
|
+
readonly evidence: {
|
|
8
|
+
current: unknown;
|
|
9
|
+
};
|
|
10
|
+
constructor();
|
|
11
|
+
load(...args: Parameters<StaticWorkerRuntimeSandbox['load']>): Promise<void>;
|
|
12
|
+
run(...args: Parameters<StaticWorkerRuntimeSandbox['run']>): Promise<string | boolean>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { StaticWorkerRuntimeSandbox } from './staticWorkerRuntime';
|
|
2
|
+
import { LEAN4_DEFAULT_WASM_MEMORY_BYTES, preflightLean4RuntimeAssets, resolveLean4RuntimeAssetConfig } from './lean4Assets';
|
|
3
|
+
function lean4ExecutionOptions(options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
...options,
|
|
6
|
+
limits: {
|
|
7
|
+
...options.limits,
|
|
8
|
+
maxWasmMemoryBytes: options.limits?.maxWasmMemoryBytes ?? LEAN4_DEFAULT_WASM_MEMORY_BYTES
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Upstream Lean 4 frontend and IR interpreter (`lean --run`) compiled to wasm32 Emscripten. */
|
|
13
|
+
export default class Lean4 extends StaticWorkerRuntimeSandbox {
|
|
14
|
+
defaultExecutionLimits = Object.freeze({
|
|
15
|
+
maxWasmMemoryBytes: LEAN4_DEFAULT_WASM_MEMORY_BYTES
|
|
16
|
+
});
|
|
17
|
+
evidence;
|
|
18
|
+
constructor() {
|
|
19
|
+
const evidence = { current: undefined };
|
|
20
|
+
super({
|
|
21
|
+
displayName: 'Lean 4',
|
|
22
|
+
languageId: 'LEAN4',
|
|
23
|
+
defaultActivePath: 'Main.lean',
|
|
24
|
+
workerLifetime: { mode: 'per-run' },
|
|
25
|
+
runtimePreflightDelivery: 'transfer-owned',
|
|
26
|
+
inlineVerifiedWorker: true,
|
|
27
|
+
requireExactWorkerResponseUrl: true,
|
|
28
|
+
moduleWorker: true,
|
|
29
|
+
includeExecutionLimits: true,
|
|
30
|
+
onEvidence(value) {
|
|
31
|
+
evidence.current = value;
|
|
32
|
+
},
|
|
33
|
+
stdin: { mode: 'streaming', sourceHintPattern: /\b(?:getStdin|getLine|readToEnd)\b/u },
|
|
34
|
+
resolveRuntimeAssets: resolveLean4RuntimeAssetConfig,
|
|
35
|
+
async preflightRuntimeAssets(urls, context) {
|
|
36
|
+
evidence.current = undefined;
|
|
37
|
+
return context.createOwnedDelivery(await preflightLean4RuntimeAssets(urls.baseUrl, context));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
this.evidence = evidence;
|
|
41
|
+
}
|
|
42
|
+
async load(...args) {
|
|
43
|
+
args[4] = lean4ExecutionOptions(args[4]);
|
|
44
|
+
return super.load(...args);
|
|
45
|
+
}
|
|
46
|
+
async run(...args) {
|
|
47
|
+
args[5] = lean4ExecutionOptions(args[5]);
|
|
48
|
+
return super.run(...args);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ExecutionLimits, type RuntimeAssetCacheOptions } from '@wasm-idle/core';
|
|
2
|
+
import type { PlaygroundRuntimeAssets } from './assets';
|
|
3
|
+
/** Linear memory needed to start Lean, import Init, and interpret a small program. */
|
|
4
|
+
export declare const LEAN4_INITIAL_MEMORY_BYTES: number;
|
|
5
|
+
/** Default limit: importing the 277 MB Init library grows the heap to about 370 MiB. */
|
|
6
|
+
export declare const LEAN4_DEFAULT_WASM_MEMORY_BYTES: number;
|
|
7
|
+
export declare const LEAN4_RUNTIME_MANIFEST: import("@wasm-idle/core").RuntimeRegistryManifest;
|
|
8
|
+
export declare function resolveLean4RuntimeAssetConfig(runtimeAssets: string | PlaygroundRuntimeAssets, currentUrl: string): {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
workerUrl: string;
|
|
11
|
+
manifestFingerprint: "e60eb3fdcc108fa7c6127c5a8925ffb622706facb17bc4f84e9ed27f82495fa1";
|
|
12
|
+
workerReceipt: {
|
|
13
|
+
readonly bytes: 15759;
|
|
14
|
+
readonly sha256: "469ef5ec7accf4c221d06134ccd4989f14e9229c61dd35c386617a015aec6f03";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare function preflightLean4RuntimeAssets(baseUrl: string, options: {
|
|
18
|
+
limits: ExecutionLimits;
|
|
19
|
+
persistentCache?: RuntimeAssetCacheOptions;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
reportProgress?: (value: number, stage: string) => void;
|
|
22
|
+
fetch?: typeof globalThis.fetch;
|
|
23
|
+
}): Promise<Readonly<{
|
|
24
|
+
fingerprint: "e60eb3fdcc108fa7c6127c5a8925ffb622706facb17bc4f84e9ed27f82495fa1";
|
|
25
|
+
}>>;
|