@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,362 @@
1
+ /**
2
+ * The Luau bytecode loader: bytes in, a module of protos out. It follows
3
+ * `lvmload.cpp` for the layout and reads versions 3 to 14 with type-info
4
+ * versions 1 to 3 (8 on: 64-bit integer constants and classes are refused, 12 on:
5
+ * proto sizes, feedback slots and inline cost are read and dropped); the VM runs the result.
6
+ *
7
+ * The input starts at the version byte. Rive stores scripts with one wrapper
8
+ * byte before it, and the caller strips that byte. A leading 0 byte is how
9
+ * Luau carries a compile error as the rest of the bytes, and loads as a
10
+ * thrown error with that text.
11
+ *
12
+ * Constants are decoded into Lua values: `undefined` is nil, strings are
13
+ * byte strings (one char per byte, 0 to 255), vectors are `Vector`. Three
14
+ * kinds stay descriptors for the VM to expand: imports, table templates
15
+ * (DUPTABLE) and closures (DUPCLOSURE).
16
+ */
17
+
18
+ import { CONSTANT, OP, PROTO_INLINABLE, OP_LENGTH, TYPE_VERSION_MAX, TYPE_VERSION_MIN, VERSION_MAX, VERSION_MIN } from './load.ops.js'
19
+ import { Vector } from '../vm/vm.value.js'
20
+
21
+ /**
22
+ * @typedef {object} Proto
23
+ * @property {number} id index in the module's proto table
24
+ * @property {number} maxStack registers the function needs
25
+ * @property {number} numParams
26
+ * @property {number} numUpvals
27
+ * @property {boolean} isVararg
28
+ * @property {number} flags
29
+ * @property {Uint8Array | null} typeInfo raw type bytes, as stored
30
+ * @property {Int32Array} code instruction words
31
+ * @property {any[]} k constants
32
+ * @property {Proto[]} protos child protos, by NEWCLOSURE index
33
+ * @property {number} lineDefined
34
+ * @property {string} name debug name, '' when stripped
35
+ * @property {Int32Array | null} lines source line of each instruction word, null when stripped
36
+ * @property {{ name: string, start: number, end: number, reg: number }[]} locals
37
+ * @property {string[]} upvalNames
38
+ * @property {string} chunk the module's chunk name
39
+ */
40
+
41
+ /**
42
+ * @typedef {object} Module
43
+ * @property {number} version bytecode version
44
+ * @property {number} typesVersion type-info version, 0 before bytecode version 4
45
+ * @property {string[]} strings the string table
46
+ * @property {Proto[]} protos every proto, in file order
47
+ * @property {Proto} main the main chunk's proto
48
+ * @property {number} size bytes read; equals the input's length for a well-formed module
49
+ */
50
+
51
+ /** An import constant: the path of constants that GETIMPORT walks from the globals */
52
+ export class Import {
53
+ /** @param {number} id the packed id: path length in the top 2 bits, three 10-bit constant indices below */
54
+ constructor(id) {
55
+ this.id = id
56
+ }
57
+ }
58
+
59
+ /** A DUPTABLE template: the keys the table starts with, and the values for those given one */
60
+ export class TableTemplate {
61
+ /**
62
+ * @param {any[]} keys
63
+ * @param {any[]} values
64
+ */
65
+ constructor(keys, values) {
66
+ this.keys = keys
67
+ this.values = values
68
+ }
69
+ }
70
+
71
+ /** A DUPCLOSURE target: the child proto that becomes the closure */
72
+ export class ClosureRef {
73
+ /** @param {Proto} proto */
74
+ constructor(proto) {
75
+ this.proto = proto
76
+ }
77
+ }
78
+
79
+ // Reader
80
+ // ■■■■■■
81
+
82
+ /** A cursor over the bytes; reading past the end throws */
83
+ class Reader {
84
+ /** @param {Uint8Array} bytes */
85
+ constructor(bytes) {
86
+ this.bytes = bytes
87
+ this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
88
+ this.pos = 0
89
+ }
90
+
91
+ /** @param {number} n */
92
+ need(n) {
93
+ if (this.pos + n > this.bytes.length) throw new Error('luau: truncated bytecode at byte ' + this.pos)
94
+ }
95
+
96
+ u8() {
97
+ this.need(1)
98
+ return this.bytes[this.pos++]
99
+ }
100
+
101
+ /** A 32-bit word, signed (the instruction decoders want it that way) */
102
+ u32() {
103
+ this.need(4)
104
+ const v = this.view.getInt32(this.pos, true)
105
+ this.pos += 4
106
+ return v
107
+ }
108
+
109
+ f32() {
110
+ this.need(4)
111
+ const v = this.view.getFloat32(this.pos, true)
112
+ this.pos += 4
113
+ return v
114
+ }
115
+
116
+ f64() {
117
+ this.need(8)
118
+ const v = this.view.getFloat64(this.pos, true)
119
+ this.pos += 8
120
+ return v
121
+ }
122
+
123
+ /** A variable-length unsigned integer, 7 bits a byte, low first */
124
+ varint() {
125
+ let result = 0
126
+ let scale = 1
127
+ let byte
128
+ do {
129
+ byte = this.u8()
130
+ result += (byte & 127) * scale
131
+ scale *= 128
132
+ } while (byte & 128)
133
+ return result
134
+ }
135
+
136
+ /**
137
+ * @param {number} n
138
+ * @returns {string} n bytes as a byte string
139
+ */
140
+ str(n) {
141
+ this.need(n)
142
+ let out = ''
143
+ for (let i = 0; i < n; i += 4096) {
144
+ const end = Math.min(n, i + 4096)
145
+ out += String.fromCharCode.apply(null, /** @type {any} */ (this.bytes.subarray(this.pos + i, this.pos + end)))
146
+ }
147
+ this.pos += n
148
+ return out
149
+ }
150
+ }
151
+
152
+ // Load
153
+ // ■■■■
154
+
155
+ /**
156
+ * @param {Uint8Array} bytes Luau bytecode from its version byte on
157
+ * @param {string} [chunk] the chunk name errors carry
158
+ * @returns {Module}
159
+ */
160
+ export function loadBytecode(bytes, chunk) {
161
+ const name = chunk || '?'
162
+ const r = new Reader(bytes)
163
+ const version = r.u8()
164
+ if (version === 0) throw new Error(name + r.str(bytes.length - 1))
165
+ if (version < VERSION_MIN || version > VERSION_MAX) {
166
+ throw new Error(name + ': bytecode version mismatch (expected [' + VERSION_MIN + '..' + VERSION_MAX + '], got ' + version + ')')
167
+ }
168
+
169
+ let typesVersion = 0
170
+ if (version >= 4) {
171
+ typesVersion = r.u8()
172
+ if (typesVersion < TYPE_VERSION_MIN || typesVersion > TYPE_VERSION_MAX) {
173
+ throw new Error(name + ': bytecode type version mismatch (expected [' + TYPE_VERSION_MIN + '..' + TYPE_VERSION_MAX + '], got ' + typesVersion + ')')
174
+ }
175
+ }
176
+
177
+ const stringCount = r.varint()
178
+ /** @type {string[]} */
179
+ const strings = []
180
+ for (let i = 0; i < stringCount; i++) strings.push(r.str(r.varint()))
181
+
182
+ /** @param {number} id */
183
+ const string = (id) => (id === 0 ? '' : strings[id - 1])
184
+
185
+ // userdata type names, which the VM has no use for
186
+ if (typesVersion === 3) {
187
+ let index = r.u8()
188
+ while (index !== 0) {
189
+ r.varint()
190
+ index = r.u8()
191
+ }
192
+ }
193
+
194
+ const protoCount = r.varint()
195
+ /** @type {Proto[]} */
196
+ const protos = []
197
+ for (let i = 0; i < protoCount; i++) protos.push(readProto(r, i, version, string, protos, name))
198
+
199
+ const mainId = r.varint()
200
+ if (mainId >= protos.length) throw new Error(name + ': main proto ' + mainId + ' out of range')
201
+ return { version, typesVersion, strings, protos, main: protos[mainId], size: r.pos }
202
+ }
203
+
204
+ /**
205
+ * @param {Reader} r
206
+ * @param {number} id
207
+ * @param {number} version
208
+ * @param {(id: number) => string} string
209
+ * @param {Proto[]} protos the protos read so far
210
+ * @param {string} chunk
211
+ * @returns {Proto}
212
+ */
213
+ function readProto(r, id, version, string, protos, chunk) {
214
+ // version 12 on: each proto is prefixed with its size, so a reader can skip what it does not know
215
+ const protoSize = version >= 12 ? r.varint() : 0
216
+ const protoStart = r.pos
217
+ const maxStack = r.u8()
218
+ const numParams = r.u8()
219
+ const numUpvals = r.u8()
220
+ const isVararg = r.u8() !== 0
221
+ let flags = 0
222
+ let typeInfo = null
223
+ if (version >= 4) {
224
+ flags = r.u8()
225
+ const typeSize = r.varint()
226
+ if (typeSize > 0) {
227
+ r.need(typeSize)
228
+ // a copy: typed arrays' slice is ES2015, subarray and the constructor are older
229
+ typeInfo = new Uint8Array(r.bytes.subarray(r.pos, r.pos + typeSize))
230
+ r.pos += typeSize
231
+ }
232
+ }
233
+
234
+ const sizeCode = r.varint()
235
+ const code = new Int32Array(sizeCode)
236
+ for (let i = 0; i < sizeCode; i++) code[i] = r.u32()
237
+
238
+ const sizeK = r.varint()
239
+ /** @type {any[]} */
240
+ const k = []
241
+ for (let i = 0; i < sizeK; i++) {
242
+ const tag = r.u8()
243
+ if (tag === CONSTANT.nil) k.push(undefined)
244
+ else if (tag === CONSTANT.boolean) k.push(r.u8() !== 0)
245
+ else if (tag === CONSTANT.number) k.push(r.f64())
246
+ else if (tag === CONSTANT.string) k.push(string(r.varint()))
247
+ else if (tag === CONSTANT.import) k.push(new Import(r.u32()))
248
+ else if (tag === CONSTANT.vector) {
249
+ const x = r.f32()
250
+ const y = r.f32()
251
+ const z = r.f32()
252
+ r.f32()
253
+ k.push(new Vector(x, y, z))
254
+ } else if (tag === CONSTANT.table) {
255
+ const n = r.varint()
256
+ const keys = []
257
+ const values = []
258
+ for (let j = 0; j < n; j++) {
259
+ keys.push(k[r.varint()])
260
+ // the shape only: the code that follows sets each value
261
+ values.push(0)
262
+ }
263
+ k.push(new TableTemplate(keys, values))
264
+ } else if (tag === CONSTANT.tableWithConstants) {
265
+ const n = r.varint()
266
+ const keys = []
267
+ const values = []
268
+ for (let j = 0; j < n; j++) {
269
+ keys.push(k[r.varint()])
270
+ const index = r.u32()
271
+ values.push(index >= 0 ? k[index] : 0)
272
+ }
273
+ k.push(new TableTemplate(keys, values))
274
+ } else if (tag === CONSTANT.closure) {
275
+ const fid = r.varint()
276
+ if (fid >= protos.length) throw new Error(chunk + ': closure constant names proto ' + fid + ' before it is read')
277
+ k.push(new ClosureRef(protos[fid]))
278
+ } else throw new Error(chunk + ': unknown constant kind ' + tag)
279
+ }
280
+
281
+ const sizeP = r.varint()
282
+ const children = []
283
+ for (let i = 0; i < sizeP; i++) {
284
+ const fid = r.varint()
285
+ if (fid >= protos.length) throw new Error(chunk + ': child proto ' + fid + ' is not read yet')
286
+ children.push(protos[fid])
287
+ }
288
+
289
+ const lineDefined = r.varint()
290
+ const name = string(r.varint())
291
+
292
+ /** @type {Int32Array | null} */
293
+ let lines = null
294
+ if (r.u8() !== 0) {
295
+ const gapLog2 = r.u8()
296
+ const offsets = new Uint8Array(sizeCode)
297
+ let last = 0
298
+ for (let i = 0; i < sizeCode; i++) {
299
+ last = (last + r.u8()) & 255
300
+ offsets[i] = last
301
+ }
302
+ const intervals = ((sizeCode - 1) >> gapLog2) + 1
303
+ const absolute = []
304
+ let line = 0
305
+ for (let i = 0; i < intervals; i++) {
306
+ line += r.u32()
307
+ absolute.push(line)
308
+ }
309
+ lines = new Int32Array(sizeCode)
310
+ for (let i = 0; i < sizeCode; i++) lines[i] = absolute[i >> gapLog2] + offsets[i]
311
+ }
312
+
313
+ const locals = []
314
+ const upvalNames = []
315
+ if (r.u8() !== 0) {
316
+ const n = r.varint()
317
+ for (let i = 0; i < n; i++) {
318
+ const varName = string(r.varint())
319
+ const start = r.varint()
320
+ const end = r.varint()
321
+ locals.push({ name: varName, start, end, reg: r.u8() })
322
+ }
323
+ const nu = r.varint()
324
+ for (let i = 0; i < nu; i++) upvalNames.push(string(r.varint()))
325
+ }
326
+
327
+ if (version >= 11) {
328
+ // feedback slots: a call target (pc) or a construct site (shape), unused here
329
+ const slots = r.varint()
330
+ for (let i = 0; i < slots; i++) {
331
+ r.u8()
332
+ r.varint()
333
+ }
334
+ }
335
+ // the inline cost of a small function, unused here
336
+ if (version >= 12 && (flags & PROTO_INLINABLE) !== 0) r.varint()
337
+ if (version >= 12) r.pos = protoStart + protoSize
338
+
339
+ checkCode(code, children.length, chunk, id)
340
+
341
+ return { id, maxStack, numParams, numUpvals, isVararg, flags, typeInfo, code, k, protos: children, lineDefined, name, lines, locals, upvalNames, chunk }
342
+ }
343
+
344
+ /**
345
+ * Walks the instruction stream so a bad opcode or a cut-off AUX word fails at
346
+ * load, not in the middle of a run.
347
+ *
348
+ * @param {Int32Array} code
349
+ * @param {number} sizeP
350
+ * @param {string} chunk
351
+ * @param {number} id
352
+ */
353
+ function checkCode(code, sizeP, chunk, id) {
354
+ for (let pc = 0; pc < code.length;) {
355
+ const op = code[pc] & 255
356
+ const length = OP_LENGTH[op]
357
+ if (length === 0) throw new Error(chunk + ': proto ' + id + ' has unknown opcode ' + op + ' at ' + pc)
358
+ if (pc + length > code.length) throw new Error(chunk + ': proto ' + id + ' ends inside an instruction at ' + pc)
359
+ if (op === OP.NEWCLOSURE && (code[pc] >> 16) >= sizeP) throw new Error(chunk + ': proto ' + id + ' makes a closure of a missing child at ' + pc)
360
+ pc += length
361
+ }
362
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Luau's opcodes, in the order Bytecode.h numbers them (the numbers are baked
3
+ * into compiled bytecode), and how long each instruction is: one 32-bit word,
4
+ * or two when an AUX word follows.
5
+ */
6
+
7
+ // Opcodes
8
+ // ■■■■■■■■
9
+
10
+ /** Opcode names by number, for bytecode versions 3 to 14 (the experimental and userdata ones between are not carried) */
11
+ export const OP_NAMES = [
12
+ 'NOP', 'BREAK', 'LOADNIL', 'LOADB', 'LOADN', 'LOADK', 'MOVE', 'GETGLOBAL', 'SETGLOBAL', 'GETUPVAL',
13
+ 'SETUPVAL', 'CLOSEUPVALS', 'GETIMPORT', 'GETTABLE', 'SETTABLE', 'GETTABLEKS', 'SETTABLEKS', 'GETTABLEN', 'SETTABLEN', 'NEWCLOSURE',
14
+ 'NAMECALL', 'CALL', 'RETURN', 'JUMP', 'JUMPBACK', 'JUMPIF', 'JUMPIFNOT', 'JUMPIFEQ', 'JUMPIFLE', 'JUMPIFLT',
15
+ 'JUMPIFNOTEQ', 'JUMPIFNOTLE', 'JUMPIFNOTLT', 'ADD', 'SUB', 'MUL', 'DIV', 'MOD', 'POW', 'ADDK',
16
+ 'SUBK', 'MULK', 'DIVK', 'MODK', 'POWK', 'AND', 'OR', 'ANDK', 'ORK', 'CONCAT',
17
+ 'NOT', 'MINUS', 'LENGTH', 'NEWTABLE', 'DUPTABLE', 'SETLIST', 'FORNPREP', 'FORNLOOP', 'FORGLOOP', 'FORGPREP_INEXT',
18
+ 'FASTCALL3', 'FORGPREP_NEXT', 'NATIVECALL', 'GETVARARGS', 'DUPCLOSURE', 'PREPVARARGS', 'LOADKX', 'JUMPX', 'FASTCALL', 'COVERAGE',
19
+ 'CAPTURE', 'SUBRK', 'DIVRK', 'FASTCALL1', 'FASTCALL2', 'FASTCALL2K', 'FORGPREP', 'JUMPXEQKNIL', 'JUMPXEQKB', 'JUMPXEQKN',
20
+ 'JUMPXEQKS', 'IDIV', 'IDIVK',
21
+ ]
22
+
23
+ /**
24
+ * Opcodes past the contiguous table, after the userdata and experimental ones:
25
+ * CALLFB (a CALL and a feedback slot word) and FASTPCALL (a fast-call hint for
26
+ * pcall and xpcall), which the compiler emits from bytecode version 14
27
+ */
28
+ const OP_LATE = { CALLFB: 87, FASTPCALL: 89 }
29
+
30
+ /** Opcode numbers by name */
31
+ export const OP = /** @type {Record<string, number>} */ ({})
32
+ for (let i = 0; i < OP_NAMES.length; i++) OP[OP_NAMES[i]] = i
33
+ for (const name in OP_LATE) OP[name] = OP_LATE[/** @type {'CALLFB'} */ (name)]
34
+
35
+ /** Opcodes followed by an AUX word */
36
+ const WITH_AUX = [
37
+ 'GETGLOBAL', 'SETGLOBAL', 'GETIMPORT', 'GETTABLEKS', 'SETTABLEKS', 'NAMECALL', 'JUMPIFEQ', 'JUMPIFLE', 'JUMPIFLT',
38
+ 'JUMPIFNOTEQ', 'JUMPIFNOTLE', 'JUMPIFNOTLT', 'NEWTABLE', 'SETLIST', 'FORGLOOP', 'FASTCALL3', 'LOADKX', 'FASTCALL2',
39
+ 'FASTCALL2K', 'JUMPXEQKNIL', 'JUMPXEQKB', 'JUMPXEQKN', 'JUMPXEQKS',
40
+ ]
41
+
42
+ /** Instruction length in words, by opcode number; 0 for a number that is no opcode */
43
+ export const OP_LENGTH = /** @type {number[]} */ ([])
44
+ for (let i = 0; i < 256; i++) OP_LENGTH.push(i < OP_NAMES.length || i === OP_LATE.FASTPCALL || i === OP_LATE.CALLFB ? 1 : 0)
45
+ for (let i = 0; i < WITH_AUX.length; i++) OP_LENGTH[OP[WITH_AUX[i]]] = 2
46
+ OP_LENGTH[OP_LATE.CALLFB] = 2
47
+
48
+ // Constants
49
+ // ■■■■■■■■■
50
+
51
+ /** Constant table entry tags */
52
+ export const CONSTANT = {
53
+ nil: 0,
54
+ boolean: 1,
55
+ number: 2,
56
+ string: 3,
57
+ import: 4,
58
+ table: 5,
59
+ closure: 6,
60
+ vector: 7,
61
+ tableWithConstants: 8,
62
+ integer: 9,
63
+ classShape: 10,
64
+ vectorD: 11,
65
+ }
66
+
67
+ /** Proto flag: the function is small enough to inline (bytecode version 12 on stores its cost) */
68
+ export const PROTO_INLINABLE = 1 << 3
69
+
70
+ /** Upvalue capture kinds (CAPTURE's A) */
71
+ export const CAPTURE = { val: 0, ref: 1, upval: 2 }
72
+
73
+ /** Oldest and newest bytecode version this loader reads */
74
+ export const VERSION_MIN = 3
75
+ export const VERSION_MAX = 14
76
+
77
+ /** Type-info versions this loader reads */
78
+ export const TYPE_VERSION_MIN = 1
79
+ export const TYPE_VERSION_MAX = 3
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Values between the host and Lua. Strings cross as UTF-8 (Lua strings are
3
+ * byte strings); arrays and plain objects become tables and tables become
4
+ * arrays or objects; host functions become Lua functions and Lua functions
5
+ * become host functions.
6
+ */
7
+
8
+ import { EMPTY, LuaClosure, LuaTable, LuaThrow, NativeFunction, Vector } from './vm/vm.value.js'
9
+ import { tostring } from './vm/vm.meta.js'
10
+
11
+ /** @typedef {import('./vm/vm.js').VM} VM */
12
+
13
+ /** An error from Lua code, as the host sees it */
14
+ export class LuauError {
15
+ /**
16
+ * @param {string} message the error value as text, with its position when there is one
17
+ * @param {any} value the error value, converted for the host
18
+ * @param {number} line the source line it was raised at, 0 when unknown
19
+ * @param {string} chunk the chunk that line is in
20
+ */
21
+ constructor(message, value, line, chunk) {
22
+ this.name = 'LuauError'
23
+ this.message = message
24
+ this.value = value
25
+ this.line = line
26
+ this.chunk = chunk
27
+ this.stack = new Error(message).stack
28
+ }
29
+
30
+ toString() {
31
+ return 'LuauError: ' + this.message
32
+ }
33
+ }
34
+
35
+ /**
36
+ * @param {VM} vm
37
+ * @param {LuaThrow} e
38
+ * @returns {LuauError}
39
+ */
40
+ export function luauError(vm, e) {
41
+ const message = typeof e.value === 'string' ? decodeUtf8(e.value) : decodeUtf8(tostring(vm, e.value))
42
+ return new LuauError(message, toJs(vm, e.value), e.line, e.chunk)
43
+ }
44
+
45
+ /** Several results from a host function: `return multi(a, b)` */
46
+ export class Multi {
47
+ /** @param {any[]} values */
48
+ constructor(values) {
49
+ this.values = values
50
+ }
51
+ }
52
+
53
+ /**
54
+ * @param {...any} values
55
+ * @returns {Multi}
56
+ */
57
+ export function multi(...values) {
58
+ return new Multi(values)
59
+ }
60
+
61
+ /**
62
+ * @param {string} s a JS string
63
+ * @returns {string} the same text as UTF-8 bytes, one char per byte
64
+ */
65
+ export function encodeUtf8(s) {
66
+ let ascii = true
67
+ for (let i = 0; i < s.length; i++) {
68
+ if (s.charCodeAt(i) > 127) {
69
+ ascii = false
70
+ break
71
+ }
72
+ }
73
+ if (ascii) return s
74
+ let out = ''
75
+ for (let i = 0; i < s.length; i++) {
76
+ let c = s.charCodeAt(i)
77
+ if (c >= 0xd800 && c < 0xdc00 && i + 1 < s.length) {
78
+ const d = s.charCodeAt(i + 1)
79
+ if (d >= 0xdc00 && d < 0xe000) {
80
+ c = 0x10000 + ((c - 0xd800) << 10) + (d - 0xdc00)
81
+ i++
82
+ }
83
+ }
84
+ if (c < 0x80) out += String.fromCharCode(c)
85
+ else if (c < 0x800) out += String.fromCharCode(0xc0 | (c >> 6), 0x80 | (c & 63))
86
+ else if (c < 0x10000) out += String.fromCharCode(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 63), 0x80 | (c & 63))
87
+ else out += String.fromCharCode(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 63), 0x80 | ((c >> 6) & 63), 0x80 | (c & 63))
88
+ }
89
+ return out
90
+ }
91
+
92
+ /**
93
+ * @param {string} s a byte string
94
+ * @returns {string} its UTF-8 decoding; bytes that are not valid UTF-8 become themselves
95
+ */
96
+ export function decodeUtf8(s) {
97
+ let ascii = true
98
+ for (let i = 0; i < s.length; i++) {
99
+ if (s.charCodeAt(i) > 127) {
100
+ ascii = false
101
+ break
102
+ }
103
+ }
104
+ if (ascii) return s
105
+ let out = ''
106
+ for (let i = 0; i < s.length;) {
107
+ const c = s.charCodeAt(i)
108
+ let need = 0
109
+ let cp = c
110
+ if (c >= 0xf0 && c < 0xf8) {
111
+ need = 3
112
+ cp = c & 7
113
+ } else if (c >= 0xe0) {
114
+ need = c < 0xf0 ? 2 : -1
115
+ cp = c & 15
116
+ } else if (c >= 0xc0) {
117
+ need = 1
118
+ cp = c & 31
119
+ }
120
+ let ok = need > 0 && i + need < s.length
121
+ if (ok) {
122
+ for (let n = 1; n <= need; n++) {
123
+ const d = s.charCodeAt(i + n)
124
+ if ((d & 0xc0) !== 0x80) {
125
+ ok = false
126
+ break
127
+ }
128
+ cp = (cp << 6) | (d & 63)
129
+ }
130
+ }
131
+ if (!ok) {
132
+ out += String.fromCharCode(c)
133
+ i++
134
+ continue
135
+ }
136
+ i += need + 1
137
+ if (cp >= 0x10000) {
138
+ cp -= 0x10000
139
+ out += String.fromCharCode(0xd800 + (cp >> 10), 0xdc00 + (cp & 1023))
140
+ } else out += String.fromCharCode(cp)
141
+ }
142
+ return out
143
+ }
144
+
145
+ /**
146
+ * A host value as a Lua value. Lua values pass through; `null` is nil.
147
+ *
148
+ * @param {VM} vm
149
+ * @param {any} v
150
+ * @param {number} [depth]
151
+ * @returns {any}
152
+ */
153
+ export function toLua(vm, v, depth) {
154
+ const d = depth || 0
155
+ if (v === null || v === undefined) return undefined
156
+ switch (typeof v) {
157
+ case 'boolean':
158
+ case 'number': return v
159
+ case 'string': return encodeUtf8(v)
160
+ case 'function': return hostFunction(vm, v)
161
+ default: break
162
+ }
163
+ if (v instanceof LuaTable || v instanceof Vector || v instanceof LuaClosure || v instanceof NativeFunction) return v
164
+ if (d > 64) throw new Error('luau: value nested too deeply to convert')
165
+ const t = new LuaTable()
166
+ if (Array.isArray(v)) {
167
+ const items = []
168
+ for (let i = 0; i < v.length; i++) items.push(toLua(vm, v[i], d + 1))
169
+ t.setRange(1, items, 0, items.length)
170
+ } else {
171
+ for (const key in v) {
172
+ const value = toLua(vm, v[key], d + 1)
173
+ if (value !== undefined) t.set(encodeUtf8(key), value)
174
+ }
175
+ }
176
+ return t
177
+ }
178
+
179
+ /**
180
+ * A host function as a Lua function. Arguments and the result convert; an
181
+ * error it throws becomes a Lua error.
182
+ *
183
+ * @param {VM} vm
184
+ * @param {Function} fn
185
+ * @returns {NativeFunction}
186
+ */
187
+ export function hostFunction(vm, fn) {
188
+ return new NativeFunction(fn.name || 'host', function (vm2, args) {
189
+ const jsArgs = []
190
+ for (let i = 0; i < args.length; i++) jsArgs.push(toJs(vm2, args[i]))
191
+ let result
192
+ try {
193
+ result = fn.apply(null, jsArgs)
194
+ } catch (e) {
195
+ if (e instanceof LuaThrow) throw e
196
+ const err = /** @type {any} */ (e)
197
+ if (err instanceof LuauError) throw new LuaThrow(encodeUtf8(err.message), 0)
198
+ throw new LuaThrow(encodeUtf8(err && err.message !== undefined ? String(err.message) : String(err)), 1)
199
+ }
200
+ if (result instanceof Multi) {
201
+ const out = []
202
+ for (let i = 0; i < result.values.length; i++) out.push(toLua(vm2, result.values[i]))
203
+ return out
204
+ }
205
+ return result === undefined ? EMPTY : toLua(vm2, result)
206
+ })
207
+ }
208
+
209
+ /**
210
+ * A Lua value as a host value: strings decode, a table whose keys are 1..n
211
+ * becomes an array and any other an object, a function becomes a host
212
+ * function (which returns nothing, its one result, or an array of them). A
213
+ * vector, and a function a host function made, stay as they are.
214
+ *
215
+ * @param {VM} vm
216
+ * @param {any} v
217
+ * @param {number} [depth]
218
+ * @returns {any}
219
+ */
220
+ export function toJs(vm, v, depth) {
221
+ const d = depth || 0
222
+ if (typeof v === 'string') return decodeUtf8(v)
223
+ if (v instanceof LuaTable) {
224
+ if (d > 64) throw new Error('luau: table nested too deeply to convert')
225
+ if (v.hk.length - v.dead === 0) {
226
+ const out = []
227
+ for (let i = 0; i < v.arr.length; i++) out.push(toJs(vm, v.arr[i], d + 1))
228
+ return out
229
+ }
230
+ /** @type {Record<string, any>} */
231
+ const obj = {}
232
+ let pos = 0
233
+ for (;;) {
234
+ const next = v.advance(pos)
235
+ if (next === null) break
236
+ pos = next
237
+ obj[decodeUtf8(typeof v.ck === 'string' ? v.ck : tostring(vm, v.ck))] = toJs(vm, v.cv, d + 1)
238
+ }
239
+ return obj
240
+ }
241
+ if (v instanceof LuaClosure || v instanceof NativeFunction) {
242
+ return function () {
243
+ const args = []
244
+ for (let i = 0; i < arguments.length; i++) args.push(toLua(vm, arguments[i]))
245
+ let res
246
+ try {
247
+ res = vm.call(v, args)
248
+ } catch (e) {
249
+ throw e instanceof LuaThrow ? luauError(vm, e) : e
250
+ }
251
+ if (res.length === 0) return undefined
252
+ if (res.length === 1) return toJs(vm, res[0])
253
+ const out = []
254
+ for (let i = 0; i < res.length; i++) out.push(toJs(vm, res[i]))
255
+ return out
256
+ }
257
+ }
258
+ return v
259
+ }