@px-petals/roku.motion-web 1.0.0 → 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.
Files changed (77) hide show
  1. package/bands/bands.js +3 -1
  2. package/bundle/bundle.js +56 -0
  3. package/list/list.js +12 -1
  4. package/lottie/lottie.layers.js +64 -11
  5. package/lottie/lottie.paint.js +30 -0
  6. package/lottie/lottie.paths.js +19 -4
  7. package/lottie/lottie.props.js +18 -8
  8. package/lottie/lottie.shapes.js +2 -1
  9. package/mixer/mixer.js +31 -8
  10. package/mixer/mixer.mix.js +17 -0
  11. package/motion/motion.convert.js +32 -1
  12. package/motion/motion.flow.js +20 -2
  13. package/motion/motion.machine.js +28 -13
  14. package/motion/motion.values.js +13 -9
  15. package/motion.js +196 -20
  16. package/package.json +10 -16
  17. package/player/player.js +25 -5
  18. package/readme.md +78 -3
  19. package/script/luau/lib/base/base.js +202 -0
  20. package/script/luau/lib/base/base.table.js +189 -0
  21. package/script/luau/lib/lib.args.js +152 -0
  22. package/script/luau/lib/lib.js +23 -0
  23. package/script/luau/lib/math/math.bit32.js +144 -0
  24. package/script/luau/lib/math/math.js +288 -0
  25. package/script/luau/lib/math/math.vector.js +125 -0
  26. package/script/luau/lib/string/string.format.js +143 -0
  27. package/script/luau/lib/string/string.js +160 -0
  28. package/script/luau/lib/string/string.pattern.js +505 -0
  29. package/script/luau/load/load.js +362 -0
  30. package/script/luau/load/load.ops.js +79 -0
  31. package/script/luau/luau.convert.js +259 -0
  32. package/script/luau/luau.js +132 -0
  33. package/script/luau/luau.md +111 -0
  34. package/script/luau/vm/vm.js +167 -0
  35. package/script/luau/vm/vm.meta.js +401 -0
  36. package/script/luau/vm/vm.num.js +236 -0
  37. package/script/luau/vm/vm.run.js +842 -0
  38. package/script/luau/vm/vm.value.js +406 -0
  39. package/script/play/play.js +199 -0
  40. package/script/play/play.objects.js +295 -0
  41. package/script/play/play.scope.js +91 -0
  42. package/script/rive/api/api.statics.js +31 -0
  43. package/script/rive/api/api.type.js +188 -0
  44. package/script/rive/api/artboard/artboard.js +89 -0
  45. package/script/rive/api/assets/assets.js +72 -0
  46. package/script/rive/api/data/data.value.js +99 -0
  47. package/script/rive/api/draw/draw.contours.js +163 -0
  48. package/script/rive/api/draw/draw.measure.js +196 -0
  49. package/script/rive/api/draw/draw.paint.js +235 -0
  50. package/script/rive/api/draw/draw.path.js +153 -0
  51. package/script/rive/api/draw/draw.renderer.js +117 -0
  52. package/script/rive/api/math/math.color.js +96 -0
  53. package/script/rive/api/math/math.mat2d.js +180 -0
  54. package/script/rive/api/math/math.vector.js +121 -0
  55. package/script/rive/api/media/media.audio.js +113 -0
  56. package/script/rive/api/media/media.draw.js +73 -0
  57. package/script/rive/api/media/media.image.js +61 -0
  58. package/script/rive/api/media/media.mesh.js +50 -0
  59. package/script/rive/api/pointer/pointer.event.js +64 -0
  60. package/script/rive/api/pointer/pointer.route.js +69 -0
  61. package/script/rive/api/text/text.font.js +129 -0
  62. package/script/rive/api/text/text.js +278 -0
  63. package/script/rive/api/text/text.layout.js +259 -0
  64. package/script/rive/context/context.js +76 -0
  65. package/script/rive/context/context.memory.js +196 -0
  66. package/script/rive/context/context.viewmodel.js +302 -0
  67. package/script/rive/context/lists/lists.js +160 -0
  68. package/script/rive/context/lists/lists.values.js +281 -0
  69. package/script/rive/host/host.globals.js +44 -0
  70. package/script/rive/host/host.js +533 -0
  71. package/script/rive/host/host.node.js +45 -0
  72. package/script/rive/rive.js +23 -0
  73. package/script/rive/rive.md +180 -0
  74. package/script/script.js +11 -0
  75. package/skin/skin.deform.js +514 -0
  76. package/skin/skin.ik.js +260 -0
  77. package/skin/skin.js +297 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * A Luau bytecode loader and interpreter: load compiled bytecode, run its
