moonscratch 0.1.1 → 0.1.2
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/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/index.d.mts +1173 -0
- package/dist/index.mjs +27135 -0
- package/package.json +6 -1
- package/.agents/skills/moonbit-agent-guide/LICENSE +0 -202
- package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +0 -1126
- package/.agents/skills/moonbit-agent-guide/SKILL.md +0 -1126
- package/.agents/skills/moonbit-agent-guide/ide.md +0 -116
- package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +0 -106
- package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +0 -422
- package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +0 -422
- package/.agents/skills/moonbit-practice/SKILL.md +0 -258
- package/.agents/skills/moonbit-practice/assets/ci.yaml +0 -25
- package/.agents/skills/moonbit-practice/reference/agents.md +0 -1469
- package/.agents/skills/moonbit-practice/reference/configuration.md +0 -228
- package/.agents/skills/moonbit-practice/reference/ffi.md +0 -229
- package/.agents/skills/moonbit-practice/reference/ide.md +0 -189
- package/.agents/skills/moonbit-practice/reference/performance.md +0 -217
- package/.agents/skills/moonbit-practice/reference/refactor.md +0 -154
- package/.agents/skills/moonbit-practice/reference/stdlib.md +0 -351
- package/.agents/skills/moonbit-practice/reference/testing.md +0 -228
- package/.agents/skills/moonbit-refactoring/LICENSE +0 -21
- package/.agents/skills/moonbit-refactoring/SKILL.md +0 -323
- package/.githooks/README.md +0 -23
- package/.githooks/pre-commit +0 -3
- package/.github/workflows/copilot-setup-steps.yml +0 -40
- package/AGENTS.md +0 -91
- package/PLAN.md +0 -64
- package/TODO.md +0 -120
- package/benchmarks/calc.bench.ts +0 -144
- package/benchmarks/draw.bench.ts +0 -215
- package/benchmarks/load.bench.ts +0 -28
- package/benchmarks/render.bench.ts +0 -53
- package/benchmarks/run.bench.ts +0 -8
- package/benchmarks/types.d.ts +0 -15
- package/docs/scratch-vm-specs/eventloop.md +0 -103
- package/docs/scratch-vm-specs/moonscratch-time-separation.md +0 -50
- package/index.html +0 -91
- package/js/AGENTS.md +0 -5
- package/js/a.ts +0 -52
- package/js/assets/AGENTS.md +0 -5
- package/js/assets/base64.test.ts +0 -14
- package/js/assets/base64.ts +0 -21
- package/js/assets/build-asset.test.ts +0 -26
- package/js/assets/build-asset.ts +0 -28
- package/js/assets/create.test.ts +0 -142
- package/js/assets/create.ts +0 -122
- package/js/assets/index.test.ts +0 -15
- package/js/assets/index.ts +0 -2
- package/js/assets/types.ts +0 -26
- package/js/assets/validation.test.ts +0 -34
- package/js/assets/validation.ts +0 -25
- package/js/assets.test.ts +0 -14
- package/js/assets.ts +0 -1
- package/js/index.test.ts +0 -26
- package/js/index.ts +0 -3
- package/js/render/index.test.ts +0 -65
- package/js/render/index.ts +0 -13
- package/js/render/sharp.ts +0 -87
- package/js/render/svg.ts +0 -68
- package/js/render/types.ts +0 -35
- package/js/render/utils.ts +0 -108
- package/js/render/webgl.ts +0 -274
- package/js/sharp-optional.d.ts +0 -16
- package/js/test/helpers.ts +0 -116
- package/js/test/hikkaku-sample.test.ts +0 -37
- package/js/test/rubik-components.input-motion.test.ts +0 -60
- package/js/test/rubik-components.lists.test.ts +0 -49
- package/js/test/rubik-components.operators.test.ts +0 -104
- package/js/test/rubik-components.pen.test.ts +0 -112
- package/js/test/rubik-components.procedures-loops.test.ts +0 -72
- package/js/test/rubik-components.variables-branches.test.ts +0 -57
- package/js/test/rubik-components.visibility-entry.test.ts +0 -31
- package/js/test/test-projects.ts +0 -598
- package/js/test/variable.ts +0 -200
- package/js/test/warp.test.ts +0 -59
- package/js/vm/AGENTS.md +0 -6
- package/js/vm/README.md +0 -183
- package/js/vm/bindings.test.ts +0 -13
- package/js/vm/bindings.ts +0 -5
- package/js/vm/compare-operators.test.ts +0 -145
- package/js/vm/constants.test.ts +0 -11
- package/js/vm/constants.ts +0 -4
- package/js/vm/effect-guards.test.ts +0 -68
- package/js/vm/effect-guards.ts +0 -44
- package/js/vm/factory.test.ts +0 -486
- package/js/vm/factory.ts +0 -615
- package/js/vm/headless-vm.test.ts +0 -131
- package/js/vm/headless-vm.ts +0 -342
- package/js/vm/index.test.ts +0 -28
- package/js/vm/index.ts +0 -5
- package/js/vm/internal-types.ts +0 -32
- package/js/vm/json.test.ts +0 -40
- package/js/vm/json.ts +0 -273
- package/js/vm/normalize.test.ts +0 -48
- package/js/vm/normalize.ts +0 -65
- package/js/vm/options.test.ts +0 -30
- package/js/vm/options.ts +0 -55
- package/js/vm/pen-transparency.test.ts +0 -115
- package/js/vm/program-wasm.ts +0 -322
- package/js/vm/scheduler-render.test.ts +0 -401
- package/js/vm/scratch-assets.test.ts +0 -136
- package/js/vm/scratch-assets.ts +0 -202
- package/js/vm/types.ts +0 -358
- package/js/vm/value-guards.test.ts +0 -25
- package/js/vm/value-guards.ts +0 -18
- package/moon.mod.json +0 -10
- package/scripts/preinstall.ts +0 -4
- package/src/AGENTS.md +0 -6
- package/src/api.mbt +0 -161
- package/src/api_aot_commands.mbt +0 -184
- package/src/api_effects_json.mbt +0 -72
- package/src/api_options.mbt +0 -60
- package/src/api_program_wasm.mbt +0 -1647
- package/src/api_program_wat.mbt +0 -2206
- package/src/api_snapshot_json.mbt +0 -44
- package/src/cmd/AGENTS.md +0 -5
- package/src/cmd/main/AGENTS.md +0 -5
- package/src/cmd/main/main.mbt +0 -29
- package/src/cmd/main/moon.pkg +0 -7
- package/src/cmd/main/pkg.generated.mbti +0 -13
- package/src/json_helpers.mbt +0 -176
- package/src/moon.pkg +0 -65
- package/src/moonscratch.mbt +0 -3
- package/src/moonscratch_wbtest.mbt +0 -40
- package/src/parser_sb3.mbt +0 -890
- package/src/pkg.generated.mbti +0 -479
- package/src/runtime_eval.mbt +0 -2844
- package/src/runtime_exec.mbt +0 -3850
- package/src/runtime_render.mbt +0 -2550
- package/src/runtime_state.mbt +0 -870
- package/src/test/AGENTS.md +0 -3
- package/src/test/projects/AGENTS.md +0 -6
- package/src/test/projects/moon.pkg +0 -4
- package/src/test/projects/moonscratch_compat_test.mbt +0 -642
- package/src/test/projects/moonscratch_core_test.mbt +0 -1332
- package/src/test/projects/moonscratch_runtime_test.mbt +0 -1087
- package/src/test/projects/pkg.generated.mbti +0 -13
- package/src/test/projects/test_support.mbt +0 -35
- package/src/types_effects.mbt +0 -20
- package/src/types_error.mbt +0 -4
- package/src/types_options.mbt +0 -31
- package/src/types_runtime_structs.mbt +0 -254
- package/src/types_vm.mbt +0 -109
- package/tsconfig.json +0 -29
- package/viewer/index.ts +0 -399
- package/viewer/vite.d.ts +0 -1
- package/viewer/worker.ts +0 -161
- package/vite.config.ts +0 -61
package/js/vm/factory.ts
DELETED
|
@@ -1,615 +0,0 @@
|
|
|
1
|
-
import { moonscratch } from './bindings.ts'
|
|
2
|
-
import { HeadlessVM } from './headless-vm.ts'
|
|
3
|
-
import type { MoonResult } from './internal-types.ts'
|
|
4
|
-
import {
|
|
5
|
-
toOptionalJsonString,
|
|
6
|
-
toProjectJsonString,
|
|
7
|
-
unwrapResult,
|
|
8
|
-
} from './json.ts'
|
|
9
|
-
import { toOptionsJson } from './options.ts'
|
|
10
|
-
import { instantiateProgramModule } from './program-wasm.ts'
|
|
11
|
-
import { resolveMissingScratchAssets } from './scratch-assets.ts'
|
|
12
|
-
import type {
|
|
13
|
-
CompileProjectToWasmOptions,
|
|
14
|
-
CompileProjectToWasmResult,
|
|
15
|
-
CompileProjectToWatOptions,
|
|
16
|
-
CompileProjectToWatResult,
|
|
17
|
-
CreateHeadlessVMFromProjectOptions,
|
|
18
|
-
CreateHeadlessVMOptions,
|
|
19
|
-
CreateHeadlessVMWithScratchAssetsOptions,
|
|
20
|
-
CreateProgramModuleFromProjectOptions,
|
|
21
|
-
CreateProgramModuleOptions,
|
|
22
|
-
JsonValue,
|
|
23
|
-
PrecompileProgramForRuntimeOptions,
|
|
24
|
-
ProgramManifest,
|
|
25
|
-
ProgramModule,
|
|
26
|
-
RuntimeHandle,
|
|
27
|
-
} from './types.ts'
|
|
28
|
-
|
|
29
|
-
type BoundMoonscratchFactory = {
|
|
30
|
-
vm_compile_from_json?: (
|
|
31
|
-
projectJson: string,
|
|
32
|
-
assetsJson?: string,
|
|
33
|
-
) => MoonResult<unknown, unknown>
|
|
34
|
-
vm_compile_project_to_wat?: (
|
|
35
|
-
projectJson: string,
|
|
36
|
-
assetsJson?: string,
|
|
37
|
-
) => MoonResult<string, unknown>
|
|
38
|
-
vm_compile_project_to_wasm?: (
|
|
39
|
-
projectJson: string,
|
|
40
|
-
assetsJson?: string,
|
|
41
|
-
) => MoonResult<string, unknown>
|
|
42
|
-
vm_abi_version?: () => number
|
|
43
|
-
vm_set_aot_commands_json?: (
|
|
44
|
-
vmHandle: unknown,
|
|
45
|
-
commandsJson: string,
|
|
46
|
-
) => MoonResult<unknown, unknown>
|
|
47
|
-
vm_get_variable_number_by_id?: (
|
|
48
|
-
vmHandle: unknown,
|
|
49
|
-
targetIndex: number,
|
|
50
|
-
variableId: string,
|
|
51
|
-
) => number
|
|
52
|
-
vm_set_variable_number_by_id?: (
|
|
53
|
-
vmHandle: unknown,
|
|
54
|
-
targetIndex: number,
|
|
55
|
-
variableId: string,
|
|
56
|
-
value: number,
|
|
57
|
-
) => void
|
|
58
|
-
vm_set_variable_json_by_id?: (
|
|
59
|
-
vmHandle: unknown,
|
|
60
|
-
targetIndex: number,
|
|
61
|
-
variableId: string,
|
|
62
|
-
valueJson: string,
|
|
63
|
-
) => void
|
|
64
|
-
vm_exec_script_tail_by_pc?: (
|
|
65
|
-
vmHandle: unknown,
|
|
66
|
-
targetIndex: number,
|
|
67
|
-
startPc: number,
|
|
68
|
-
) => number
|
|
69
|
-
vm_exec_opcode_once_by_pc?: (
|
|
70
|
-
vmHandle: unknown,
|
|
71
|
-
targetIndex: number,
|
|
72
|
-
pc: number,
|
|
73
|
-
) => number
|
|
74
|
-
vm_exec_draw_opcode?: (
|
|
75
|
-
vmHandle: unknown,
|
|
76
|
-
targetIndex: number,
|
|
77
|
-
opcode: string,
|
|
78
|
-
arg0: number,
|
|
79
|
-
arg1: number,
|
|
80
|
-
extra: number,
|
|
81
|
-
) => number
|
|
82
|
-
vm_new_from_compiled?: (
|
|
83
|
-
precompiled: unknown,
|
|
84
|
-
optionsJson?: string,
|
|
85
|
-
) => MoonResult<unknown, unknown>
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const OPCODE_SET_VERSION = 2
|
|
89
|
-
const runtimePrecompiledCacheSymbol = Symbol(
|
|
90
|
-
'moonscratch.runtime_precompiled_cache',
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
type ProgramModuleInternal = ProgramModule & {
|
|
94
|
-
[runtimePrecompiledCacheSymbol]?: WeakMap<object, unknown>
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const hasAnyAssetEntry = (assets: Record<string, JsonValue>): boolean => {
|
|
98
|
-
for (const _key in assets) {
|
|
99
|
-
return true
|
|
100
|
-
}
|
|
101
|
-
return false
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const toAssetsJson = (
|
|
105
|
-
assets: string | Record<string, JsonValue> | undefined,
|
|
106
|
-
): string | undefined => {
|
|
107
|
-
return assets === undefined ||
|
|
108
|
-
(typeof assets !== 'string' && !hasAnyAssetEntry(assets))
|
|
109
|
-
? undefined
|
|
110
|
-
: toOptionalJsonString(assets, 'assets')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const requireBinding = (): BoundMoonscratchFactory => {
|
|
114
|
-
return moonscratch as unknown as BoundMoonscratchFactory
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const requireAbiVersion = (binding: BoundMoonscratchFactory): number => {
|
|
118
|
-
if (typeof binding.vm_abi_version !== 'function') {
|
|
119
|
-
throw new Error(
|
|
120
|
-
'vm_abi_version is unavailable in this build. Please rebuild moonscratch JS bindings.',
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
const abiVersion = binding.vm_abi_version()
|
|
124
|
-
if (!Number.isInteger(abiVersion) || abiVersion <= 0) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`vm_abi_version returned an invalid value: ${String(abiVersion)}`,
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
return abiVersion
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const encodeUtf8Length = (value: string): number =>
|
|
133
|
-
new TextEncoder().encode(value).length
|
|
134
|
-
|
|
135
|
-
const fnv1aHex = (input: string): string => {
|
|
136
|
-
let hash = 0x811c9dc5
|
|
137
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
138
|
-
hash ^= input.charCodeAt(index)
|
|
139
|
-
hash = Math.imul(hash, 0x01000193)
|
|
140
|
-
}
|
|
141
|
-
return (hash >>> 0).toString(16).padStart(8, '0')
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const createManifest = ({
|
|
145
|
-
abiVersion,
|
|
146
|
-
projectJson,
|
|
147
|
-
assetsJson,
|
|
148
|
-
}: {
|
|
149
|
-
abiVersion: number
|
|
150
|
-
projectJson: string
|
|
151
|
-
assetsJson: string
|
|
152
|
-
}): ProgramManifest => {
|
|
153
|
-
return {
|
|
154
|
-
abiVersion,
|
|
155
|
-
opcodeSetVersion: OPCODE_SET_VERSION,
|
|
156
|
-
projectByteLength: encodeUtf8Length(projectJson),
|
|
157
|
-
assetsByteLength: encodeUtf8Length(assetsJson),
|
|
158
|
-
buildFingerprint: fnv1aHex(
|
|
159
|
-
`abi=${String(abiVersion)}|project=${projectJson}|assets=${assetsJson}`,
|
|
160
|
-
),
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export const createRuntime = (): RuntimeHandle => {
|
|
165
|
-
const binding = requireBinding()
|
|
166
|
-
const abiVersion = requireAbiVersion(binding)
|
|
167
|
-
return {
|
|
168
|
-
raw: binding,
|
|
169
|
-
abiVersion,
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export const compileProjectToWat = ({
|
|
174
|
-
projectJson,
|
|
175
|
-
assets,
|
|
176
|
-
}: CompileProjectToWatOptions): CompileProjectToWatResult => {
|
|
177
|
-
const binding = requireBinding()
|
|
178
|
-
if (typeof binding.vm_compile_project_to_wat !== 'function') {
|
|
179
|
-
throw new Error(
|
|
180
|
-
'vm_compile_project_to_wat is unavailable in this build. Please rebuild moonscratch JS bindings.',
|
|
181
|
-
)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const normalizedProjectJson = toProjectJsonString(projectJson)
|
|
185
|
-
const normalizedAssetsJson = toAssetsJson(assets)
|
|
186
|
-
const abiVersion = requireAbiVersion(binding)
|
|
187
|
-
const wat = unwrapResult(
|
|
188
|
-
binding.vm_compile_project_to_wat(
|
|
189
|
-
normalizedProjectJson,
|
|
190
|
-
normalizedAssetsJson,
|
|
191
|
-
),
|
|
192
|
-
'vm_compile_project_to_wat failed',
|
|
193
|
-
)
|
|
194
|
-
return {
|
|
195
|
-
wat,
|
|
196
|
-
abiVersion,
|
|
197
|
-
manifest: createManifest({
|
|
198
|
-
abiVersion,
|
|
199
|
-
projectJson: normalizedProjectJson,
|
|
200
|
-
assetsJson: normalizedAssetsJson ?? '{}',
|
|
201
|
-
}),
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const toUint8Array = (value: Uint8Array | ArrayBuffer): Uint8Array => {
|
|
206
|
-
return value instanceof Uint8Array ? value : new Uint8Array(value)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const decodeBase64ToUint8Array = (base64: string): Uint8Array => {
|
|
210
|
-
const maybeBuffer = globalThis as {
|
|
211
|
-
Buffer?: { from: (input: string, encoding: string) => Uint8Array }
|
|
212
|
-
}
|
|
213
|
-
if (typeof maybeBuffer.Buffer?.from === 'function') {
|
|
214
|
-
return new Uint8Array(maybeBuffer.Buffer.from(base64, 'base64'))
|
|
215
|
-
}
|
|
216
|
-
const maybeAtob = (globalThis as { atob?: (input: string) => string }).atob
|
|
217
|
-
if (typeof maybeAtob === 'function') {
|
|
218
|
-
const binary = maybeAtob(base64)
|
|
219
|
-
const out = new Uint8Array(binary.length)
|
|
220
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
221
|
-
out[index] = binary.charCodeAt(index) & 0xff
|
|
222
|
-
}
|
|
223
|
-
return out
|
|
224
|
-
}
|
|
225
|
-
throw new Error('No base64 decoder found in this runtime')
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export const compileProjectToWasm = ({
|
|
229
|
-
watToWasm,
|
|
230
|
-
...rest
|
|
231
|
-
}: CompileProjectToWasmOptions): CompileProjectToWasmResult => {
|
|
232
|
-
const compiled = compileProjectToWat(rest)
|
|
233
|
-
let wasm: Uint8Array
|
|
234
|
-
if (watToWasm) {
|
|
235
|
-
wasm = toUint8Array(watToWasm(compiled.wat))
|
|
236
|
-
} else {
|
|
237
|
-
const binding = requireBinding()
|
|
238
|
-
if (typeof binding.vm_compile_project_to_wasm !== 'function') {
|
|
239
|
-
throw new Error(
|
|
240
|
-
'vm_compile_project_to_wasm is unavailable in this build. Please rebuild moonscratch JS bindings.',
|
|
241
|
-
)
|
|
242
|
-
}
|
|
243
|
-
const wasmBase64 = unwrapResult(
|
|
244
|
-
binding.vm_compile_project_to_wasm(
|
|
245
|
-
toProjectJsonString(rest.projectJson),
|
|
246
|
-
toAssetsJson(rest.assets),
|
|
247
|
-
),
|
|
248
|
-
'vm_compile_project_to_wasm failed',
|
|
249
|
-
)
|
|
250
|
-
wasm = decodeBase64ToUint8Array(wasmBase64)
|
|
251
|
-
}
|
|
252
|
-
return {
|
|
253
|
-
...compiled,
|
|
254
|
-
wasmBytes: wasm,
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export const createProgramModule = ({
|
|
259
|
-
wasmBytes,
|
|
260
|
-
manifest,
|
|
261
|
-
}: CreateProgramModuleOptions): ProgramModule => {
|
|
262
|
-
return instantiateProgramModule(wasmBytes, manifest)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export const createProgramModuleFromProject = (
|
|
266
|
-
options: CreateProgramModuleFromProjectOptions,
|
|
267
|
-
): ProgramModule => {
|
|
268
|
-
const compiled = compileProjectToWasm(options)
|
|
269
|
-
return createProgramModule({
|
|
270
|
-
wasmBytes: compiled.wasmBytes,
|
|
271
|
-
manifest: compiled.manifest,
|
|
272
|
-
})
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const requireRuntimeBinding = (
|
|
276
|
-
runtime?: RuntimeHandle,
|
|
277
|
-
): BoundMoonscratchFactory => {
|
|
278
|
-
return (
|
|
279
|
-
(runtime?.raw as BoundMoonscratchFactory | undefined) ?? requireBinding()
|
|
280
|
-
)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const runtimePrecompiledCache = (
|
|
284
|
-
program: ProgramModule,
|
|
285
|
-
): WeakMap<object, unknown> => {
|
|
286
|
-
const internal = program as ProgramModuleInternal
|
|
287
|
-
if (!internal[runtimePrecompiledCacheSymbol]) {
|
|
288
|
-
internal[runtimePrecompiledCacheSymbol] = new WeakMap<object, unknown>()
|
|
289
|
-
}
|
|
290
|
-
return internal[runtimePrecompiledCacheSymbol]
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const compileProgramForRuntime = (
|
|
294
|
-
program: ProgramModule,
|
|
295
|
-
binding: BoundMoonscratchFactory,
|
|
296
|
-
): unknown => {
|
|
297
|
-
if (typeof binding.vm_compile_from_json !== 'function') {
|
|
298
|
-
throw new Error(
|
|
299
|
-
'vm_compile_from_json is unavailable in this build. Please rebuild moonscratch JS bindings.',
|
|
300
|
-
)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const runtimeKey = binding as object
|
|
304
|
-
const cache = runtimePrecompiledCache(program)
|
|
305
|
-
const cached = cache.get(runtimeKey)
|
|
306
|
-
if (cached !== undefined) {
|
|
307
|
-
return cached
|
|
308
|
-
}
|
|
309
|
-
const payload = program.readPayload()
|
|
310
|
-
const compiled = unwrapResult(
|
|
311
|
-
binding.vm_compile_from_json(payload.projectJson, payload.assetsJson),
|
|
312
|
-
'vm_compile_from_json failed',
|
|
313
|
-
)
|
|
314
|
-
cache.set(runtimeKey, compiled)
|
|
315
|
-
return compiled
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const withCommandsExecMode = (
|
|
319
|
-
commandsJson: string,
|
|
320
|
-
execMode: string,
|
|
321
|
-
): string | null => {
|
|
322
|
-
try {
|
|
323
|
-
const parsed = JSON.parse(commandsJson) as unknown
|
|
324
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
325
|
-
return null
|
|
326
|
-
}
|
|
327
|
-
const next = {
|
|
328
|
-
...(parsed as Record<string, unknown>),
|
|
329
|
-
exec_mode: execMode,
|
|
330
|
-
}
|
|
331
|
-
return JSON.stringify(next)
|
|
332
|
-
} catch {
|
|
333
|
-
return null
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const commandContainsHostTail = (entry: unknown, depth = 0): boolean => {
|
|
338
|
-
if (depth > 64 || !entry || typeof entry !== 'object') {
|
|
339
|
-
return false
|
|
340
|
-
}
|
|
341
|
-
const command = entry as {
|
|
342
|
-
op?: unknown
|
|
343
|
-
then?: unknown
|
|
344
|
-
else?: unknown
|
|
345
|
-
body?: unknown
|
|
346
|
-
}
|
|
347
|
-
if (command.op === 'host_tail') {
|
|
348
|
-
return true
|
|
349
|
-
}
|
|
350
|
-
if (command.op === 'if' && Array.isArray(command.then)) {
|
|
351
|
-
return command.then.some((child) =>
|
|
352
|
-
commandContainsHostTail(child, depth + 1),
|
|
353
|
-
)
|
|
354
|
-
}
|
|
355
|
-
if (command.op === 'if_else') {
|
|
356
|
-
const thenHasTail =
|
|
357
|
-
Array.isArray(command.then) &&
|
|
358
|
-
command.then.some((child) => commandContainsHostTail(child, depth + 1))
|
|
359
|
-
const elseHasTail =
|
|
360
|
-
Array.isArray(command.else) &&
|
|
361
|
-
command.else.some((child) => commandContainsHostTail(child, depth + 1))
|
|
362
|
-
return thenHasTail || elseHasTail
|
|
363
|
-
}
|
|
364
|
-
if (
|
|
365
|
-
(command.op === 'repeat' ||
|
|
366
|
-
command.op === 'repeat_until' ||
|
|
367
|
-
command.op === 'while') &&
|
|
368
|
-
Array.isArray(command.body)
|
|
369
|
-
) {
|
|
370
|
-
return command.body.some((child) =>
|
|
371
|
-
commandContainsHostTail(child, depth + 1),
|
|
372
|
-
)
|
|
373
|
-
}
|
|
374
|
-
return false
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const commandsIncludeHostTail = (commandsJson: string): boolean => {
|
|
378
|
-
try {
|
|
379
|
-
const parsed = JSON.parse(commandsJson) as unknown
|
|
380
|
-
const exec = Array.isArray(parsed)
|
|
381
|
-
? parsed
|
|
382
|
-
: parsed &&
|
|
383
|
-
typeof parsed === 'object' &&
|
|
384
|
-
Array.isArray((parsed as { exec?: unknown }).exec)
|
|
385
|
-
? (parsed as { exec: unknown[] }).exec
|
|
386
|
-
: []
|
|
387
|
-
return exec.some((entry) => commandContainsHostTail(entry))
|
|
388
|
-
} catch {
|
|
389
|
-
return false
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const hasWasmHostBridgeApi = (binding: BoundMoonscratchFactory): boolean => {
|
|
394
|
-
return (
|
|
395
|
-
typeof binding.vm_get_variable_number_by_id === 'function' &&
|
|
396
|
-
typeof binding.vm_set_variable_number_by_id === 'function' &&
|
|
397
|
-
typeof binding.vm_set_variable_json_by_id === 'function' &&
|
|
398
|
-
typeof binding.vm_exec_opcode_once_by_pc === 'function' &&
|
|
399
|
-
typeof binding.vm_exec_script_tail_by_pc === 'function' &&
|
|
400
|
-
typeof binding.vm_exec_draw_opcode === 'function'
|
|
401
|
-
)
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
export const precompileProgramForRuntime = ({
|
|
405
|
-
program,
|
|
406
|
-
runtime,
|
|
407
|
-
}: PrecompileProgramForRuntimeOptions): void => {
|
|
408
|
-
const binding = requireRuntimeBinding(runtime)
|
|
409
|
-
const runtimeAbi = requireAbiVersion(binding)
|
|
410
|
-
if (program.abiVersion !== runtimeAbi) {
|
|
411
|
-
throw new Error(
|
|
412
|
-
`Program ABI ${String(program.abiVersion)} does not match runtime ABI ${String(runtimeAbi)}`,
|
|
413
|
-
)
|
|
414
|
-
}
|
|
415
|
-
void compileProgramForRuntime(program, binding)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
export const createHeadlessVM = ({
|
|
419
|
-
runtime,
|
|
420
|
-
program,
|
|
421
|
-
options,
|
|
422
|
-
initialNowMs,
|
|
423
|
-
viewerLanguage,
|
|
424
|
-
translateCache,
|
|
425
|
-
}: CreateHeadlessVMOptions): HeadlessVM => {
|
|
426
|
-
const binding = requireRuntimeBinding(runtime)
|
|
427
|
-
if (typeof binding.vm_new_from_compiled !== 'function') {
|
|
428
|
-
throw new Error(
|
|
429
|
-
'vm_new_from_compiled is unavailable in this build. Please rebuild moonscratch JS bindings.',
|
|
430
|
-
)
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const runtimeAbi = requireAbiVersion(binding)
|
|
434
|
-
if (program.abiVersion !== runtimeAbi) {
|
|
435
|
-
throw new Error(
|
|
436
|
-
`Program ABI ${String(program.abiVersion)} does not match runtime ABI ${String(runtimeAbi)}`,
|
|
437
|
-
)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const precompiled = compileProgramForRuntime(program, binding)
|
|
441
|
-
const vm = unwrapResult(
|
|
442
|
-
binding.vm_new_from_compiled(precompiled, toOptionsJson(options)),
|
|
443
|
-
'vm_new_from_compiled failed',
|
|
444
|
-
)
|
|
445
|
-
let afterGreenFlag: (() => void) | undefined
|
|
446
|
-
let beforeStepFrame: (() => void) | undefined
|
|
447
|
-
const payload = program.readPayload()
|
|
448
|
-
if (payload.commandsJson && payload.commandsJson.trim().length > 0) {
|
|
449
|
-
if (typeof binding.vm_set_aot_commands_json !== 'function') {
|
|
450
|
-
throw new Error(
|
|
451
|
-
'vm_set_aot_commands_json is unavailable in this build. Please rebuild moonscratch JS bindings.',
|
|
452
|
-
)
|
|
453
|
-
}
|
|
454
|
-
const hasHostTail = commandsIncludeHostTail(payload.commandsJson)
|
|
455
|
-
let commandsJsonForRuntime = payload.commandsJson
|
|
456
|
-
if (program.hasWasmExec() && hasWasmHostBridgeApi(binding)) {
|
|
457
|
-
const commandsJsonWasmOnly = withCommandsExecMode(
|
|
458
|
-
payload.commandsJson,
|
|
459
|
-
'wasm_only',
|
|
460
|
-
)
|
|
461
|
-
if (commandsJsonWasmOnly !== null) {
|
|
462
|
-
const runner = program.createWasmExecRunner({
|
|
463
|
-
getVarNumber: (targetIndex, variableId) => {
|
|
464
|
-
return (
|
|
465
|
-
binding.vm_get_variable_number_by_id?.(
|
|
466
|
-
vm,
|
|
467
|
-
targetIndex,
|
|
468
|
-
variableId,
|
|
469
|
-
) ?? 0
|
|
470
|
-
)
|
|
471
|
-
},
|
|
472
|
-
setVarNumber: (targetIndex, variableId, value) => {
|
|
473
|
-
binding.vm_set_variable_number_by_id?.(
|
|
474
|
-
vm,
|
|
475
|
-
targetIndex,
|
|
476
|
-
variableId,
|
|
477
|
-
value,
|
|
478
|
-
)
|
|
479
|
-
},
|
|
480
|
-
setVarJson: (targetIndex, variableId, valueJson) => {
|
|
481
|
-
binding.vm_set_variable_json_by_id?.(
|
|
482
|
-
vm,
|
|
483
|
-
targetIndex,
|
|
484
|
-
variableId,
|
|
485
|
-
valueJson,
|
|
486
|
-
)
|
|
487
|
-
},
|
|
488
|
-
execHostTail: (targetIndex, startPc) => {
|
|
489
|
-
return (
|
|
490
|
-
binding.vm_exec_script_tail_by_pc?.(vm, targetIndex, startPc) ?? 0
|
|
491
|
-
)
|
|
492
|
-
},
|
|
493
|
-
execHostOpcode: (targetIndex, pc) => {
|
|
494
|
-
return binding.vm_exec_opcode_once_by_pc?.(vm, targetIndex, pc) ?? 0
|
|
495
|
-
},
|
|
496
|
-
execDrawOpcode: (targetIndex, opcode, arg0, arg1, extra) => {
|
|
497
|
-
return (
|
|
498
|
-
binding.vm_exec_draw_opcode?.(
|
|
499
|
-
vm,
|
|
500
|
-
targetIndex,
|
|
501
|
-
opcode,
|
|
502
|
-
arg0,
|
|
503
|
-
arg1,
|
|
504
|
-
extra,
|
|
505
|
-
) ?? 0
|
|
506
|
-
)
|
|
507
|
-
},
|
|
508
|
-
})
|
|
509
|
-
if (runner) {
|
|
510
|
-
commandsJsonForRuntime = commandsJsonWasmOnly
|
|
511
|
-
if (hasHostTail) {
|
|
512
|
-
let pendingRunner = false
|
|
513
|
-
afterGreenFlag = () => {
|
|
514
|
-
pendingRunner = true
|
|
515
|
-
}
|
|
516
|
-
beforeStepFrame = () => {
|
|
517
|
-
if (!pendingRunner) {
|
|
518
|
-
return
|
|
519
|
-
}
|
|
520
|
-
pendingRunner = false
|
|
521
|
-
void runner()
|
|
522
|
-
}
|
|
523
|
-
} else {
|
|
524
|
-
afterGreenFlag = () => {
|
|
525
|
-
void runner()
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
unwrapResult(
|
|
532
|
-
binding.vm_set_aot_commands_json(vm, commandsJsonForRuntime),
|
|
533
|
-
'vm_set_aot_commands_json failed',
|
|
534
|
-
)
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
const runtimeVm = new HeadlessVM(vm, {
|
|
538
|
-
afterGreenFlag,
|
|
539
|
-
beforeStepFrame,
|
|
540
|
-
})
|
|
541
|
-
runtimeVm.setTime(initialNowMs ?? Date.now())
|
|
542
|
-
if (viewerLanguage !== undefined) {
|
|
543
|
-
runtimeVm.setViewerLanguage(viewerLanguage)
|
|
544
|
-
}
|
|
545
|
-
if (translateCache !== undefined) {
|
|
546
|
-
runtimeVm.setTranslateCache(translateCache)
|
|
547
|
-
}
|
|
548
|
-
return runtimeVm
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
export const createVM = createHeadlessVM
|
|
552
|
-
|
|
553
|
-
export const createHeadlessVMFromProject = ({
|
|
554
|
-
projectJson,
|
|
555
|
-
assets,
|
|
556
|
-
watToWasm,
|
|
557
|
-
runtime,
|
|
558
|
-
options,
|
|
559
|
-
initialNowMs,
|
|
560
|
-
viewerLanguage,
|
|
561
|
-
translateCache,
|
|
562
|
-
}: CreateHeadlessVMFromProjectOptions): HeadlessVM => {
|
|
563
|
-
const program = createProgramModuleFromProject({
|
|
564
|
-
projectJson,
|
|
565
|
-
assets,
|
|
566
|
-
watToWasm,
|
|
567
|
-
})
|
|
568
|
-
return createHeadlessVM({
|
|
569
|
-
runtime,
|
|
570
|
-
program,
|
|
571
|
-
options,
|
|
572
|
-
initialNowMs,
|
|
573
|
-
viewerLanguage,
|
|
574
|
-
translateCache,
|
|
575
|
-
})
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
export const createVMFromProject = createHeadlessVMFromProject
|
|
579
|
-
|
|
580
|
-
export const createHeadlessVMWithScratchAssets = async ({
|
|
581
|
-
projectJson,
|
|
582
|
-
assets = {},
|
|
583
|
-
scratchCdnBaseUrl,
|
|
584
|
-
fetchAsset,
|
|
585
|
-
decodeImageBytes,
|
|
586
|
-
watToWasm,
|
|
587
|
-
runtime,
|
|
588
|
-
options,
|
|
589
|
-
initialNowMs,
|
|
590
|
-
viewerLanguage,
|
|
591
|
-
translateCache,
|
|
592
|
-
}: CreateHeadlessVMWithScratchAssetsOptions): Promise<HeadlessVM> => {
|
|
593
|
-
const resolvedAssets = await resolveMissingScratchAssets({
|
|
594
|
-
projectJson,
|
|
595
|
-
assets,
|
|
596
|
-
scratchCdnBaseUrl,
|
|
597
|
-
fetchAsset,
|
|
598
|
-
decodeImageBytes,
|
|
599
|
-
})
|
|
600
|
-
|
|
601
|
-
return createHeadlessVMFromProject({
|
|
602
|
-
projectJson,
|
|
603
|
-
assets: resolvedAssets,
|
|
604
|
-
watToWasm,
|
|
605
|
-
runtime,
|
|
606
|
-
options,
|
|
607
|
-
initialNowMs,
|
|
608
|
-
viewerLanguage,
|
|
609
|
-
translateCache,
|
|
610
|
-
})
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
export const createVMWithScratchAssets = createHeadlessVMWithScratchAssets
|
|
614
|
-
|
|
615
|
-
export { moonscratch }
|