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/program-wasm.ts
DELETED
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ProgramManifest,
|
|
3
|
-
ProgramModule,
|
|
4
|
-
ProgramPayload,
|
|
5
|
-
ProgramWasmBytes,
|
|
6
|
-
ProgramWasmExecHost,
|
|
7
|
-
ProgramWasmExecRunner,
|
|
8
|
-
} from './types.ts'
|
|
9
|
-
|
|
10
|
-
const OPCODE_SET_VERSION = 2
|
|
11
|
-
|
|
12
|
-
const utf8Encoder = new TextEncoder()
|
|
13
|
-
const utf8Decoder = new TextDecoder()
|
|
14
|
-
|
|
15
|
-
const toUint8Array = (input: ProgramWasmBytes): Uint8Array => {
|
|
16
|
-
return input instanceof Uint8Array ? input : new Uint8Array(input)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const encodeUtf8 = (value: string): Uint8Array => utf8Encoder.encode(value)
|
|
20
|
-
|
|
21
|
-
const decodeUtf8 = (bytes: Uint8Array): string => utf8Decoder.decode(bytes)
|
|
22
|
-
|
|
23
|
-
const toArrayBuffer = (bytes: Uint8Array): ArrayBuffer => {
|
|
24
|
-
const out = new ArrayBuffer(bytes.byteLength)
|
|
25
|
-
new Uint8Array(out).set(bytes)
|
|
26
|
-
return out
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const decodeBase64 = (base64: string): Uint8Array => {
|
|
30
|
-
const maybeBuffer = globalThis as {
|
|
31
|
-
Buffer?: { from: (input: string, encoding: string) => Uint8Array }
|
|
32
|
-
}
|
|
33
|
-
if (typeof maybeBuffer.Buffer?.from === 'function') {
|
|
34
|
-
return new Uint8Array(maybeBuffer.Buffer.from(base64, 'base64'))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const maybeAtob = (globalThis as { atob?: (input: string) => string }).atob
|
|
38
|
-
if (typeof maybeAtob === 'function') {
|
|
39
|
-
const binary = maybeAtob(base64)
|
|
40
|
-
const out = new Uint8Array(binary.length)
|
|
41
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
42
|
-
out[index] = binary.charCodeAt(index) & 0xff
|
|
43
|
-
}
|
|
44
|
-
return out
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
throw new Error('No base64 decoder found in this runtime')
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const readExportedI32 = (
|
|
51
|
-
exports: Record<string, unknown>,
|
|
52
|
-
name: string,
|
|
53
|
-
): number => {
|
|
54
|
-
const raw = exports[name]
|
|
55
|
-
if (typeof raw !== 'function') {
|
|
56
|
-
throw new Error(`Program WASM export "${name}" is missing`)
|
|
57
|
-
}
|
|
58
|
-
const value = (raw as () => number)()
|
|
59
|
-
if (!Number.isInteger(value)) {
|
|
60
|
-
throw new Error(`Program WASM export "${name}" did not return an integer`)
|
|
61
|
-
}
|
|
62
|
-
return value
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const readMemorySlice = (
|
|
66
|
-
memory: WebAssembly.Memory,
|
|
67
|
-
ptr: number,
|
|
68
|
-
len: number,
|
|
69
|
-
): Uint8Array => {
|
|
70
|
-
if (ptr < 0 || len < 0) {
|
|
71
|
-
throw new Error('Program WASM returned a negative pointer/length')
|
|
72
|
-
}
|
|
73
|
-
const buffer = memory.buffer
|
|
74
|
-
const end = ptr + len
|
|
75
|
-
if (end > buffer.byteLength) {
|
|
76
|
-
throw new Error('Program WASM payload pointer is out of bounds')
|
|
77
|
-
}
|
|
78
|
-
return new Uint8Array(buffer, ptr, len)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const defaultProgramImports = {
|
|
82
|
-
env: {
|
|
83
|
-
ms_get_var_num: () => 0,
|
|
84
|
-
ms_set_var_num: () => {},
|
|
85
|
-
ms_set_var_json: () => {},
|
|
86
|
-
ms_mod: (left: number, right: number) => {
|
|
87
|
-
if (!Number.isFinite(left) || !Number.isFinite(right) || right === 0) {
|
|
88
|
-
return 0
|
|
89
|
-
}
|
|
90
|
-
const raw = left % right
|
|
91
|
-
if (raw === 0) {
|
|
92
|
-
return 0
|
|
93
|
-
}
|
|
94
|
-
if ((raw < 0 && right > 0) || (raw > 0 && right < 0)) {
|
|
95
|
-
return raw + right
|
|
96
|
-
}
|
|
97
|
-
return raw
|
|
98
|
-
},
|
|
99
|
-
ms_exec_opcode: () => 0,
|
|
100
|
-
ms_exec_tail: () => 0,
|
|
101
|
-
ms_exec_draw_opcode: () => 0,
|
|
102
|
-
},
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const fnv1aHex = (input: string): string => {
|
|
106
|
-
let hash = 0x811c9dc5
|
|
107
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
108
|
-
hash ^= input.charCodeAt(index)
|
|
109
|
-
hash = Math.imul(hash, 0x01000193)
|
|
110
|
-
}
|
|
111
|
-
return (hash >>> 0).toString(16).padStart(8, '0')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const deriveManifest = (
|
|
115
|
-
abiVersion: number,
|
|
116
|
-
payload: ProgramPayload,
|
|
117
|
-
): ProgramManifest => {
|
|
118
|
-
const projectByteLength = encodeUtf8(payload.projectJson).length
|
|
119
|
-
const assetsByteLength = encodeUtf8(payload.assetsJson).length
|
|
120
|
-
return {
|
|
121
|
-
abiVersion,
|
|
122
|
-
opcodeSetVersion: OPCODE_SET_VERSION,
|
|
123
|
-
projectByteLength,
|
|
124
|
-
assetsByteLength,
|
|
125
|
-
buildFingerprint: fnv1aHex(
|
|
126
|
-
`abi=${String(abiVersion)}|project=${payload.projectJson}|assets=${payload.assetsJson}`,
|
|
127
|
-
),
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export const instantiateProgramModule = (
|
|
132
|
-
wasmBytes: ProgramWasmBytes,
|
|
133
|
-
manifest?: ProgramManifest,
|
|
134
|
-
): ProgramModule => {
|
|
135
|
-
const bytes = toUint8Array(wasmBytes)
|
|
136
|
-
const module = new WebAssembly.Module(toArrayBuffer(bytes))
|
|
137
|
-
const instance = new WebAssembly.Instance(module, defaultProgramImports)
|
|
138
|
-
const exports = instance.exports as Record<string, unknown>
|
|
139
|
-
const memory = exports.memory
|
|
140
|
-
if (!(memory instanceof WebAssembly.Memory)) {
|
|
141
|
-
throw new Error('Program WASM export "memory" is missing')
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const abiVersion = readExportedI32(exports, 'ms_abi_version')
|
|
145
|
-
const projectPtr = readExportedI32(exports, 'ms_project_ptr')
|
|
146
|
-
const projectLen = readExportedI32(exports, 'ms_project_len')
|
|
147
|
-
const assetsPtr = readExportedI32(exports, 'ms_assets_ptr')
|
|
148
|
-
const assetsLen = readExportedI32(exports, 'ms_assets_len')
|
|
149
|
-
const commandsPtr = readExportedI32(exports, 'ms_commands_ptr')
|
|
150
|
-
const commandsLen = readExportedI32(exports, 'ms_commands_len')
|
|
151
|
-
|
|
152
|
-
const projectBase64 = decodeUtf8(
|
|
153
|
-
readMemorySlice(memory, projectPtr, projectLen),
|
|
154
|
-
)
|
|
155
|
-
const assetsBase64 = decodeUtf8(readMemorySlice(memory, assetsPtr, assetsLen))
|
|
156
|
-
const payload: ProgramPayload = {
|
|
157
|
-
projectJson: decodeUtf8(decodeBase64(projectBase64)),
|
|
158
|
-
assetsJson: decodeUtf8(decodeBase64(assetsBase64)),
|
|
159
|
-
}
|
|
160
|
-
if (commandsLen > 0) {
|
|
161
|
-
const commandsBase64 = decodeUtf8(
|
|
162
|
-
readMemorySlice(memory, commandsPtr, commandsLen),
|
|
163
|
-
)
|
|
164
|
-
payload.commandsJson = decodeUtf8(decodeBase64(commandsBase64))
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (manifest && manifest.abiVersion !== abiVersion) {
|
|
168
|
-
throw new Error(
|
|
169
|
-
`Program manifest ABI ${String(manifest.abiVersion)} does not match module ABI ${String(abiVersion)}`,
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const hasWasmExecExport =
|
|
174
|
-
typeof exports.ms_has_exec_runner === 'function'
|
|
175
|
-
? (exports.ms_has_exec_runner as () => number)()
|
|
176
|
-
: 0
|
|
177
|
-
const hasWasmExec =
|
|
178
|
-
Number.isInteger(hasWasmExecExport) && hasWasmExecExport > 0
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
raw: instance,
|
|
182
|
-
abiVersion,
|
|
183
|
-
manifest: manifest ?? deriveManifest(abiVersion, payload),
|
|
184
|
-
readPayload: () => payload,
|
|
185
|
-
hasWasmExec: () => hasWasmExec,
|
|
186
|
-
createWasmExecRunner: (
|
|
187
|
-
host: ProgramWasmExecHost,
|
|
188
|
-
): ProgramWasmExecRunner | null => {
|
|
189
|
-
if (!hasWasmExec) {
|
|
190
|
-
return null
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
let runnerMemory: WebAssembly.Memory | null = null
|
|
194
|
-
const decodedLiteralCache = new Map<number, Map<number, string>>()
|
|
195
|
-
const readCachedLiteral = (ptr: number, len: number): string => {
|
|
196
|
-
const normalizedPtr = ptr | 0
|
|
197
|
-
const normalizedLen = len | 0
|
|
198
|
-
const byLen = decodedLiteralCache.get(normalizedPtr)
|
|
199
|
-
const cached = byLen?.get(normalizedLen)
|
|
200
|
-
if (cached !== undefined) {
|
|
201
|
-
return cached
|
|
202
|
-
}
|
|
203
|
-
if (!(runnerMemory instanceof WebAssembly.Memory)) {
|
|
204
|
-
return ''
|
|
205
|
-
}
|
|
206
|
-
const decoded = decodeUtf8(
|
|
207
|
-
readMemorySlice(runnerMemory, normalizedPtr, normalizedLen),
|
|
208
|
-
)
|
|
209
|
-
if (byLen) {
|
|
210
|
-
byLen.set(normalizedLen, decoded)
|
|
211
|
-
} else {
|
|
212
|
-
decodedLiteralCache.set(
|
|
213
|
-
normalizedPtr,
|
|
214
|
-
new Map([[normalizedLen, decoded]]),
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
return decoded
|
|
218
|
-
}
|
|
219
|
-
const runnerInstance = new WebAssembly.Instance(module, {
|
|
220
|
-
env: {
|
|
221
|
-
ms_get_var_num: (
|
|
222
|
-
targetIndex: number,
|
|
223
|
-
idPtr: number,
|
|
224
|
-
idLen: number,
|
|
225
|
-
) => {
|
|
226
|
-
const variableId = readCachedLiteral(idPtr, idLen)
|
|
227
|
-
const value = host.getVarNumber(targetIndex | 0, variableId)
|
|
228
|
-
return Number.isFinite(value) ? value : 0
|
|
229
|
-
},
|
|
230
|
-
ms_set_var_num: (
|
|
231
|
-
targetIndex: number,
|
|
232
|
-
idPtr: number,
|
|
233
|
-
idLen: number,
|
|
234
|
-
value: number,
|
|
235
|
-
) => {
|
|
236
|
-
const variableId = readCachedLiteral(idPtr, idLen)
|
|
237
|
-
host.setVarNumber(
|
|
238
|
-
targetIndex | 0,
|
|
239
|
-
variableId,
|
|
240
|
-
Number.isFinite(value) ? value : 0,
|
|
241
|
-
)
|
|
242
|
-
},
|
|
243
|
-
ms_set_var_json: (
|
|
244
|
-
targetIndex: number,
|
|
245
|
-
idPtr: number,
|
|
246
|
-
idLen: number,
|
|
247
|
-
valuePtr: number,
|
|
248
|
-
valueLen: number,
|
|
249
|
-
) => {
|
|
250
|
-
const variableId = readCachedLiteral(idPtr, idLen)
|
|
251
|
-
const valueJson = readCachedLiteral(valuePtr, valueLen)
|
|
252
|
-
host.setVarJson(targetIndex | 0, variableId, valueJson)
|
|
253
|
-
},
|
|
254
|
-
ms_mod: (left: number, right: number) => {
|
|
255
|
-
if (
|
|
256
|
-
!Number.isFinite(left) ||
|
|
257
|
-
!Number.isFinite(right) ||
|
|
258
|
-
right === 0
|
|
259
|
-
) {
|
|
260
|
-
return 0
|
|
261
|
-
}
|
|
262
|
-
const raw = left % right
|
|
263
|
-
if (raw === 0) {
|
|
264
|
-
return 0
|
|
265
|
-
}
|
|
266
|
-
if ((raw < 0 && right > 0) || (raw > 0 && right < 0)) {
|
|
267
|
-
return raw + right
|
|
268
|
-
}
|
|
269
|
-
return raw
|
|
270
|
-
},
|
|
271
|
-
ms_exec_opcode: (targetIndex: number, pc: number) => {
|
|
272
|
-
return host.execHostOpcode(targetIndex | 0, pc | 0) | 0
|
|
273
|
-
},
|
|
274
|
-
ms_exec_tail: (targetIndex: number, startPc: number) => {
|
|
275
|
-
return host.execHostTail(targetIndex | 0, startPc | 0) | 0
|
|
276
|
-
},
|
|
277
|
-
ms_exec_draw_opcode: (
|
|
278
|
-
targetIndex: number,
|
|
279
|
-
opcodePtr: number,
|
|
280
|
-
opcodeLen: number,
|
|
281
|
-
arg0: number,
|
|
282
|
-
arg1: number,
|
|
283
|
-
extra: number,
|
|
284
|
-
) => {
|
|
285
|
-
const opcode = readCachedLiteral(opcodePtr, opcodeLen)
|
|
286
|
-
const normalizedArg0 = Number.isFinite(arg0) ? arg0 : 0
|
|
287
|
-
const normalizedArg1 = Number.isFinite(arg1) ? arg1 : 0
|
|
288
|
-
return (
|
|
289
|
-
host.execDrawOpcode(
|
|
290
|
-
targetIndex | 0,
|
|
291
|
-
opcode,
|
|
292
|
-
normalizedArg0,
|
|
293
|
-
normalizedArg1,
|
|
294
|
-
extra | 0,
|
|
295
|
-
) | 0
|
|
296
|
-
)
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
})
|
|
300
|
-
const runnerExports = runnerInstance.exports as Record<string, unknown>
|
|
301
|
-
const runnerMemoryExport = runnerExports.memory
|
|
302
|
-
const runnerExecExport = runnerExports.ms_exec_green_flag
|
|
303
|
-
if (!(runnerMemoryExport instanceof WebAssembly.Memory)) {
|
|
304
|
-
return null
|
|
305
|
-
}
|
|
306
|
-
if (typeof runnerExecExport !== 'function') {
|
|
307
|
-
return null
|
|
308
|
-
}
|
|
309
|
-
runnerMemory = runnerMemoryExport
|
|
310
|
-
|
|
311
|
-
return () => {
|
|
312
|
-
const result = (runnerExecExport as () => number)()
|
|
313
|
-
if (!Number.isInteger(result) || result < 0) {
|
|
314
|
-
throw new Error(
|
|
315
|
-
`Program WASM runner returned invalid op count: ${String(result)}`,
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
return result
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
}
|
|
322
|
-
}
|
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vite-plus/test'
|
|
2
|
-
|
|
3
|
-
import { createHeadlessVM, createProgramModuleFromProject } from './factory.ts'
|
|
4
|
-
import type { ProjectJson } from './types.ts'
|
|
5
|
-
|
|
6
|
-
const NON_WARP_REDRAW_PROJECT: ProjectJson = {
|
|
7
|
-
targets: [
|
|
8
|
-
{
|
|
9
|
-
isStage: true,
|
|
10
|
-
name: 'Stage',
|
|
11
|
-
variables: {},
|
|
12
|
-
lists: {},
|
|
13
|
-
blocks: {},
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
isStage: false,
|
|
17
|
-
name: 'Sprite1',
|
|
18
|
-
variables: {},
|
|
19
|
-
lists: {},
|
|
20
|
-
blocks: {
|
|
21
|
-
hat_flag: {
|
|
22
|
-
opcode: 'event_whenflagclicked',
|
|
23
|
-
next: 'repeat_draw',
|
|
24
|
-
parent: null,
|
|
25
|
-
inputs: {},
|
|
26
|
-
fields: {},
|
|
27
|
-
topLevel: true,
|
|
28
|
-
},
|
|
29
|
-
repeat_draw: {
|
|
30
|
-
opcode: 'control_repeat',
|
|
31
|
-
next: null,
|
|
32
|
-
parent: 'hat_flag',
|
|
33
|
-
inputs: {
|
|
34
|
-
TIMES: [4, 200],
|
|
35
|
-
SUBSTACK: [2, 'pen_clear'],
|
|
36
|
-
},
|
|
37
|
-
fields: {},
|
|
38
|
-
topLevel: false,
|
|
39
|
-
},
|
|
40
|
-
pen_clear: {
|
|
41
|
-
opcode: 'pen_clear',
|
|
42
|
-
next: null,
|
|
43
|
-
parent: 'repeat_draw',
|
|
44
|
-
inputs: {},
|
|
45
|
-
fields: {},
|
|
46
|
-
topLevel: false,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const WARP_REDRAW_PROJECT: ProjectJson = {
|
|
54
|
-
targets: [
|
|
55
|
-
{
|
|
56
|
-
isStage: true,
|
|
57
|
-
name: 'Stage',
|
|
58
|
-
variables: {},
|
|
59
|
-
lists: {},
|
|
60
|
-
blocks: {},
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
isStage: false,
|
|
64
|
-
name: 'Sprite1',
|
|
65
|
-
variables: {},
|
|
66
|
-
lists: {},
|
|
67
|
-
blocks: {
|
|
68
|
-
hat_flag: {
|
|
69
|
-
opcode: 'event_whenflagclicked',
|
|
70
|
-
next: 'call_proc',
|
|
71
|
-
parent: null,
|
|
72
|
-
inputs: {},
|
|
73
|
-
fields: {},
|
|
74
|
-
topLevel: true,
|
|
75
|
-
},
|
|
76
|
-
call_proc: {
|
|
77
|
-
opcode: 'procedures_call',
|
|
78
|
-
next: null,
|
|
79
|
-
parent: 'hat_flag',
|
|
80
|
-
inputs: {},
|
|
81
|
-
fields: {},
|
|
82
|
-
mutation: {
|
|
83
|
-
proccode: 'draw loop',
|
|
84
|
-
argumentids: '[]',
|
|
85
|
-
argumentnames: '[]',
|
|
86
|
-
argumentdefaults: '[]',
|
|
87
|
-
warp: true,
|
|
88
|
-
},
|
|
89
|
-
topLevel: false,
|
|
90
|
-
},
|
|
91
|
-
proc_def: {
|
|
92
|
-
opcode: 'procedures_definition',
|
|
93
|
-
next: 'repeat_proc',
|
|
94
|
-
parent: null,
|
|
95
|
-
inputs: {
|
|
96
|
-
custom_block: [1, 'proc_proto'],
|
|
97
|
-
},
|
|
98
|
-
fields: {},
|
|
99
|
-
topLevel: true,
|
|
100
|
-
},
|
|
101
|
-
proc_proto: {
|
|
102
|
-
opcode: 'procedures_prototype',
|
|
103
|
-
next: null,
|
|
104
|
-
parent: 'proc_def',
|
|
105
|
-
inputs: {},
|
|
106
|
-
fields: {},
|
|
107
|
-
mutation: {
|
|
108
|
-
proccode: 'draw loop',
|
|
109
|
-
argumentids: '[]',
|
|
110
|
-
argumentnames: '[]',
|
|
111
|
-
argumentdefaults: '[]',
|
|
112
|
-
warp: true,
|
|
113
|
-
},
|
|
114
|
-
topLevel: false,
|
|
115
|
-
},
|
|
116
|
-
repeat_proc: {
|
|
117
|
-
opcode: 'control_repeat',
|
|
118
|
-
next: null,
|
|
119
|
-
parent: 'proc_def',
|
|
120
|
-
inputs: {
|
|
121
|
-
TIMES: [4, 6000000],
|
|
122
|
-
SUBSTACK: [2, 'looks_say_warp'],
|
|
123
|
-
},
|
|
124
|
-
fields: {},
|
|
125
|
-
topLevel: false,
|
|
126
|
-
},
|
|
127
|
-
looks_say_warp: {
|
|
128
|
-
opcode: 'looks_say',
|
|
129
|
-
next: null,
|
|
130
|
-
parent: 'repeat_proc',
|
|
131
|
-
inputs: {
|
|
132
|
-
MESSAGE: [1, [10, 'warp']],
|
|
133
|
-
},
|
|
134
|
-
fields: {},
|
|
135
|
-
topLevel: false,
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
],
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const WARP_EXIT_PROJECT: ProjectJson = {
|
|
143
|
-
targets: [
|
|
144
|
-
{
|
|
145
|
-
isStage: true,
|
|
146
|
-
name: 'Stage',
|
|
147
|
-
variables: {
|
|
148
|
-
stage_value: ['stage value', 0],
|
|
149
|
-
},
|
|
150
|
-
lists: {},
|
|
151
|
-
blocks: {},
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
isStage: false,
|
|
155
|
-
name: 'Sprite1',
|
|
156
|
-
variables: {},
|
|
157
|
-
lists: {},
|
|
158
|
-
blocks: {
|
|
159
|
-
hat_flag: {
|
|
160
|
-
opcode: 'event_whenflagclicked',
|
|
161
|
-
next: 'call_proc',
|
|
162
|
-
parent: null,
|
|
163
|
-
inputs: {},
|
|
164
|
-
fields: {},
|
|
165
|
-
topLevel: true,
|
|
166
|
-
},
|
|
167
|
-
call_proc: {
|
|
168
|
-
opcode: 'procedures_call',
|
|
169
|
-
next: null,
|
|
170
|
-
parent: 'hat_flag',
|
|
171
|
-
inputs: {},
|
|
172
|
-
fields: {},
|
|
173
|
-
mutation: {
|
|
174
|
-
proccode: 'set value',
|
|
175
|
-
argumentids: '[]',
|
|
176
|
-
argumentnames: '[]',
|
|
177
|
-
argumentdefaults: '[]',
|
|
178
|
-
warp: true,
|
|
179
|
-
},
|
|
180
|
-
topLevel: false,
|
|
181
|
-
},
|
|
182
|
-
proc_def: {
|
|
183
|
-
opcode: 'procedures_definition',
|
|
184
|
-
next: 'set_value',
|
|
185
|
-
parent: null,
|
|
186
|
-
inputs: {
|
|
187
|
-
custom_block: [1, 'proc_proto'],
|
|
188
|
-
},
|
|
189
|
-
fields: {},
|
|
190
|
-
topLevel: true,
|
|
191
|
-
},
|
|
192
|
-
proc_proto: {
|
|
193
|
-
opcode: 'procedures_prototype',
|
|
194
|
-
next: null,
|
|
195
|
-
parent: 'proc_def',
|
|
196
|
-
inputs: {},
|
|
197
|
-
fields: {},
|
|
198
|
-
mutation: {
|
|
199
|
-
proccode: 'set value',
|
|
200
|
-
argumentids: '[]',
|
|
201
|
-
argumentnames: '[]',
|
|
202
|
-
argumentdefaults: '[]',
|
|
203
|
-
warp: true,
|
|
204
|
-
},
|
|
205
|
-
topLevel: false,
|
|
206
|
-
},
|
|
207
|
-
set_value: {
|
|
208
|
-
opcode: 'data_setvariableto',
|
|
209
|
-
next: null,
|
|
210
|
-
parent: 'proc_def',
|
|
211
|
-
inputs: {
|
|
212
|
-
VALUE: [1, [4, 7]],
|
|
213
|
-
},
|
|
214
|
-
fields: {
|
|
215
|
-
VARIABLE: ['stage value', 'stage_value'],
|
|
216
|
-
},
|
|
217
|
-
topLevel: false,
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const WARP_FOREVER_PROJECT: ProjectJson = {
|
|
225
|
-
targets: [
|
|
226
|
-
{
|
|
227
|
-
isStage: true,
|
|
228
|
-
name: 'Stage',
|
|
229
|
-
variables: {},
|
|
230
|
-
lists: {},
|
|
231
|
-
blocks: {},
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
isStage: false,
|
|
235
|
-
name: 'Sprite1',
|
|
236
|
-
variables: {},
|
|
237
|
-
lists: {},
|
|
238
|
-
blocks: {
|
|
239
|
-
hat_flag: {
|
|
240
|
-
opcode: 'event_whenflagclicked',
|
|
241
|
-
next: 'forever_proc',
|
|
242
|
-
parent: null,
|
|
243
|
-
inputs: {},
|
|
244
|
-
fields: {},
|
|
245
|
-
topLevel: true,
|
|
246
|
-
},
|
|
247
|
-
forever_proc: {
|
|
248
|
-
opcode: 'control_forever',
|
|
249
|
-
next: null,
|
|
250
|
-
parent: 'hat_flag',
|
|
251
|
-
inputs: {
|
|
252
|
-
SUBSTACK: [2, 'call_proc'],
|
|
253
|
-
},
|
|
254
|
-
fields: {},
|
|
255
|
-
topLevel: false,
|
|
256
|
-
},
|
|
257
|
-
call_proc: {
|
|
258
|
-
opcode: 'procedures_call',
|
|
259
|
-
next: null,
|
|
260
|
-
parent: 'forever_proc',
|
|
261
|
-
inputs: {},
|
|
262
|
-
fields: {},
|
|
263
|
-
mutation: {
|
|
264
|
-
proccode: 'tick',
|
|
265
|
-
argumentids: '[]',
|
|
266
|
-
argumentnames: '[]',
|
|
267
|
-
argumentdefaults: '[]',
|
|
268
|
-
warp: true,
|
|
269
|
-
},
|
|
270
|
-
topLevel: false,
|
|
271
|
-
},
|
|
272
|
-
proc_def: {
|
|
273
|
-
opcode: 'procedures_definition',
|
|
274
|
-
next: 'say_tick',
|
|
275
|
-
parent: null,
|
|
276
|
-
inputs: {
|
|
277
|
-
custom_block: [1, 'proc_proto'],
|
|
278
|
-
},
|
|
279
|
-
fields: {},
|
|
280
|
-
topLevel: true,
|
|
281
|
-
},
|
|
282
|
-
proc_proto: {
|
|
283
|
-
opcode: 'procedures_prototype',
|
|
284
|
-
next: null,
|
|
285
|
-
parent: 'proc_def',
|
|
286
|
-
inputs: {},
|
|
287
|
-
fields: {},
|
|
288
|
-
mutation: {
|
|
289
|
-
proccode: 'tick',
|
|
290
|
-
argumentids: '[]',
|
|
291
|
-
argumentnames: '[]',
|
|
292
|
-
argumentdefaults: '[]',
|
|
293
|
-
warp: true,
|
|
294
|
-
},
|
|
295
|
-
topLevel: false,
|
|
296
|
-
},
|
|
297
|
-
say_tick: {
|
|
298
|
-
opcode: 'looks_say',
|
|
299
|
-
next: null,
|
|
300
|
-
parent: 'proc_def',
|
|
301
|
-
inputs: {
|
|
302
|
-
MESSAGE: [1, [10, 'tick']],
|
|
303
|
-
},
|
|
304
|
-
fields: {},
|
|
305
|
-
topLevel: false,
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
],
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
describe('moonscratch/js/vm scheduler render contracts', () => {
|
|
313
|
-
const nonWarpProgram = createProgramModuleFromProject({
|
|
314
|
-
projectJson: NON_WARP_REDRAW_PROJECT,
|
|
315
|
-
})
|
|
316
|
-
const warpProgram = createProgramModuleFromProject({
|
|
317
|
-
projectJson: WARP_REDRAW_PROJECT,
|
|
318
|
-
})
|
|
319
|
-
const warpExitProgram = createProgramModuleFromProject({
|
|
320
|
-
projectJson: WARP_EXIT_PROJECT,
|
|
321
|
-
})
|
|
322
|
-
const warpForeverProgram = createProgramModuleFromProject({
|
|
323
|
-
projectJson: WARP_FOREVER_PROJECT,
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
test('non-warp redraw stops frame with rerender reason and asks renderer update', () => {
|
|
327
|
-
const vm = createHeadlessVM({
|
|
328
|
-
program: nonWarpProgram,
|
|
329
|
-
initialNowMs: 0,
|
|
330
|
-
})
|
|
331
|
-
vm.greenFlag()
|
|
332
|
-
vm.setTime(16)
|
|
333
|
-
|
|
334
|
-
const frame = vm.stepFrame()
|
|
335
|
-
|
|
336
|
-
expect(
|
|
337
|
-
frame.stopReason === 'rerender' || frame.stopReason === 'finished',
|
|
338
|
-
).toBe(true)
|
|
339
|
-
expect(frame.shouldRender).toBe(true)
|
|
340
|
-
if (frame.stopReason === 'rerender') {
|
|
341
|
-
expect(frame.activeThreads).toBeGreaterThan(0)
|
|
342
|
-
} else {
|
|
343
|
-
expect(frame.activeThreads).toBe(0)
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
test('warp redraw does not request render while warp is still active', () => {
|
|
348
|
-
const vm = createHeadlessVM({
|
|
349
|
-
program: warpProgram,
|
|
350
|
-
initialNowMs: 0,
|
|
351
|
-
options: {
|
|
352
|
-
stepTimeoutTicks: 1,
|
|
353
|
-
},
|
|
354
|
-
})
|
|
355
|
-
vm.greenFlag()
|
|
356
|
-
vm.setTime(16)
|
|
357
|
-
|
|
358
|
-
const frame = vm.stepFrame()
|
|
359
|
-
|
|
360
|
-
expect(frame.stopReason).toBe('timeout')
|
|
361
|
-
expect(frame.shouldRender).toBe(false)
|
|
362
|
-
expect(frame.activeThreads).toBeGreaterThan(0)
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
test('returns warp-exit before finished when warp context ends in same frame', () => {
|
|
366
|
-
const vm = createHeadlessVM({
|
|
367
|
-
program: warpExitProgram,
|
|
368
|
-
initialNowMs: 0,
|
|
369
|
-
options: {
|
|
370
|
-
stepTimeoutTicks: 10000,
|
|
371
|
-
turbo: true,
|
|
372
|
-
},
|
|
373
|
-
})
|
|
374
|
-
vm.greenFlag()
|
|
375
|
-
|
|
376
|
-
const first = vm.stepFrame()
|
|
377
|
-
const second = vm.stepFrame()
|
|
378
|
-
|
|
379
|
-
expect(first.stopReason).toBe('warp-exit')
|
|
380
|
-
expect(first.shouldRender).toBe(true)
|
|
381
|
-
expect(first.activeThreads).toBe(0)
|
|
382
|
-
expect(second.stopReason).toBe('finished')
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
test('emits warp-exit when forever script repeatedly re-enters warp procedure', () => {
|
|
386
|
-
const vm = createHeadlessVM({
|
|
387
|
-
program: warpForeverProgram,
|
|
388
|
-
initialNowMs: 0,
|
|
389
|
-
options: {
|
|
390
|
-
stepTimeoutTicks: 100,
|
|
391
|
-
},
|
|
392
|
-
})
|
|
393
|
-
vm.greenFlag()
|
|
394
|
-
|
|
395
|
-
const frame = vm.stepFrame()
|
|
396
|
-
|
|
397
|
-
expect(frame.stopReason).toBe('warp-exit')
|
|
398
|
-
expect(frame.shouldRender).toBe(true)
|
|
399
|
-
expect(frame.activeThreads).toBeGreaterThan(0)
|
|
400
|
-
})
|
|
401
|
-
})
|