3
+ * main chunk against globals the host provides, and call Lua functions from
4
+ * JS and JS functions from Lua. See `luau.md` for the design.
5
+ *
6
+ * ```js
7
+ * const luau = createLuau({ print: (text) => console.log(text) })
8
+ * luau.set('twice', (n) => n * 2)
9
+ * const module = luau.load(bytes) // bytes start at the version byte
10
+ * const results = luau.run(module) // the main chunk's returns, as host values
11
+ * luau.call(luau.get('update'), 16) // a global Lua function
12
+ * ```
13
+ *
14
+ * Rive writes one wrapper byte before the bytecode of a script; strip it
15
+ * before `load`.
16
+ */
17
+
18
+ import { loadBytecode } from './load/load.js'
19
+ import { installLibs } from './lib/lib.js'
20
+ import { LuauError, luauError, multi, toJs, toLua, encodeUtf8, decodeUtf8 } from './luau.convert.js'
21
+ import { VM } from './vm/vm.js'
22
+ import { LuaClosure, LuaTable, LuaThrow, NativeFunction, Vector } from './vm/vm.value.js'
23
+
24
+ export { loadBytecode, LuauError, LuaClosure, LuaTable, NativeFunction, Vector, VM, multi, toJs, toLua, encodeUtf8, decodeUtf8 }
25
+
26
+ /** @typedef {import('./load/load.js').Module} Module */
27
+
28
+ /**
29
+ * @typedef {object} LuauOptions
30
+ * @property {(text: string) => void} [print] where `print` writes, a line at a time; default discards
31
+ * @property {boolean} [libs] install the standard library subset; default true
32
+ * @property {string} [chunk] the chunk name errors carry; default the module's own or `?`
33
+ * @property {Record<string, any>} [globals] host values to set as globals
34
+ */
35
+
36
+ /** A VM with its libraries and the host-facing calls */
37
+ export class Luau {
38
+ /** @param {LuauOptions} [options] */
39
+ constructor(options) {
40
+ const o = options || {}
41
+ this.options = o
42
+ this.vm = new VM({ print: o.print, chunk: o.chunk })
43
+ if (o.libs !== false) installLibs(this.vm)
44
+ if (o.globals) for (const name in o.globals) this.set(name, o.globals[name])
45
+ }
46
+
47
+ /**
48
+ * @param {Uint8Array} bytes bytecode from its version byte on
49
+ * @param {string} [chunk] the chunk name for errors
50
+ * @returns {Module}
51
+ */
52
+ load(bytes, chunk) {
53
+ return loadBytecode(bytes, chunk || this.options.chunk)
54
+ }
55
+
56
+ /**
57
+ * Sets a global from a host value.
58
+ *
59
+ * @param {string} name
60
+ * @param {any} value
61
+ */
62
+ set(name, value) {
63
+ this.vm.globals.set(encodeUtf8(name), toLua(this.vm, value))
64
+ }
65
+
66
+ /**
67
+ * @param {string} name
68
+ * @returns {any} the global as a host value
69
+ */
70
+ get(name) {
71
+ return toJs(this.vm, this.vm.globals.get(encodeUtf8(name)))
72
+ }
73
+
74
+ /**
75
+ * @param {string} name
76
+ * @returns {any} the global as the Lua value it is
77
+ */
78
+ getRaw(name) {
79
+ return this.vm.globals.get(encodeUtf8(name))
80
+ }
81
+
82
+ /**
83
+ * Runs a module's main chunk.
84
+ *
85
+ * @param {Module} module
86
+ * @param {any[]} [args] host values passed to the chunk as `...`
87
+ * @returns {any[]} what the chunk returned, as host values
88
+ */
89
+ run(module, args) {
90
+ const results = this.callRaw(this.vm.instantiate(module), (args || []).map((a) => toLua(this.vm, a)))
91
+ return results.map((r) => toJs(this.vm, r))
92
+ }
93
+
94
+ /**
95
+ * Calls a Lua value with Lua values. Throws `LuauError` for a Lua error.
96
+ *
97
+ * @param {any} fn
98
+ * @param {any[]} args
99
+ * @returns {any[]} the results, as Lua values
100
+ */
101
+ callRaw(fn, args) {
102
+ try {
103
+ return this.vm.call(fn, args)
104
+ } catch (e) {
105
+ throw e instanceof LuaThrow ? luauError(this.vm, e) : e
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Calls a Lua function with host values.
111
+ *
112
+ * @param {any} fn a Lua function (`getRaw`), or a host function made from one (`get`)
113
+ * @param {...any} args
114
+ * @returns {any} nothing, the one result, or an array of results, as host values
115
+ */
116
+ call(fn, ...args) {
117
+ if (typeof fn === 'function') return fn.apply(null, args)
118
+ const f = fn
119
+ const results = this.callRaw(f, args.map((a) => toLua(this.vm, a)))
120
+ if (results.length === 0) return undefined
121
+ if (results.length === 1) return toJs(this.vm, results[0])
122
+ return results.map((r) => toJs(this.vm, r))
123
+ }
124
+ }
125
+
126
+ /**
127
+ * @param {LuauOptions} [options]
128
+ * @returns {Luau}
129
+ */
130
+ export function createLuau(options) {
131
+ return new Luau(options)
132
+ }
@@ -0,0 +1,111 @@
1
+ # luau
2
+
3
+ A Luau bytecode loader and interpreter in plain JS, for the scripts a Rive file carries as compiled Luau. It knows nothing of Rive: a host adds its own globals. The design is table-driven and keeps its state in plain arrays and objects (a register stack, a frame stack, a dispatch on small integers), so the BrightScript port can follow it file by file.
4
+
5
+ ```js
6
+ import { createLuau } from '@px-petals/roku.motion-web/script/luau/luau.js'
7
+
8
+ const luau = createLuau({ print: (text) => console.log(text) })
9
+ luau.set('twice', (n) => n * 2) // a host function, or any host value
10
+ const module = luau.load(bytes) // bytes start at the version byte
11
+ const returned = luau.run(module) // the main chunk's results, as host values
12
+ const update = luau.getRaw('update') // a Lua function, as the Lua value it is
13
+ luau.call(update, 16) // call it with host values
14
+ ```
15
+
16
+ Rive writes one wrapper byte before the bytecode of a script. The caller strips it: `load` takes a `Uint8Array` that begins at the Luau version byte.
17
+
18
+ | Call | |
19
+ | --- | --- |
20
+ | `createLuau({ print, libs, chunk, globals })` | a VM with the standard library subset installed (`libs: false` for none) |
21
+ | `luau.load(bytes, chunk?)` | decodes bytecode into a `Module`; throws on a version it cannot read, a truncated file or an unknown opcode |
22
+ | `luau.run(module, args?)` | runs the main chunk in this VM's globals; `args` arrive as `...` |
23
+ | `luau.set(name, value)` / `get(name)` / `getRaw(name)` | globals, converted to and from host values, or raw |
24
+ | `luau.call(fn, ...args)` | calls a Lua function (or a host function made from one) and converts |
25
+ | `luau.callRaw(fn, args)` | the same with Lua values in and an array of Lua values out |
26
+ | `luau.vm` | the `VM`: `call`, `globals`, `native(name, fn)`, `stringMeta` |
27
+
28
+ An error in Lua code throws `LuauError { message, value, line, chunk }`; `line` is 0 when the bytecode was stripped of debug info (Rive's is), and the message then has no position. A host function that throws becomes a Lua error that `pcall` can catch.
29
+
30
+ `luau.convert.js` holds the conversions: strings cross as UTF-8 (Lua strings are byte strings), arrays and plain objects become tables and back, host functions become Lua functions and back, `multi(a, b)` returns several results, and Lua values (tables, functions, vectors) pass through unchanged. For live access to a Lua table, use the raw API: `LuaTable` has `get`, `set`, `getStr`, `length` and `advance` (traversal).
31
+
32
+ ## Files
33
+
34
+ | File | Holds |
35
+ | --- | --- |
36
+ | `load/load.js` | the bytecode reader: strings, protos, constants, line and debug info |
37
+ | `load/load.ops.js` | opcode names in Bytecode.h order, instruction lengths, version bounds |
38
+ | `vm/vm.js` | `VM`: the value stack, frame stack, open upvalues, `call` |
39
+ | `vm/vm.run.js` | `Frame`, `enter`, and `execute`, the instruction loop |
40
+ | `vm/vm.meta.js` | `index`, `setIndex`, `arith`, `equals`, `lessThan`, `concat`, `length`, `tostring`: Lua's operations with their metamethods |
41
+ | `vm/vm.value.js` | value types and `LuaTable` |
42
+ | `vm/vm.num.js` | number formatting (`%g`, `%e`, `%f`) and reading |
43
+ | `lib/` | the standard library subset, one folder per area |
44
+ | `.tests/asm/` | a bytecode assembler for tests (Luau's `BytecodeBuilder` encoding in JS) |
45
+ | `.tests/reference/` | the official Luau toolchain as a test oracle (see below) |
46
+ | `.tests/sources/`, `.tests/fixtures/` | Luau programs, and their committed `-O1` bytecode and expected output |
47
+
48
+ ## Values
49
+
50
+ | Lua | JS | Notes |
51
+ | --- | --- | --- |
52
+ | nil | `undefined` | |
53
+ | boolean | `true`, `false` | |
54
+ | number | a double | integers are doubles too, as in Luau |
55
+ | string | a JS string holding bytes: one char per byte, 0 to 255 | `("é"):len()` is 2; the host converts at its edge |
56
+ | vector | `Vector { x, y, z }` | immutable; equal and keyed by components |
57
+ | table | `LuaTable` | see below |
58
+ | function | `LuaClosure { proto, upvals, env }` or `NativeFunction { name, fn }` | |
59
+ | upvalue | `Upvalue { s, i, v }` | `s` is the stack while the variable is live (`stack[i]`), `null` once closed (`v`) |
60
+
61
+ A JS array is never a Lua value, so a native function may return one to mean several results; any other value is a single result, and `EMPTY` (an empty array) is none.
62
+
63
+ **Tables** keep keys 1..n in an array `arr` (no trailing nils, so `arr.length` is a border and `#t`). Every other key lives in one of three dictionaries (strings, numbers, other values by identity id) that map a key to a *slot* in the parallel arrays `hk` and `hv`. Slots stay in insertion order, which is traversal order; a removed key keeps its slot with a nil value, so assigning nil during `pairs` is safe, and the slots are rebuilt when half are dead. A traversal position is an array index (0 and up) or `-1 - slot`. A BrightScript port can use an `roArray` and an `roAssociativeArray` of slot numbers the same way.
64
+
65
+ ## Bytecode
66
+
67
+ A module is a string table, then protos. A proto is `maxStack`, `numParams`, `numUpvals`, `isVararg`, `flags`, type info (kept raw, not used), the instruction words (`Int32Array`), constants, child protos, `lineDefined`, debug name, line info and local and upvalue names. Versions 3 to 14 load, with type-info versions 1 to 3. Version 12 on prefixes each proto with its size and keeps feedback slots and an inline cost, which the loader reads past; the compiler of release 0.744 emits version 14, whose compiled calls are `CALLFB` (a `CALL` and a feedback word) and whose `pcall` sites carry a `FASTPCALL` hint, both run as the ordinary call.
68
+
69
+ Constants load as Lua values (nil, booleans, numbers, byte strings, `Vector`) except three descriptors: `Import` (a packed path of constant names), `TableTemplate` (the keys, and for version 7 the values, of a DUPTABLE) and `ClosureRef` (the proto of a DUPCLOSURE).
70
+
71
+ Line info is stored the way Luau stores it (a baseline per 2^gap words and a byte delta each) and expanded at load to one source line per instruction word.
72
+
73
+ ## Execution
74
+
75
+ An instruction is a 32-bit word. The opcode is the low byte; `A`, `B` and `C` are the next three bytes, or `A` and a signed 16-bit `D`, or a signed 24-bit `E`. Some opcodes are followed by an AUX word (`load.ops.js` lists them). A jump `D` counts from the word after the instruction, or from its AUX word when it has one (so `1` skips the AUX).
76
+
77
+ The VM has one value stack, a plain array shared by every call. A frame is `{ cl, base, pc, ret, nres, varargs, entry }`: register `n` is `stack[base + n]`; `ret` is the slot the callee's results go to, which is the slot its function was called from; `nres` is how many results the caller wants, or -1 for all.
78
+
79
+ `execute` loops over the top frame: `switch (insn & 255)` over the opcode numbers, written as literal numbers with the name in a comment (a test keeps them equal to Bytecode.h's). A call from Lua to a Lua closure pushes a frame and swaps the loop's locals, so Lua recursion does not use the JS stack and is bounded by the frame limit (20000, "stack overflow"). A call to a native function, and metamethod, `pcall`, `sort` and generic-for calls, run through `VM.call`, which starts a nested `execute` on the same stacks; those nest up to 200 deep ("C stack overflow").
80
+
81
+ - **Arguments and results.** The caller puts the function at `ra` and arguments after it; the callee's base is `ra + 1`, so arguments are already its first registers. Missing parameters are set to nil. A vararg function copies its extra arguments into `frame.varargs`; GETVARARGS reads them.
82
+ - **Multiple values.** CALL, RETURN, SETLIST and GETVARARGS with count 0 mean "up to `top`". `top` is a local of the loop, set by the instruction that produced the values and read by the next.
83
+ - **Upvalues.** NEWCLOSURE reads the CAPTURE words that follow it. A by-reference capture finds or makes an open `Upvalue` for the stack slot (`findUpvalue`, kept sorted); CLOSEUPVALS and RETURN close the ones at or above a base by copying the slot into `v`. A by-value capture makes a closed upvalue at once.
84
+ - **Calls through the fast path.** FASTCALL, FASTCALL1, 2, 2K and 3 are skipped: Luau always emits the ordinary call sequence after them, so doing nothing is correct.
85
+ - **Generic for.** FORGPREP recognises a table (and `pairs`/`ipairs` via FORGPREP_NEXT and FORGPREP_INEXT, identified by function identity) and turns the registers into a built-in traversal: `ra = nil`, `ra + 1 = table`, `ra + 2 = position`. FORGLOOP advances it without a call. Anything else is called each turn. A `__iter` metamethod is honoured.
86
+ - **Errors.** `LuaThrow` carries the error value and a `level`: how many Lua frames up its position comes from (1 for the code that called `error`). `execute` catches an error on its way out, adds `chunk:line:` from the frame's saved `pc`, and rethrows. `VM.call` unwinds the frames and closes the upvalues of a call that threw, so `pcall` is a try/catch around `VM.call`.
87
+ - **Metamethods.** The interpreter does number and plain-table work inline and calls `vm.meta.js` for the rest: `__index`, `__newindex`, `__call`, `__add` and the other arithmetic ones, `__eq`, `__lt`, `__le`, `__len`, `__concat`, `__unm`, `__tostring`, `__iter`, `__metatable`. Strings share one metatable whose `__index` is the `string` table, which is how NAMECALL on a string finds `upper`.
88
+
89
+ ## Library
90
+
91
+ base (`assert`, `error`, `pcall`, `xpcall`, `type`, `typeof`, `tostring`, `tonumber`, `ipairs`, `pairs`, `next`, `select`, `rawget`, `rawset`, `rawequal`, `rawlen`, `setmetatable`, `getmetatable`, `unpack`, `print`, `collectgarbage` (a stub)), `math` (all, with `noise` and `random` from a small generator that is not Luau's), `table` (`insert`, `remove`, `concat`, `unpack`, `pack`, `sort`, `find`, `clear`, `create`, `clone`, `freeze`, `isfrozen`, `move`, `getn`), `string` (`byte`, `char`, `find`, `match`, `gmatch`, `gsub` on Lua 5.1 patterns, `format`, `len`, `lower`, `upper`, `rep`, `reverse`, `sub`, `split`), `bit32`, `vector`, and `os.clock` and `os.time`. `print` goes to the host hook.
92
+
93
+ Not carried: coroutines, `buffer`, `utf8`, `string.pack` and `unpack`, `os.date`, `getfenv` and `setfenv`, 64-bit integer and class constants of bytecode versions 8 and up (the loader refuses a module that holds one), and Luau's native codegen opcodes. `string.format` has no `%a`.
94
+
95
+ ## Checked against the official Luau
96
+
97
+ The tests hold the VM to the real compiler and interpreter. `.tests/sources/*.luau` are programs (arithmetic, strings and patterns, `string.format`, tables, metatables, closures, every loop, varargs, `pcall` and `error`, methods, vectors, integer-like doubles, `table.sort`, `bit32`, deep recursion). The reference test compiles each with the official `luau-compile --binary` at -O0, -O1 and -O2, loads the bytes here and requires the loader to consume every one, runs them, and runs the same source on the official `luau`; what both print must match byte for byte, error messages included.
98
+
99
+ - **The reference is a pinned release** of [luau-lang/luau](https://github.com/luau-lang/luau/releases): one version, with each platform's asset URL and SHA-256 in `.tests/reference/reference.json`. It is test-only and never ships (`.tests` is outside the package's `files`).
100
+ - **Fetch it** with `npm run luau:fetch` in `src/runtime/web`. It downloads the asset for this platform, checks the hash, unpacks it with the OS's `tar` (bsdtar reads zip) into `.artifacts/luau/<version>/` (git-ignored) and marks the binaries executable. Nothing else is fetched.
101
+ - **Without the binaries** the reference tests skip, naming that script. CI that has not fetched them runs only the committed fixtures: `.tests/fixtures/<name>.O1.luaubc` is each program's -O1 bytecode and `<name>.out` what the reference printed, so the loader and interpreter are still checked against real compiler output.
102
+ - **Regenerate the fixtures** after changing a source or the pinned version: `npm run luau:fetch`, then `npm run luau:fixtures`, and commit the files. With the binaries present a test fails when a committed `.luaubc` is not what the pinned compiler emits.
103
+ - **Opt-in only.** No install or prepare hook runs the fetch, nothing under `.tests` ships, and the VM does not load it.
104
+ - **To move the pin**, change `reference.json` (version, URLs, hashes from the release page), fetch, regenerate, and run the tests.
105
+
106
+ ## Porting notes
107
+
108
+ - Everything the interpreter needs is a number, a string, a boolean, an array or an object with fixed fields; there is no reliance on closures for Lua semantics (`pcall` is the one place a JS exception stands in for a longjmp, and a BrightScript port can use `try`/`catch` the same way).
109
+ - Replace the `switch` with a function table or an `if` ladder ordered by frequency; the cases are short and independent.
110
+ - The register file is one array and a frame is a record of indices, so a port can keep the frame stack in parallel arrays.
111
+ - `LuaTable.advance` returns a position and leaves the entry in two fields to avoid allocating per step; a port with cheap tuples can return the pair.
@@ -0,0 +1,167 @@
1
+ /**
2
+ * The VM state: one value stack shared by every call, a stack of call
3
+ * frames, the open upvalues, and the globals. `call` runs a Lua or native
4
+ * function from the host (or from a native function) and returns its results
5
+ * as an array; the interpreter loop is in `vm.run.js`.
6
+ */
7
+
8
+ /** @import { Frame } from './vm.run.js' */
9
+
10
+ import { enter, execute } from './vm.run.js'
11
+ import { metamethod, tostring } from './vm.meta.js'
12
+ import { LuaClosure, LuaTable, NativeFunction, Upvalue, fail, newId, typeName } from './vm.value.js'
13
+
14
+ /** Nested native-to-Lua calls allowed before "C stack overflow" */
15
+ const MAX_NATIVE_DEPTH = 200
16
+
17
+ export class VM {
18
+ /** @param {{ print?: (text: string) => void, chunk?: string }} [options] */
19
+ constructor(options) {
20
+ const o = options || {}
21
+ /** The value stack: every frame's registers, one after another */
22
+ this.stack = /** @type {any[]} */ ([])
23
+ for (let i = 0; i < 1024; i++) this.stack.push(undefined)
24
+ /** First free stack index above the running frame's registers */
25
+ this.sp = 0
26
+ /** @type {Frame[]} */
27
+ this.frames = []
28
+ /** @type {Upvalue[]} open upvalues, by ascending stack index */
29
+ this.openUpvals = []
30
+ this.nativeDepth = 0
31
+ this.globals = new LuaTable()
32
+ /** @type {LuaTable | undefined} */
33
+ this.stringMeta = undefined
34
+ /** @type {LuaTable | undefined} */
35
+ this.vectorMeta = undefined
36
+ /** `ipairs` and `pairs` iterate with these; FORGPREP recognises them */
37
+ this.inext = /** @type {NativeFunction | null} */ (null)
38
+ this.next = /** @type {NativeFunction | null} */ (null)
39
+ /** Where `print` writes */
40
+ this.print = o.print || function () {}
41
+ this.chunk = o.chunk || '?'
42
+ }
43
+
44
+ /** @returns {number} */
45
+ newId() {
46
+ return newId()
47
+ }
48
+
49
+ /**
50
+ * Makes the main closure of a loaded module, running in this VM's globals.
51
+ *
52
+ * @param {import('../load/load.js').Module} module
53
+ * @returns {LuaClosure}
54
+ */
55
+ instantiate(module) {
56
+ return new LuaClosure(module.main, [], this.globals)
57
+ }
58
+
59
+ /**
60
+ * Calls any callable value.
61
+ *
62
+ * @param {any} fn
63
+ * @param {any[]} args
64
+ * @returns {any[]} the results
65
+ */
66
+ call(fn, args) {
67
+ if (fn instanceof NativeFunction) {
68
+ if (this.nativeDepth >= MAX_NATIVE_DEPTH) throw fail('C stack overflow')
69
+ this.nativeDepth++
70
+ try {
71
+ const res = fn.fn(this, args)
72
+ return Array.isArray(res) ? res : [ res ]
73
+ } finally {
74
+ this.nativeDepth--
75
+ }
76
+ }
77
+ if (!(fn instanceof LuaClosure)) {
78
+ const handler = metamethod(this, fn, '__call')
79
+ if (handler === undefined) throw fail('attempt to call a ' + typeName(fn) + ' value')
80
+ return this.call(handler, [ fn ].concat(args))
81
+ }
82
+ if (this.nativeDepth >= MAX_NATIVE_DEPTH) throw fail('C stack overflow')
83
+ const stack = this.stack
84
+ const ra = this.sp
85
+ const savedSp = this.sp
86
+ const depth = this.frames.length
87
+ while (stack.length < ra + args.length + 2) stack.push(undefined)
88
+ stack[ra] = fn
89
+ for (let i = 0; i < args.length; i++) stack[ra + 1 + i] = args[i]
90
+ this.nativeDepth++
91
+ try {
92
+ const frame = enter(this, fn, ra, args.length, -1)
93
+ frame.entry = true
94
+ return execute(this)
95
+ } catch (e) {
96
+ this.unwind(depth, ra)
97
+ throw e
98
+ } finally {
99
+ this.nativeDepth--
100
+ this.sp = savedSp
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Drops the frames above `depth` and closes the upvalues from `from` up,
106
+ * after an error.
107
+ *
108
+ * @param {number} depth
109
+ * @param {number} from
110
+ */
111
+ unwind(depth, from) {
112
+ this.frames.length = depth
113
+ this.closeUpvals(from)
114
+ }
115
+
116
+ /**
117
+ * The upvalue that captures stack slot `index`, shared by every closure that captures it.
118
+ *
119
+ * @param {number} index
120
+ * @returns {Upvalue}
121
+ */
122
+ findUpvalue(index) {
123
+ const list = this.openUpvals
124
+ let i = list.length
125
+ while (i > 0 && list[i - 1].i >= index) {
126
+ if (list[i - 1].i === index) return list[i - 1]
127
+ i--
128
+ }
129
+ const uv = new Upvalue(this.stack, index)
130
+ list.splice(i, 0, uv)
131
+ return uv
132
+ }
133
+
134
+ /**
135
+ * Closes the open upvalues at stack slot `from` and above: they keep their value from here on.
136
+ *
137
+ * @param {number} from
138
+ */
139
+ closeUpvals(from) {
140
+ const list = this.openUpvals
141
+ while (list.length > 0 && list[list.length - 1].i >= from) {
142
+ const uv = /** @type {Upvalue} */ (list.pop())
143
+ uv.v = this.stack[uv.i]
144
+ uv.s = null
145
+ }
146
+ }
147
+
148
+ // Convenience
149
+ // ■■■■■■■■■■■
150
+
151
+ /**
152
+ * @param {any} v
153
+ * @returns {string} Lua's `tostring` of v
154
+ */
155
+ tostring(v) {
156
+ return tostring(this, v)
157
+ }
158
+
159
+ /**
160
+ * @param {string} name
161
+ * @param {(vm: VM, args: any[]) => any} fn
162
+ * @returns {NativeFunction}
163
+ */
164
+ native(name, fn) {
165
+ return new NativeFunction(name, fn)
166
+ }
167
+ }