@flamework-experimental/transformer 2.0.0-alpha.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 (72) hide show
  1. package/README.md +66 -0
  2. package/flamework-schema.json +76 -0
  3. package/flamework.config.schema.json +197 -0
  4. package/out/classes/buildInfo.js +467 -0
  5. package/out/classes/diagnostics.js +110 -0
  6. package/out/classes/logger.js +201 -0
  7. package/out/classes/nodeMetadata.js +232 -0
  8. package/out/classes/transformState.js +623 -0
  9. package/out/index.js +75 -0
  10. package/out/transformations/expressions/transformAccessExpression.js +35 -0
  11. package/out/transformations/expressions/transformBinaryExpression.js +57 -0
  12. package/out/transformations/expressions/transformCallExpression.js +33 -0
  13. package/out/transformations/expressions/transformDeleteExpression.js +22 -0
  14. package/out/transformations/expressions/transformNewExpression.js +17 -0
  15. package/out/transformations/expressions/transformUnaryExpression.js +63 -0
  16. package/out/transformations/macros/intrinsics/components.js +64 -0
  17. package/out/transformations/macros/intrinsics/env.js +31 -0
  18. package/out/transformations/macros/intrinsics/guards.js +43 -0
  19. package/out/transformations/macros/intrinsics/inlining.js +22 -0
  20. package/out/transformations/macros/intrinsics/networking.js +131 -0
  21. package/out/transformations/macros/intrinsics/parameters.js +81 -0
  22. package/out/transformations/macros/intrinsics/paths.js +42 -0
  23. package/out/transformations/macros/updateComponentConfig.js +409 -0
  24. package/out/transformations/plugins/nodeFactory.js +159 -0
  25. package/out/transformations/plugins/pluginHost.js +235 -0
  26. package/out/transformations/plugins/typeFacade.js +242 -0
  27. package/out/transformations/statements/transformClassDeclaration.js +282 -0
  28. package/out/transformations/transformExpression.js +34 -0
  29. package/out/transformations/transformFile.js +78 -0
  30. package/out/transformations/transformNetworkingCall.js +566 -0
  31. package/out/transformations/transformNode.js +27 -0
  32. package/out/transformations/transformStatement.js +57 -0
  33. package/out/transformations/transformStatementList.js +64 -0
  34. package/out/transformations/transformUserMacro.js +698 -0
  35. package/out/transformer.js +52 -0
  36. package/out/util/cache.js +10 -0
  37. package/out/util/constants.js +5 -0
  38. package/out/util/diagnosticsUtils.js +112 -0
  39. package/out/util/env.js +267 -0
  40. package/out/util/factory.js +583 -0
  41. package/out/util/functions/addLeadingComment.js +13 -0
  42. package/out/util/functions/arePathsEqual.js +14 -0
  43. package/out/util/functions/assert.js +17 -0
  44. package/out/util/functions/buildGuardFromType.js +713 -0
  45. package/out/util/functions/buildInstanceShape.js +156 -0
  46. package/out/util/functions/buildSerializerFromType.js +2309 -0
  47. package/out/util/functions/createPathTranslator.js +54 -0
  48. package/out/util/functions/emitTypescriptMismatch.js +80 -0
  49. package/out/util/functions/getDeclarationName.js +23 -0
  50. package/out/util/functions/getDeclarationOfType.js +7 -0
  51. package/out/util/functions/getIndexExpression.js +16 -0
  52. package/out/util/functions/getInstanceTypeFromType.js +75 -0
  53. package/out/util/functions/getNodeList.js +6 -0
  54. package/out/util/functions/getPackageJson.js +44 -0
  55. package/out/util/functions/getSuperClasses.js +51 -0
  56. package/out/util/functions/isAttributesAccess.js +23 -0
  57. package/out/util/functions/isCleanBuildDirectory.js +13 -0
  58. package/out/util/functions/isDefinedType.js +16 -0
  59. package/out/util/functions/isPathDescendantOf.js +25 -0
  60. package/out/util/functions/isTupleType.js +10 -0
  61. package/out/util/functions/parseCommandLine.js +31 -0
  62. package/out/util/functions/shuffle.js +39 -0
  63. package/out/util/functions/tryResolve.js +15 -0
  64. package/out/util/functions/validateConstraintMetadata.js +60 -0
  65. package/out/util/packages.js +12 -0
  66. package/out/util/projectConfig.js +221 -0
  67. package/out/util/schema.js +80 -0
  68. package/out/util/tsInternals.js +16 -0
  69. package/out/util/uid.js +227 -0
  70. package/package.json +45 -0
  71. package/rojo-schema.json +34 -0
  72. package/types.d.ts +4 -0
@@ -0,0 +1,2309 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __values = (this && this.__values) || function(o) {
14
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
15
+ if (m) return m.call(o);
16
+ if (o && typeof o.length === "number") return {
17
+ next: function () {
18
+ if (o && i >= o.length) o = void 0;
19
+ return { value: o && o[i++], done: !o };
20
+ }
21
+ };
22
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
23
+ };
24
+ var __read = (this && this.__read) || function (o, n) {
25
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
26
+ if (!m) return o;
27
+ var i = m.call(o), r, ar = [], e;
28
+ try {
29
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
30
+ }
31
+ catch (error) { e = { error: error }; }
32
+ finally {
33
+ try {
34
+ if (r && !r.done && (m = i["return"])) m.call(i);
35
+ }
36
+ finally { if (e) throw e.error; }
37
+ }
38
+ return ar;
39
+ };
40
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
41
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
42
+ if (ar || !(i in from)) {
43
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
44
+ ar[i] = from[i];
45
+ }
46
+ }
47
+ return to.concat(ar || Array.prototype.slice.call(from));
48
+ };
49
+ var __importDefault = (this && this.__importDefault) || function (mod) {
50
+ return (mod && mod.__esModule) ? mod : { "default": mod };
51
+ };
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.buildSerializerFromType = buildSerializerFromType;
54
+ exports.buildDecoderFromType = buildDecoderFromType;
55
+ exports.buildInlineEncoding = buildInlineEncoding;
56
+ exports.buildResultDecoderFromType = buildResultDecoderFromType;
57
+ exports.buildInlineResultEncoding = buildInlineResultEncoding;
58
+ exports.unwrapPromise = unwrapPromise;
59
+ exports.createSerializerGenerator = createSerializerGenerator;
60
+ var typescript_1 = __importDefault(require("typescript"));
61
+ var diagnostics_1 = require("../../classes/diagnostics");
62
+ var factory_1 = require("../factory");
63
+ var tsInternals_1 = require("../tsInternals");
64
+ var buildGuardFromType_1 = require("./buildGuardFromType");
65
+ var isTupleType_1 = require("./isTupleType");
66
+ var WIDTH_SIZE = { u8: 1, i8: 1, u16: 2, i16: 2, u32: 4, i32: 4, f32: 4, f64: 8 };
67
+ var LENGTH_MAX = { u8: 0xff, u16: 0xffff, u32: 0xffffffff };
68
+ var lengthMin = function (width) { return (width === "v" ? 1 : WIDTH_SIZE[width]); };
69
+ /** `number & { <anything>: "<brand>" }` selects a width; the property name does not matter. */
70
+ var NUMBER_BRANDS = new Set(Object.keys(WIDTH_SIZE));
71
+ var VARINT_BRAND = "varint";
72
+ var STRING_BRANDS = { u8_string: "u8", u16_string: "u16", u32_string: "u32" };
73
+ var BUFFER_BRANDS = { u16_buffer: "u16", u32_buffer: "u32" };
74
+ /** A blob's 1-based index in the blob list, 0 for nil. */
75
+ var BLOB_SIZE = 4;
76
+ var VARINT_MAX_BYTES = 5;
77
+ /**
78
+ * Counts of zero-size elements cannot be bounded by the bytes left, so a payload gets a plain cap on
79
+ * how many of them it may hold in all. Per payload rather than per collection: a cap per collection
80
+ * multiplies through nesting, and `Array<Array<Marker>>` let a 151-byte payload drive 50 × 65535
81
+ * element tables.
82
+ */
83
+ var ZERO_SIZE_COUNT_MAX = 0xffff;
84
+ /** Where roblox-ts declares the Roblox API: everything in there without a layout travels as a blob. */
85
+ var ROBLOX_TYPES = /[\\/]@rbxts[\\/]types[\\/]/;
86
+ /** Roblox datatypes with a buffer representation: the fields written, in constructor order. */
87
+ var DATATYPES = {
88
+ Vector3: [
89
+ ["f32", ["X"]],
90
+ ["f32", ["Y"]],
91
+ ["f32", ["Z"]],
92
+ ],
93
+ Vector2: [
94
+ ["f32", ["X"]],
95
+ ["f32", ["Y"]],
96
+ ],
97
+ Vector3int16: [
98
+ ["i16", ["X"]],
99
+ ["i16", ["Y"]],
100
+ ["i16", ["Z"]],
101
+ ],
102
+ Vector2int16: [
103
+ ["i16", ["X"]],
104
+ ["i16", ["Y"]],
105
+ ],
106
+ Color3: [
107
+ ["f32", ["R"]],
108
+ ["f32", ["G"]],
109
+ ["f32", ["B"]],
110
+ ],
111
+ UDim: [
112
+ ["f32", ["Scale"]],
113
+ ["i32", ["Offset"]],
114
+ ],
115
+ UDim2: [
116
+ ["f32", ["X", "Scale"]],
117
+ ["i32", ["X", "Offset"]],
118
+ ["f32", ["Y", "Scale"]],
119
+ ["i32", ["Y", "Offset"]],
120
+ ],
121
+ NumberRange: [
122
+ ["f32", ["Min"]],
123
+ ["f32", ["Max"]],
124
+ ],
125
+ Rect: [
126
+ ["f32", ["Min", "X"]],
127
+ ["f32", ["Min", "Y"]],
128
+ ["f32", ["Max", "X"]],
129
+ ["f32", ["Max", "Y"]],
130
+ ],
131
+ BrickColor: [["u16", ["Number"]]],
132
+ };
133
+ var CFRAME_COMPONENTS = 12;
134
+ var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
135
+ var MALFORMED = "malformed payload";
136
+ /** What a name has to mean globally for a generated local to have to avoid it; see `localName`. */
137
+ var GLOBAL_MEANING = typescript_1.default.SymbolFlags.Value | typescript_1.default.SymbolFlags.Namespace;
138
+ /** Kinds whose values are Luau tables, which can be indexed without a `typeof` check first. */
139
+ var TABLE_KINDS = new Set(["object", "map", "array", "set", "list"]);
140
+ function isKind(shape) {
141
+ return "kind" in shape;
142
+ }
143
+ // --- AST shorthands ---------------------------------------------------------------------------------
144
+ var factory = typescript_1.default.factory;
145
+ var num = function (value) { return factory_1.f.number(value); };
146
+ var prop = function (object, name) {
147
+ return factory.createPropertyAccessExpression(typeof object === "string" ? factory_1.f.identifier(object) : object, name);
148
+ };
149
+ var bufferCall = function (method, args) { return factory_1.f.call(prop("buffer", method), args); };
150
+ var uid = function (hint) { return factory_1.f.identifier(hint, true); };
151
+ var assign = function (target, value) {
152
+ return factory_1.f.statement(factory_1.f.binary(target, typescript_1.default.SyntaxKind.EqualsToken, value));
153
+ };
154
+ var addAssign = function (target, value) {
155
+ return factory_1.f.statement(factory_1.f.binary(target, typescript_1.default.SyntaxKind.PlusEqualsToken, value));
156
+ };
157
+ var constDecl = function (name, value, type) {
158
+ return factory_1.f.variableStatement(name, value, type);
159
+ };
160
+ var letDecl = function (name, value, type) {
161
+ return factory_1.f.variableStatement(name, value, type, true);
162
+ };
163
+ var ifStatement = function (condition, then, otherwise) {
164
+ return factory.createIfStatement(condition, factory_1.f.block(then), Array.isArray(otherwise) ? factory_1.f.block(otherwise) : otherwise);
165
+ };
166
+ var forOf = function (name, iterable, body) {
167
+ return factory.createForOfStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(name)], typescript_1.default.NodeFlags.Const), iterable, factory_1.f.block(body));
168
+ };
169
+ var range = function (from, to) { return factory_1.f.call("$range", [from, to]); };
170
+ var notNil = function (value) { return factory_1.f.binary(value, typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, factory_1.f.nil()); };
171
+ var isNil = function (value) { return factory_1.f.binary(value, typescript_1.default.SyntaxKind.EqualsEqualsEqualsToken, factory_1.f.nil()); };
172
+ var equals = function (left, right) {
173
+ return factory_1.f.binary(left, typescript_1.default.SyntaxKind.EqualsEqualsEqualsToken, right);
174
+ };
175
+ var conditional = function (condition, whenTrue, whenFalse) {
176
+ return factory.createConditionalExpression(condition, factory_1.f.token(typescript_1.default.SyntaxKind.QuestionToken), whenTrue, factory_1.f.token(typescript_1.default.SyntaxKind.ColonToken), whenFalse);
177
+ };
178
+ var raise = function (message) { return factory_1.f.statement(factory_1.f.call("error", [factory_1.f.string(message)])); };
179
+ /** `typeIs(v, name)`: roblox-ts emits `type(v) == name` for primitives and `typeof(v) == name` otherwise, with no temporaries. */
180
+ var typeOfIs = function (value, name) { return factory_1.f.call("typeIs", [value, factory_1.f.string(name)]); };
181
+ var construct = function (name, args, typeArguments) {
182
+ return factory.createNewExpression(factory_1.f.identifier(name), typeArguments, args);
183
+ };
184
+ /** A literal expression as text, for comparing and ordering literals. */
185
+ function printLiteral(expression) {
186
+ if (factory_1.f.is.string(expression))
187
+ return JSON.stringify(expression.text);
188
+ if (factory_1.f.is.number(expression))
189
+ return expression.text;
190
+ if (typescript_1.default.isPrefixUnaryExpression(expression))
191
+ return "-".concat(printLiteral(expression.operand));
192
+ if (expression.kind === typescript_1.default.SyntaxKind.TrueKeyword)
193
+ return "true";
194
+ if (expression.kind === typescript_1.default.SyntaxKind.FalseKeyword)
195
+ return "false";
196
+ return "#".concat(expression.kind);
197
+ }
198
+ /** A byte total under construction: the constants fold into one literal, the terms keep their order. */
199
+ var Sum = /** @class */ (function () {
200
+ function Sum() {
201
+ this.constant = 0;
202
+ this.terms = new Array();
203
+ }
204
+ Sum.prototype.add = function (part) {
205
+ if (typeof part === "number")
206
+ this.constant += part;
207
+ else if (factory_1.f.is.number(part))
208
+ this.constant += Number(part.text);
209
+ else
210
+ this.terms.push(part);
211
+ };
212
+ Sum.prototype.build = function () {
213
+ var e_1, _a;
214
+ var total;
215
+ try {
216
+ for (var _b = __values(this.terms), _c = _b.next(); !_c.done; _c = _b.next()) {
217
+ var term = _c.value;
218
+ total = total ? factory_1.f.binary(total, typescript_1.default.SyntaxKind.PlusToken, term) : term;
219
+ }
220
+ }
221
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
222
+ finally {
223
+ try {
224
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
225
+ }
226
+ finally { if (e_1) throw e_1.error; }
227
+ }
228
+ return total ? add(total, this.constant) : num(this.constant);
229
+ };
230
+ return Sum;
231
+ }());
232
+ /** `left + right` with constants folded. */
233
+ function add(left, right) {
234
+ if (typeof right === "number") {
235
+ if (right === 0)
236
+ return left;
237
+ if (factory_1.f.is.number(left))
238
+ return num(Number(left.text) + right);
239
+ return factory_1.f.binary(left, typescript_1.default.SyntaxKind.PlusToken, num(right));
240
+ }
241
+ if (factory_1.f.is.number(left) && Number(left.text) === 0)
242
+ return right;
243
+ if (factory_1.f.is.number(right))
244
+ return add(left, Number(right.text));
245
+ return factory_1.f.binary(left, typescript_1.default.SyntaxKind.PlusToken, right);
246
+ }
247
+ var T;
248
+ (function (T) {
249
+ T.unknown = function () { return factory_1.f.keywordType(typescript_1.default.SyntaxKind.UnknownKeyword); };
250
+ T.number = function () { return factory_1.f.keywordType(typescript_1.default.SyntaxKind.NumberKeyword); };
251
+ T.string = function () { return factory_1.f.keywordType(typescript_1.default.SyntaxKind.StringKeyword); };
252
+ T.buffer = function () { return factory_1.f.referenceType("buffer"); };
253
+ T.defined = function () { return factory_1.f.referenceType("defined"); };
254
+ T.blobs = function () { return factory_1.f.referenceType("Array", [T.defined()]); };
255
+ T.array = function () { return factory_1.f.referenceType("Array", [T.unknown()]); };
256
+ T.record = function () { return factory_1.f.referenceType("Record", [T.string(), T.unknown()]); };
257
+ T.map = function () { return factory_1.f.referenceType("Map", [T.unknown(), T.unknown()]); };
258
+ T.set = function () { return factory_1.f.referenceType("Set", [T.defined()]); };
259
+ T.enumItem = function () { return factory_1.f.referenceType("EnumItem"); };
260
+ T.tuple = function (elements) { return factory_1.f.referenceType("LuaTuple", [factory_1.f.tupleType(elements)]); };
261
+ T.fn = function (parameters, result) {
262
+ return factory_1.f.functionType(parameters.map(function (_a) {
263
+ var _b = __read(_a, 2), name = _b[0], type = _b[1];
264
+ return factory_1.f.parameterDeclaration(name, type);
265
+ }), result);
266
+ };
267
+ })(T || (T = {}));
268
+ // --- entry points -----------------------------------------------------------------------------------
269
+ /**
270
+ * One generator per file: every intrinsic and call site in it shares the hoisted helpers (a named
271
+ * type's functions, guards, literal and enum tables), which land at file scope ahead of the root
272
+ * statement that first needed them.
273
+ */
274
+ var generators = new WeakMap();
275
+ function generatorFor(state, node, file) {
276
+ var generator = generators.get(file);
277
+ if (!generator) {
278
+ generator = createSerializerGenerator(state, file, node);
279
+ generators.set(file, generator);
280
+ }
281
+ generator.use(node);
282
+ return generator;
283
+ }
284
+ function emitHoisted(state, generator) {
285
+ var _a;
286
+ (_a = state.nextRootStatements).push.apply(_a, __spreadArray([], __read(generator.takeHoisted()), false));
287
+ }
288
+ /** `Flamework.createSerializer<T>()`: a `{ serialize, deserialize }` pair for one value. */
289
+ function buildSerializerFromType(state, node, type, file) {
290
+ var _a;
291
+ if (file === void 0) { file = state.getSourceFile(node); }
292
+ var generator = generatorFor(state, node, file);
293
+ // The type argument as written is where the unions in it get their member order from.
294
+ var written = typescript_1.default.isCallExpression(node) ? (_a = node.typeArguments) === null || _a === void 0 ? void 0 : _a[0] : undefined;
295
+ var serializer = generator.buildSerializer(generator.spell(written, unwrapPromise(state, type)));
296
+ emitHoisted(state, generator);
297
+ // roblox-ts type-checks the transformed file. The generated functions are typed loosely inside
298
+ // (`unknown` values with casts); the macro's own return type is what users see.
299
+ return factory_1.f.asNever(serializer);
300
+ }
301
+ /**
302
+ * Networking: the decoder for an argument list, given its tuple type: `(payload, blobs) => values`.
303
+ * Promise elements are unwrapped, since a function's resolved value is what crosses the network.
304
+ * There is no encoder counterpart as a value; see {@link buildInlineEncoding}. A list that carries
305
+ * nothing (no elements, or only `void` ones) gets `undefined`: the runtime then passes the (empty)
306
+ * argument list through, and the call sites send no payload at all.
307
+ */
308
+ function buildDecoderFromType(state, node, type, file) {
309
+ if (file === void 0) { file = state.getSourceFile(node); }
310
+ var generator = generatorFor(state, node, file);
311
+ var decoder = generator.buildDecoder(type);
312
+ emitHoisted(state, generator);
313
+ if (!decoder)
314
+ return factory_1.f.nil();
315
+ // A block-bodied arrow cannot be followed by `as` without parentheses.
316
+ return factory_1.f.asNever(factory.createParenthesizedExpression(decoder));
317
+ }
318
+ /**
319
+ * Packs an argument list where it is sent. `values` are the call's arguments for the tuple's
320
+ * elements (a missing optional is `undefined`, extra ones feed the rest element), or the table that
321
+ * holds them when a spread argument makes their number unknown. Argument expressions must be
322
+ * identifiers or literals: they are read more than once.
323
+ */
324
+ function buildInlineEncoding(state, node, type, values, file) {
325
+ if (file === void 0) { file = state.getSourceFile(node); }
326
+ var generator = generatorFor(state, node, file);
327
+ var encoding = generator.encodeList(type, values);
328
+ emitHoisted(state, generator);
329
+ return encoding;
330
+ }
331
+ /**
332
+ * Networking: the decoder for the result of a function type, carried as a one-element list. The
333
+ * function type, rather than its return type, so that a union written in the return type gets its
334
+ * member order from the declaration.
335
+ */
336
+ function buildResultDecoderFromType(state, node, fn, file) {
337
+ if (file === void 0) { file = state.getSourceFile(node); }
338
+ var generator = generatorFor(state, node, file);
339
+ var decoder = generator.buildDecoder(resultOf(state, generator, fn, node));
340
+ emitHoisted(state, generator);
341
+ if (!decoder)
342
+ return factory_1.f.nil();
343
+ return factory_1.f.asNever(factory.createParenthesizedExpression(decoder));
344
+ }
345
+ /**
346
+ * Packs a function's result as a one-element list; see {@link buildResultDecoderFromType}. `value`
347
+ * is flagged as a parameter when it is one, so the generated code copies it before any macro sees it.
348
+ */
349
+ function buildInlineResultEncoding(state, node, fn, value, isParameter, file) {
350
+ if (file === void 0) { file = state.getSourceFile(node); }
351
+ var generator = generatorFor(state, node, file);
352
+ if (isParameter)
353
+ generator.markParameter(value);
354
+ var encoding = generator.encodeList(resultOf(state, generator, fn, node), [value]);
355
+ emitHoisted(state, generator);
356
+ return encoding;
357
+ }
358
+ /** The one-element list a function's (resolved) result travels as, shaped by its declared return type node. */
359
+ function resultOf(state, generator, fn, node) {
360
+ var _a;
361
+ var signature = fn.getCallSignatures()[0];
362
+ if (!signature) {
363
+ diagnostics_1.Diagnostics.error(node, "Flamework expected a function type here, got '".concat(state.typeChecker.typeToString(fn), "'."));
364
+ }
365
+ var returnType = unwrapPromise(state, signature.getReturnType());
366
+ return { kind: "list", elements: [generator.spell((_a = signature.getDeclaration()) === null || _a === void 0 ? void 0 : _a.type, returnType)] };
367
+ }
368
+ /** Unwraps `Promise<T>` to `T`. */
369
+ function unwrapPromise(state, type) {
370
+ var _a;
371
+ var promiseSymbol = state.typeChecker.resolveName("Promise", undefined, typescript_1.default.SymbolFlags.Type, false);
372
+ if (promiseSymbol !== undefined && type.getSymbol() === promiseSymbol) {
373
+ return (_a = state.typeChecker.getTypeArguments(type)[0]) !== null && _a !== void 0 ? _a : type;
374
+ }
375
+ return type;
376
+ }
377
+ // --- generator --------------------------------------------------------------------------------------
378
+ function createSerializerGenerator(state, file, initialNode) {
379
+ var typeChecker = state.typeChecker;
380
+ var diagnosticNode = initialNode;
381
+ var resolve = function (name) { return typeChecker.resolveName(name, undefined, typescript_1.default.SymbolFlags.Type, false); };
382
+ var kinds = new Map();
383
+ var layouts = new Map();
384
+ var visiting = new Set();
385
+ var provisional = 0;
386
+ var trail = new Array();
387
+ /** Forward declarations of hoisted functions, then everything they refer to, then their bodies. */
388
+ var declarations = new Array();
389
+ var tables = new Array();
390
+ var definitions = new Array();
391
+ var emitted = [0, 0, 0];
392
+ var hoisted = new Map();
393
+ var guards = new Map();
394
+ var enumTables = new Map();
395
+ var literalTables = new Map();
396
+ var discriminants = new Map();
397
+ /** A union's members as alternatives, shared by every spelling of it so a literal group is one table. */
398
+ var unionAlternatives = new Map();
399
+ var varint;
400
+ /** The per-file tally of zero-size elements the payload being decoded has announced; see `readCount`. */
401
+ var zeros;
402
+ /**
403
+ * Parameters of the generated functions. roblox-ts copies a parameter into a temporary wherever one
404
+ * of its macros (`typeIs`, `Map.get`, `Array.push`) takes it, and then everything after it in the
405
+ * call too; a `const` copy of our own passes straight through, so {@link bind} makes one.
406
+ */
407
+ var parameters = new Set();
408
+ return { buildSerializer: buildSerializer, buildDecoder: buildDecoder, encodeList: encodeList, use: use, spell: spell, markParameter: markParameter, takeHoisted: takeHoisted };
409
+ /** A fresh identifier declared as a parameter of a generated function. */
410
+ function parameter(hint) {
411
+ var id = uid(hint);
412
+ parameters.add(id);
413
+ return id;
414
+ }
415
+ /** Marks an identifier declared as a parameter by the caller; see {@link bind}. */
416
+ function markParameter(id) {
417
+ parameters.add(id);
418
+ }
419
+ /** Whether an identifier from user code names a parameter, which roblox-ts treats as mutable. */
420
+ function isParameterReference(id) {
421
+ var _a;
422
+ if (parameters.has(id))
423
+ return true;
424
+ var declaration = (_a = typeChecker.getSymbolAtLocation(id)) === null || _a === void 0 ? void 0 : _a.valueDeclaration;
425
+ return declaration !== undefined && typescript_1.default.isParameter(declaration);
426
+ }
427
+ /** Points diagnostics at the intrinsic or call site being built. */
428
+ function use(node) {
429
+ diagnosticNode = node;
430
+ }
431
+ /** Hoisted statements added since the last call, in an order that keeps every reference in scope. */
432
+ function takeHoisted() {
433
+ var statements = __spreadArray(__spreadArray(__spreadArray([], __read(declarations.slice(emitted[0])), false), __read(tables.slice(emitted[1])), false), __read(definitions.slice(emitted[2])), false);
434
+ emitted = [declarations.length, tables.length, definitions.length];
435
+ return statements;
436
+ }
437
+ function fail(message) {
438
+ var chain = trail.map(function (type) { return typeChecker.typeToString(type); }).join(" > ");
439
+ var lines = ["Flamework cannot serialize this type: ".concat(message, ".")];
440
+ if (chain !== "")
441
+ lines.push("Reached through: ".concat(chain));
442
+ return diagnostics_1.Diagnostics.error.apply(diagnostics_1.Diagnostics, __spreadArray([diagnosticNode], __read(lines), false));
443
+ }
444
+ // --- top level -----------------------------------------------------------------------------------
445
+ function buildSerializer(type) {
446
+ var layout = layoutOf(type);
447
+ var value = parameter("v");
448
+ var serialize = factory_1.f.arrowFunction(factory_1.f.block(encodeBody(type, layout, value)), [
449
+ factory_1.f.parameterDeclaration(value, T.unknown()),
450
+ ]);
451
+ var buf = uid("buf");
452
+ var blobs = layout.blobs ? uid("blobs") : undefined;
453
+ var body = new Array();
454
+ var result = decodeBody(type, layout, buf, blobs, body);
455
+ body.push(factory_1.f.returnStatement(result));
456
+ var deserialize = factory_1.f.arrowFunction(factory_1.f.block(body), blobs
457
+ ? [factory_1.f.parameterDeclaration(buf, T.buffer()), factory_1.f.parameterDeclaration(blobs, T.blobs())]
458
+ : [factory_1.f.parameterDeclaration(buf, T.buffer())]);
459
+ return factory_1.f.object([
460
+ factory_1.f.propertyAssignmentDeclaration("serialize", serialize),
461
+ factory_1.f.propertyAssignmentDeclaration("deserialize", deserialize),
462
+ ]);
463
+ }
464
+ function buildDecoder(type) {
465
+ var list = isKind(type) ? type : listOf(type);
466
+ if (carriesNothing(list))
467
+ return;
468
+ var layout = layoutOf(list);
469
+ var buf = uid("buf");
470
+ var blobs = layout.blobs ? uid("blobs") : undefined;
471
+ var body = new Array();
472
+ var result = decodeBody(list, layout, buf, blobs, body);
473
+ body.push(factory_1.f.returnStatement(result));
474
+ return factory_1.f.arrowFunction(factory_1.f.block(body), blobs
475
+ ? [factory_1.f.parameterDeclaration(buf, T.buffer()), factory_1.f.parameterDeclaration(blobs, T.blobs())]
476
+ : [factory_1.f.parameterDeclaration(buf, T.buffer())]);
477
+ }
478
+ function encodeList(type, values) {
479
+ return encodeElements(isKind(type) ? type : listOf(type), values);
480
+ }
481
+ /** A list with nothing to carry: no elements, or only `void` ones. Such a list sends no payload. */
482
+ function carriesNothing(list) {
483
+ return !list.rest && list.elements.every(function (element) { return describe(element).kind === "nothing"; });
484
+ }
485
+ /**
486
+ * Packs a list whose values are known one by one, so a static count of rest values and absent
487
+ * optionals fold into the layout: a call with only fixed-size arguments gets a constant buffer size.
488
+ */
489
+ function encodeElements(list, values) {
490
+ var e_2, _a, e_3, _b, e_4, _c;
491
+ if (carriesNothing(list))
492
+ return { statements: [], payload: undefined, blobs: undefined };
493
+ var layout = layoutOf(list);
494
+ var statements = new Array();
495
+ if (!Array.isArray(values)) {
496
+ var _d = encodeInto(list, layout, values.table, statements), buf_1 = _d.buf, blobs_1 = _d.blobs;
497
+ return { statements: statements, payload: buf_1, blobs: blobs_1 };
498
+ }
499
+ var elementValue = function (index) { var _a; return (_a = values[index]) !== null && _a !== void 0 ? _a : factory_1.f.nil(); };
500
+ var rest = values.slice(list.elements.length);
501
+ if (rest.length > 0 && !list.rest)
502
+ fail("more arguments than the list has elements");
503
+ // The rest count is known here, so its varint is a constant: literal bytes, no helper call.
504
+ var countBytes = staticVarint(rest.length);
505
+ var total = new Sum();
506
+ list.elements.forEach(function (element, index) { return total.add(emitSize(element, elementValue(index), statements)); });
507
+ if (list.rest) {
508
+ total.add(countBytes.length);
509
+ try {
510
+ for (var rest_1 = __values(rest), rest_1_1 = rest_1.next(); !rest_1_1.done; rest_1_1 = rest_1.next()) {
511
+ var value = rest_1_1.value;
512
+ total.add(emitSize(list.rest, value, statements));
513
+ }
514
+ }
515
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
516
+ finally {
517
+ try {
518
+ if (rest_1_1 && !rest_1_1.done && (_a = rest_1.return)) _a.call(rest_1);
519
+ }
520
+ finally { if (e_2) throw e_2.error; }
521
+ }
522
+ }
523
+ var size = total.build();
524
+ var buf = uid("buf");
525
+ statements.push(constDecl(buf, bufferCall("create", [size])));
526
+ var blobs = layout.blobs ? uid("blobs") : undefined;
527
+ if (blobs)
528
+ statements.push(constDecl(blobs, construct("Array", []), T.blobs()));
529
+ var variable = factory_1.f.is.number(size) ? undefined : uid("o");
530
+ if (variable)
531
+ statements.push(letDecl(variable, num(0)));
532
+ var ctx = { buf: buf, blobs: blobs, cursor: { variable: variable, base: variable, offset: 0 }, out: statements };
533
+ list.elements.forEach(function (element, index) { return emitWrite(element, elementValue(index), ctx); });
534
+ if (list.rest) {
535
+ try {
536
+ for (var countBytes_1 = __values(countBytes), countBytes_1_1 = countBytes_1.next(); !countBytes_1_1.done; countBytes_1_1 = countBytes_1.next()) {
537
+ var byte = countBytes_1_1.value;
538
+ ctx.out.push(factory_1.f.statement(bufferCall("writeu8", [buf, at(ctx), num(byte)])));
539
+ ctx.cursor.offset += 1;
540
+ }
541
+ }
542
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
543
+ finally {
544
+ try {
545
+ if (countBytes_1_1 && !countBytes_1_1.done && (_b = countBytes_1.return)) _b.call(countBytes_1);
546
+ }
547
+ finally { if (e_3) throw e_3.error; }
548
+ }
549
+ try {
550
+ for (var rest_2 = __values(rest), rest_2_1 = rest_2.next(); !rest_2_1.done; rest_2_1 = rest_2.next()) {
551
+ var value = rest_2_1.value;
552
+ emitWrite(list.rest, value, ctx);
553
+ }
554
+ }
555
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
556
+ finally {
557
+ try {
558
+ if (rest_2_1 && !rest_2_1.done && (_c = rest_2.return)) _c.call(rest_2);
559
+ }
560
+ finally { if (e_4) throw e_4.error; }
561
+ }
562
+ }
563
+ return { statements: statements, payload: buf, blobs: blobs };
564
+ }
565
+ /** The bytes of a varint known at compile time. */
566
+ function staticVarint(n) {
567
+ var bytes = new Array();
568
+ while (n >= 128) {
569
+ bytes.push((n % 128) + 128);
570
+ n = Math.floor(n / 128);
571
+ }
572
+ bytes.push(n);
573
+ return bytes;
574
+ }
575
+ /**
576
+ * The argument list a tuple type describes, with Promise elements unwrapped. Each element is shaped
577
+ * by where it was written: the tuple node when there is one, else the parameter or member it was
578
+ * declared with (`Parameters<F>` keeps those).
579
+ */
580
+ function listOf(type, node) {
581
+ var _a;
582
+ if (!(0, isTupleType_1.isTupleType)(state, type)) {
583
+ return { kind: "list", elements: [unwrapPromise(state, type)] };
584
+ }
585
+ var elements = new Array();
586
+ var rest;
587
+ var types = typeChecker.getTypeArguments(type);
588
+ for (var i = 0; i < types.length; i++) {
589
+ var element = unwrapPromise(state, types[i]);
590
+ var flags = type.target.elementFlags[i];
591
+ var isRest = (flags & typescript_1.default.ElementFlags.Rest) !== 0;
592
+ var written = node
593
+ ? tupleElementNode(node.elements[i])
594
+ : declaredTypeNode((_a = type.target.labeledElementDeclarations) === null || _a === void 0 ? void 0 : _a[i]);
595
+ var shape = spellElement(written, element, isRest);
596
+ if (isRest) {
597
+ rest = shape;
598
+ }
599
+ else if (flags & typescript_1.default.ElementFlags.Optional && !hasUndefined(element)) {
600
+ elements.push({ kind: "optional", inner: shape });
601
+ }
602
+ else {
603
+ elements.push(shape);
604
+ }
605
+ }
606
+ return { kind: "list", elements: elements, rest: rest };
607
+ }
608
+ /**
609
+ * `const buf = buffer.create(<size>)`, the blob list when the type has blob slots, the writes,
610
+ * and `return buf, blobs`.
611
+ */
612
+ function encodeBody(shape, layout, value) {
613
+ var body = new Array();
614
+ var _a = encodeInto(shape, layout, value, body), buf = _a.buf, blobs = _a.blobs;
615
+ body.push(factory_1.f.returnStatement(blobs ? factory_1.f.call("$tuple", [buf, blobs]) : buf));
616
+ return body;
617
+ }
618
+ /** The size pass, the buffer, the blob list when the type has blob slots, and the writes. */
619
+ function encodeInto(shape, layout, value, body) {
620
+ var size = emitSize(shape, value, body);
621
+ var buf = uid("buf");
622
+ body.push(constDecl(buf, bufferCall("create", [size])));
623
+ var blobs = layout.blobs ? uid("blobs") : undefined;
624
+ if (blobs)
625
+ body.push(constDecl(blobs, construct("Array", []), T.blobs()));
626
+ var top = isKind(shape) ? undefined : hoist(shape);
627
+ if (top) {
628
+ body.push(factory_1.f.statement(factory_1.f.call(top.write, blobs ? [buf, num(0), value, blobs] : [buf, num(0), value])));
629
+ }
630
+ else {
631
+ var variable = layout.size === undefined ? uid("o") : undefined;
632
+ if (variable)
633
+ body.push(letDecl(variable, num(0)));
634
+ emitWrite(shape, value, { buf: buf, blobs: blobs, cursor: { variable: variable, base: variable, offset: 0 }, out: body });
635
+ }
636
+ return { buf: buf, blobs: blobs };
637
+ }
638
+ /** The reads, ending with a check that the whole buffer was consumed; returns the value. */
639
+ function decodeBody(shape, layout, buf, blobs, body) {
640
+ var length = bufferCall("len", [buf]);
641
+ // The payload's own tally of zero-size elements starts here; decoding never yields, so one
642
+ // per file is never shared between two payloads.
643
+ if (layout.zeros)
644
+ body.push(assign(zeroTally(), num(0)));
645
+ var top = isKind(shape) ? undefined : hoist(shape);
646
+ if (top) {
647
+ var value = uid("value");
648
+ var end = uid("o");
649
+ body.push(constDecl(factory_1.f.arrayBindingDeclaration([value, end]), factory_1.f.call(top.read, blobs ? [buf, num(0), blobs] : [buf, num(0)])));
650
+ body.push(ifStatement(factory_1.f.binary(end, typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, length), [raise(MALFORMED)]));
651
+ return value;
652
+ }
653
+ var variable = layout.size === undefined ? uid("o") : undefined;
654
+ if (variable)
655
+ body.push(letDecl(variable, num(0)));
656
+ var ctx = { buf: buf, blobs: blobs, cursor: { variable: variable, base: variable, offset: 0 }, out: body };
657
+ var result = emitRead(shape, ctx);
658
+ if (!factory_1.f.is.identifier(result) && !isLiteral(result)) {
659
+ result = bind(body, result, "value");
660
+ }
661
+ if (variable) {
662
+ sync(ctx);
663
+ body.push(ifStatement(factory_1.f.binary(variable, typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, length), [raise(MALFORMED)]));
664
+ }
665
+ else {
666
+ body.push(ifStatement(factory_1.f.binary(length, typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, num(layout.size)), [
667
+ raise(MALFORMED),
668
+ ]));
669
+ }
670
+ return result;
671
+ }
672
+ // --- classification ------------------------------------------------------------------------------
673
+ function describe(shape) {
674
+ if (isKind(shape))
675
+ return shape;
676
+ var kind = kinds.get(shape);
677
+ if (!kind) {
678
+ kind = classify(shape);
679
+ kinds.set(shape, kind);
680
+ }
681
+ return kind;
682
+ }
683
+ function classify(type) {
684
+ if (type.isUnion())
685
+ return classifyUnion(type);
686
+ if ((0, buildGuardFromType_1.isInstanceType)(type))
687
+ return { kind: "blob", typeofName: "Instance" };
688
+ if (type.isIntersection())
689
+ return classifyIntersection(type);
690
+ if ((0, buildGuardFromType_1.isConditionalType)(type)) {
691
+ var branches = [type.resolvedTrueType, type.resolvedFalseType];
692
+ return { kind: "union", alternatives: branches.map(function (branch) { return ({ shape: branch, type: branch }); }) };
693
+ }
694
+ if ((type.flags & typescript_1.default.TypeFlags.TypeVariable) !== 0) {
695
+ // An unconstrained type parameter can be anything, which is what a blob carries.
696
+ var constraint = typeChecker.getBaseConstraintOfType(type);
697
+ return constraint ? describe(constraint) : { kind: "blob" };
698
+ }
699
+ var literals = (0, buildGuardFromType_1.getLiteral)(type);
700
+ if (literals) {
701
+ return literals.length === 1
702
+ ? { kind: "constant", value: literals[0] }
703
+ : { kind: "literals", values: literals };
704
+ }
705
+ if (type.flags & (typescript_1.default.TypeFlags.Void | typescript_1.default.TypeFlags.Undefined | typescript_1.default.TypeFlags.Null))
706
+ return { kind: "nothing" };
707
+ if (type.flags & (typescript_1.default.TypeFlags.Any | typescript_1.default.TypeFlags.Unknown))
708
+ return { kind: "blob" };
709
+ if (type.flags & typescript_1.default.TypeFlags.Never)
710
+ fail("`never` has no values");
711
+ // A template literal (`${string}-id`) or `Uppercase<T>` is a string with a pattern; the bytes are the same.
712
+ if (type.flags & (typescript_1.default.TypeFlags.String | typescript_1.default.TypeFlags.TemplateLiteral | typescript_1.default.TypeFlags.StringMapping)) {
713
+ return { kind: "string", length: "v" };
714
+ }
715
+ if (type.flags & typescript_1.default.TypeFlags.Number)
716
+ return { kind: "number", width: "f64" };
717
+ if (type.flags & typescript_1.default.TypeFlags.BigInt)
718
+ fail("bigint does not exist in Luau");
719
+ if (type.flags & typescript_1.default.TypeFlags.ESSymbolLike)
720
+ fail("symbols cannot be sent");
721
+ if ((0, isTupleType_1.isTupleType)(state, type))
722
+ return listOf(type);
723
+ if ((0, isTupleType_1.isArrayType)(state, type) || typeChecker.isArrayType(type)) {
724
+ var element = typeChecker.getTypeArguments(type)[0];
725
+ if (!element)
726
+ fail("an array without an element type");
727
+ return { kind: "array", element: element };
728
+ }
729
+ if (type.getCallSignatures().length > 0)
730
+ fail("functions cannot be sent");
731
+ // `object`, and whatever else has no declaration behind it, has no structure to write.
732
+ var symbol = type.getSymbol();
733
+ if (!symbol)
734
+ return { kind: "blob" };
735
+ if (symbol === resolve("Map") || symbol === resolve("ReadonlyMap")) {
736
+ var _a = __read(typeChecker.getTypeArguments(type), 2), key = _a[0], value = _a[1];
737
+ if (!key || !value)
738
+ fail("a Map without key and value types");
739
+ return { kind: "map", key: key, value: value };
740
+ }
741
+ if (symbol === resolve("Set") || symbol === resolve("ReadonlySet")) {
742
+ var _b = __read(typeChecker.getTypeArguments(type), 1), element = _b[0];
743
+ if (!element)
744
+ fail("a Set without an element type");
745
+ return { kind: "set", element: element };
746
+ }
747
+ if (symbol === resolve("WeakMap") || symbol === resolve("WeakSet"))
748
+ fail("weak collections cannot be sent");
749
+ if (symbol === resolve("Promise"))
750
+ fail("a Promise cannot be sent; send its resolved value");
751
+ if (symbol === resolve("buffer"))
752
+ return { kind: "buffer", length: "v" };
753
+ var global = symbol === resolve(symbol.name);
754
+ if (global) {
755
+ if (symbol.name === "CFrame")
756
+ return { kind: "cframe" };
757
+ if (DATATYPES[symbol.name] !== undefined)
758
+ return { kind: "datatype", name: symbol.name };
759
+ }
760
+ // `Enum.Material` and friends are interfaces declared under the Enum namespace.
761
+ var enumNamespace = resolve("Enum");
762
+ if (symbol.parent && enumNamespace && typeChecker.getMergedSymbol(symbol.parent) === enumNamespace) {
763
+ return { kind: "enum", name: symbol.name };
764
+ }
765
+ // The rest of the Roblox API (EnumItem, Font, RBXScriptSignal, ...) and anything nominal has no
766
+ // structure a plain table could stand in for; a global's name is also what `typeof` reports.
767
+ if (isRobloxType(symbol))
768
+ return { kind: "blob", typeofName: global ? symbol.name : undefined };
769
+ if (hasNominalMarker(type))
770
+ return { kind: "blob" };
771
+ // A class instance is more than its fields; it travels as a reference.
772
+ if (type.isClass())
773
+ return { kind: "blob" };
774
+ return classifyObject(type);
775
+ }
776
+ /** Declared by roblox-ts's Roblox API types, as opposed to by the project or the TypeScript library. */
777
+ function isRobloxType(symbol) {
778
+ var declarations = symbol === null || symbol === void 0 ? void 0 : symbol.declarations;
779
+ if (!declarations)
780
+ return false;
781
+ return declarations.some(function (declaration) { return ROBLOX_TYPES.test(declaration.getSourceFile().fileName); });
782
+ }
783
+ /** roblox-ts marks its nominal types with a `_nominal_X` property; project code may do the same. */
784
+ function hasNominalMarker(type) {
785
+ return type.getProperties().some(function (property) { return property.name.startsWith("_nominal_"); });
786
+ }
787
+ /** A union as a kind; `node` is the spelling its members are numbered by, else the alias's own. */
788
+ function classifyUnion(type, node) {
789
+ if (type === typeChecker.getBooleanType())
790
+ return { kind: "boolean" };
791
+ var _a = alternativesOf(type), isOptional = _a.isOptional, alternatives = _a.alternatives;
792
+ var inner;
793
+ if (alternatives.length === 0)
794
+ inner = { kind: "nothing" };
795
+ else if (alternatives.length === 1)
796
+ inner = alternatives[0].shape;
797
+ // A one-byte tag numbers at most 256 members; past that the value travels whole.
798
+ else if (alternatives.length > 0xff)
799
+ inner = { kind: "blob" };
800
+ else
801
+ inner = { kind: "union", alternatives: orderAlternatives(alternatives, node !== null && node !== void 0 ? node : aliasNode(type)) };
802
+ if (isOptional)
803
+ return { kind: "optional", inner: inner };
804
+ return describe(inner);
805
+ }
806
+ function alternativesOf(type) {
807
+ var e_5, _a, e_6, _b;
808
+ var entry = unionAlternatives.get(type);
809
+ if (!entry) {
810
+ var _c = (0, buildGuardFromType_1.simplifyUnion)(type), enums = _c.enums, literals = _c.literals, types = _c.types;
811
+ var _d = __read((0, buildGuardFromType_1.extractTypes)(typeChecker, types), 2), isOptional = _d[0], members = _d[1];
812
+ var alternatives = new Array();
813
+ try {
814
+ for (var members_1 = __values(members), members_1_1 = members_1.next(); !members_1_1.done; members_1_1 = members_1.next()) {
815
+ var member = members_1_1.value;
816
+ alternatives.push({ shape: member, type: member });
817
+ }
818
+ }
819
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
820
+ finally {
821
+ try {
822
+ if (members_1_1 && !members_1_1.done && (_a = members_1.return)) _a.call(members_1);
823
+ }
824
+ finally { if (e_5) throw e_5.error; }
825
+ }
826
+ try {
827
+ for (var enums_1 = __values(enums), enums_1_1 = enums_1.next(); !enums_1_1.done; enums_1_1 = enums_1.next()) {
828
+ var name_1 = enums_1_1.value;
829
+ alternatives.push({ shape: { kind: "enum", name: name_1 } });
830
+ }
831
+ }
832
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
833
+ finally {
834
+ try {
835
+ if (enums_1_1 && !enums_1_1.done && (_b = enums_1.return)) _b.call(enums_1);
836
+ }
837
+ finally { if (e_6) throw e_6.error; }
838
+ }
839
+ if (literals.length === 1)
840
+ alternatives.push({ shape: { kind: "constant", value: literals[0] } });
841
+ if (literals.length > 1)
842
+ alternatives.push({ shape: { kind: "literals", values: literals } });
843
+ entry = { isOptional: isOptional, alternatives: alternatives };
844
+ unionAlternatives.set(type, entry);
845
+ }
846
+ return entry;
847
+ }
848
+ /**
849
+ * Union members in the order they were written: `{ Coins } | { Items }` numbers Coins 0 and Items
850
+ * 1. TypeScript lists them by internal id instead, so the order comes from the union's type node:
851
+ * an alias's own declaration, or the spelling the value is reached through (see {@link spell}).
852
+ * Members that node does not account for (a generic alias instantiation, say) keep TypeScript's
853
+ * order after the others; a union with no node at all keeps it throughout, which is the same in
854
+ * every file of a program.
855
+ */
856
+ function orderAlternatives(alternatives, node) {
857
+ var e_7, _a, e_8, _b;
858
+ if (!node)
859
+ return alternatives;
860
+ var positions = new Map();
861
+ try {
862
+ for (var _c = __values(node.types), _d = _c.next(); !_d.done; _d = _c.next()) {
863
+ var member = _d.value;
864
+ var memberType = typeChecker.getTypeFromTypeNode(member);
865
+ try {
866
+ for (var _e = (e_8 = void 0, __values(memberType.isUnion() ? memberType.types : [memberType])), _f = _e.next(); !_f.done; _f = _e.next()) {
867
+ var constituent = _f.value;
868
+ var alternative = alternativeFor(alternatives, constituent);
869
+ if (alternative && !positions.has(alternative))
870
+ positions.set(alternative, positions.size);
871
+ }
872
+ }
873
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
874
+ finally {
875
+ try {
876
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
877
+ }
878
+ finally { if (e_8) throw e_8.error; }
879
+ }
880
+ }
881
+ }
882
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
883
+ finally {
884
+ try {
885
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
886
+ }
887
+ finally { if (e_7) throw e_7.error; }
888
+ }
889
+ var rank = function (alternative) { var _a; return (_a = positions.get(alternative)) !== null && _a !== void 0 ? _a : positions.size + alternatives.indexOf(alternative); };
890
+ return __spreadArray([], __read(alternatives), false).sort(function (a, b) { return rank(a) - rank(b); });
891
+ }
892
+ function aliasNode(type) {
893
+ var _a, _b;
894
+ var declaration = (_b = (_a = type.aliasSymbol) === null || _a === void 0 ? void 0 : _a.declarations) === null || _b === void 0 ? void 0 : _b[0];
895
+ if (declaration && typescript_1.default.isTypeAliasDeclaration(declaration) && typescript_1.default.isUnionTypeNode(declaration.type)) {
896
+ return declaration.type;
897
+ }
898
+ }
899
+ /** The alternative a flattened union constituent belongs to: its own, or the group it was folded into. */
900
+ function alternativeFor(alternatives, constituent) {
901
+ var own = alternatives.find(function (alternative) { return alternative.type === constituent; });
902
+ if (own)
903
+ return own;
904
+ if (constituent.flags & typescript_1.default.TypeFlags.BooleanLiteral) {
905
+ var boolean = alternatives.find(function (alternative) { return alternative.type === typeChecker.getBooleanType(); });
906
+ if (boolean)
907
+ return boolean;
908
+ }
909
+ var enumName = robloxEnumOf(constituent);
910
+ if (enumName !== undefined) {
911
+ var whole = alternatives.find(function (alternative) {
912
+ return isKind(alternative.shape) &&
913
+ alternative.shape.kind === "enum" &&
914
+ alternative.shape.name === enumName;
915
+ });
916
+ if (whole)
917
+ return whole;
918
+ }
919
+ if (enumName !== undefined || (0, buildGuardFromType_1.getLiteral)(constituent, true) !== undefined) {
920
+ return alternatives.find(function (alternative) {
921
+ return isKind(alternative.shape) &&
922
+ (alternative.shape.kind === "constant" || alternative.shape.kind === "literals");
923
+ });
924
+ }
925
+ }
926
+ /** `Material` for `Enum.Material.Plastic`. */
927
+ function robloxEnumOf(type) {
928
+ var symbol = type.getSymbol();
929
+ var enumNamespace = resolve("Enum");
930
+ var group = symbol === null || symbol === void 0 ? void 0 : symbol.parent;
931
+ if ((group === null || group === void 0 ? void 0 : group.parent) && enumNamespace && typeChecker.getMergedSymbol(group.parent) === enumNamespace) {
932
+ return group.name;
933
+ }
934
+ }
935
+ /**
936
+ * The shape of `type` as written at `node`. TypeScript keeps one type for every spelling of an
937
+ * anonymous union -- `string | number` here and `number | string` elsewhere are the same object
938
+ * -- so its member order, which is the wire tag, has to come from the spelling the value is
939
+ * reached through: a union node gives a union kind numbered as written, and the walk goes on
940
+ * into array elements, `Set`, `Map` and `Promise` arguments, tuple elements, `readonly` and
941
+ * parentheses. Where the node adds nothing -- a reference to an alias, whose own declaration
942
+ * decides; a generic's type argument, which is not where the value is reached -- the type
943
+ * itself, whose unions keep TypeScript's order. Either way the sender and the receiver of a
944
+ * value go through the same declaration and number its members the same way.
945
+ */
946
+ function spell(node, type) {
947
+ if (!node)
948
+ return type;
949
+ if (typescript_1.default.isParenthesizedTypeNode(node))
950
+ return spell(node.type, type);
951
+ if (typescript_1.default.isTypeOperatorNode(node) && node.operator === typescript_1.default.SyntaxKind.ReadonlyKeyword) {
952
+ return spell(node.type, type);
953
+ }
954
+ if (typescript_1.default.isUnionTypeNode(node)) {
955
+ return type.isUnion() ? classifyUnion(type, node) : type;
956
+ }
957
+ if (typescript_1.default.isArrayTypeNode(node)) {
958
+ var kind = describe(type);
959
+ if (kind.kind !== "array" || isKind(kind.element))
960
+ return type;
961
+ return { kind: "array", element: spell(node.elementType, kind.element) };
962
+ }
963
+ if (typescript_1.default.isTypeReferenceNode(node) && node.typeArguments) {
964
+ var args = node.typeArguments;
965
+ var written = typeChecker.getTypeFromTypeNode(node);
966
+ // `Promise<T>` in a declaration: what crosses is `T`, so its spelling is the argument.
967
+ if (written !== type) {
968
+ return args.length === 1 && unwrapPromise(state, written) === type ? spell(args[0], type) : type;
969
+ }
970
+ var kind = describe(type);
971
+ if ((kind.kind === "array" || kind.kind === "set") && args.length === 1 && !isKind(kind.element)) {
972
+ return { kind: kind.kind, element: spell(args[0], kind.element) };
973
+ }
974
+ if (kind.kind === "map" && args.length === 2 && !isKind(kind.key) && !isKind(kind.value)) {
975
+ return { kind: "map", key: spell(args[0], kind.key), value: spell(args[1], kind.value) };
976
+ }
977
+ return type;
978
+ }
979
+ if (typescript_1.default.isTupleTypeNode(node) && (0, isTupleType_1.isTupleType)(state, type))
980
+ return listOf(type, node);
981
+ return type;
982
+ }
983
+ /** A tuple element or parameter: a rest element's node is the array, its type the element. */
984
+ function spellElement(node, type, rest) {
985
+ var _a;
986
+ if (!node || !rest)
987
+ return spell(node, type);
988
+ if (typescript_1.default.isArrayTypeNode(node))
989
+ return spell(node.elementType, type);
990
+ if (typescript_1.default.isTypeReferenceNode(node) && ((_a = node.typeArguments) === null || _a === void 0 ? void 0 : _a.length) === 1) {
991
+ return spell(node.typeArguments[0], type);
992
+ }
993
+ return type;
994
+ }
995
+ /** The node a tuple element's type was written in, past its name, `?` or `...`. */
996
+ function tupleElementNode(element) {
997
+ if (!element)
998
+ return;
999
+ if (typescript_1.default.isNamedTupleMember(element) || typescript_1.default.isRestTypeNode(element) || typescript_1.default.isOptionalTypeNode(element)) {
1000
+ return element.type;
1001
+ }
1002
+ return element;
1003
+ }
1004
+ /** A property's, parameter's or tuple member's declared type node, where its unions were written. */
1005
+ function declaredTypeNode(declaration) {
1006
+ if (!declaration)
1007
+ return;
1008
+ if (typescript_1.default.isPropertySignature(declaration) ||
1009
+ typescript_1.default.isPropertyDeclaration(declaration) ||
1010
+ typescript_1.default.isParameter(declaration) ||
1011
+ typescript_1.default.isNamedTupleMember(declaration)) {
1012
+ return declaration.type;
1013
+ }
1014
+ }
1015
+ function classifyIntersection(type) {
1016
+ // `LuaTuple<T>` is `T & { LUA_TUPLE: never }`: several values at runtime, never a table.
1017
+ if (type.types.some(function (member) { return member.getProperty("LUA_TUPLE") !== undefined; })) {
1018
+ fail("a LuaTuple is several values at runtime, not a table; declare a tuple type such as `[A, B]` instead");
1019
+ }
1020
+ var brand = findBrand(type);
1021
+ var disjoint = type.types.find(function (member) { return (member.flags & tsInternals_1.TYPE_FLAG_DISJOINT_DOMAINS) !== 0; });
1022
+ if (disjoint) {
1023
+ if (disjoint.flags & typescript_1.default.TypeFlags.Number) {
1024
+ if (brand === VARINT_BRAND)
1025
+ return { kind: "varint" };
1026
+ return { kind: "number", width: brand && NUMBER_BRANDS.has(brand) ? brand : "f64" };
1027
+ }
1028
+ if (disjoint.flags & typescript_1.default.TypeFlags.String) {
1029
+ return { kind: "string", length: (brand && STRING_BRANDS[brand]) || "v" };
1030
+ }
1031
+ return describe(disjoint);
1032
+ }
1033
+ var bufferSymbol = resolve("buffer");
1034
+ if (type.types.some(function (member) { return member.getSymbol() === bufferSymbol; })) {
1035
+ return { kind: "buffer", length: (brand && BUFFER_BRANDS[brand]) || "v" };
1036
+ }
1037
+ var datatype = type.types.find(function (member) {
1038
+ var _a;
1039
+ var name = (_a = member.getSymbol()) === null || _a === void 0 ? void 0 : _a.name;
1040
+ return (name !== undefined &&
1041
+ (DATATYPES[name] !== undefined || name === "CFrame") &&
1042
+ member.getSymbol() === resolve(name));
1043
+ });
1044
+ if (datatype)
1045
+ return describe(datatype);
1046
+ if (type.types.some(function (member) { return (0, buildGuardFromType_1.isInstanceType)(member); }))
1047
+ return { kind: "blob", typeofName: "Instance" };
1048
+ if (type.types.some(function (member) { return isRobloxType(member.getSymbol()); }) || hasNominalMarker(type)) {
1049
+ return { kind: "blob" };
1050
+ }
1051
+ return classifyObject(type);
1052
+ }
1053
+ /** The literal in `number & { __brand: "u8" }`, whatever the property is called. */
1054
+ function findBrand(type) {
1055
+ var e_9, _a, e_10, _b;
1056
+ try {
1057
+ for (var _c = __values(type.types), _d = _c.next(); !_d.done; _d = _c.next()) {
1058
+ var member = _d.value;
1059
+ if ((member.flags & typescript_1.default.TypeFlags.Object) === 0)
1060
+ continue;
1061
+ try {
1062
+ for (var _e = (e_10 = void 0, __values(member.getProperties())), _f = _e.next(); !_f.done; _f = _e.next()) {
1063
+ var property = _f.value;
1064
+ var propertyType = typeChecker.getTypeOfPropertyOfType(member, property.name);
1065
+ if (propertyType === null || propertyType === void 0 ? void 0 : propertyType.isStringLiteral()) {
1066
+ var brand = propertyType.value;
1067
+ if (NUMBER_BRANDS.has(brand) || brand === VARINT_BRAND)
1068
+ return brand;
1069
+ if (brand in STRING_BRANDS || brand in BUFFER_BRANDS)
1070
+ return brand;
1071
+ }
1072
+ }
1073
+ }
1074
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
1075
+ finally {
1076
+ try {
1077
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
1078
+ }
1079
+ finally { if (e_10) throw e_10.error; }
1080
+ }
1081
+ }
1082
+ }
1083
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
1084
+ finally {
1085
+ try {
1086
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
1087
+ }
1088
+ finally { if (e_9) throw e_9.error; }
1089
+ }
1090
+ }
1091
+ function classifyObject(type) {
1092
+ var indexInfos = typeChecker.getIndexInfosOfType(type);
1093
+ var properties = type.getProperties().filter(function (property) {
1094
+ var propertyType = typeChecker.getTypeOfPropertyOfType(type, property.name);
1095
+ return (propertyType !== undefined &&
1096
+ (propertyType.flags & (typescript_1.default.TypeFlags.Never | typescript_1.default.TypeFlags.UniqueESSymbol)) === 0);
1097
+ });
1098
+ if (properties.length === 0 && indexInfos.length === 0)
1099
+ return { kind: "blob" };
1100
+ if (indexInfos.length > 0) {
1101
+ // Named properties next to an index signature, or several signatures, have no single layout.
1102
+ if (properties.length > 0 || indexInfos.length > 1)
1103
+ return { kind: "blob" };
1104
+ return { kind: "map", key: indexInfos[0].keyType, value: indexInfos[0].type };
1105
+ }
1106
+ // Declaration order, which every compilation of the same source shares.
1107
+ var fields = properties.map(function (property) {
1108
+ var propertyType = typeChecker.getTypeOfPropertyOfType(type, property.name);
1109
+ if (propertyType.getCallSignatures().length > 0)
1110
+ fail("property '".concat(property.name, "' is a function"));
1111
+ var written = spell(declaredTypeNode(property.valueDeclaration), propertyType);
1112
+ var optional = (property.flags & typescript_1.default.SymbolFlags.Optional) !== 0 && !hasUndefined(propertyType);
1113
+ var shape = optional ? { kind: "optional", inner: written } : written;
1114
+ return { name: property.name, shape: shape };
1115
+ });
1116
+ return { kind: "object", fields: fields };
1117
+ }
1118
+ function hasUndefined(type) {
1119
+ return (type.isUnion() &&
1120
+ type.types.some(function (member) { return (member.flags & (typescript_1.default.TypeFlags.Undefined | typescript_1.default.TypeFlags.Void)) !== 0; }));
1121
+ }
1122
+ // --- layout --------------------------------------------------------------------------------------
1123
+ function layoutOf(shape) {
1124
+ var memo = layouts.get(shape);
1125
+ if (memo)
1126
+ return memo;
1127
+ // A type reached again while it is being measured: its size is not fixed, whatever it is.
1128
+ if (visiting.has(shape)) {
1129
+ provisional += 1;
1130
+ return { size: undefined, min: 0, blobs: false, zeros: false };
1131
+ }
1132
+ visiting.add(shape);
1133
+ var before = provisional;
1134
+ var layout = computeLayout(shape);
1135
+ visiting.delete(shape);
1136
+ // A result that saw a cycle is only final for the type that closes it, at the top of the chain.
1137
+ if (provisional === before || visiting.size === 0)
1138
+ layouts.set(shape, layout);
1139
+ if (visiting.size === 0)
1140
+ provisional = 0;
1141
+ return layout;
1142
+ }
1143
+ function computeLayout(shape) {
1144
+ if (!isKind(shape)) {
1145
+ trail.push(shape);
1146
+ var layout = layoutOf(describe(shape));
1147
+ trail.pop();
1148
+ return layout;
1149
+ }
1150
+ var fixed = function (size, blobs) {
1151
+ if (blobs === void 0) { blobs = false; }
1152
+ return ({ size: size, min: size, blobs: blobs, zeros: false });
1153
+ };
1154
+ var kind = shape;
1155
+ switch (kind.kind) {
1156
+ case "number":
1157
+ return fixed(WIDTH_SIZE[kind.width]);
1158
+ case "varint":
1159
+ return { size: undefined, min: 1, blobs: false, zeros: false };
1160
+ case "boolean":
1161
+ return fixed(1);
1162
+ case "string":
1163
+ case "buffer":
1164
+ return { size: undefined, min: lengthMin(kind.length), blobs: false, zeros: false };
1165
+ case "constant":
1166
+ case "nothing":
1167
+ return fixed(0);
1168
+ case "literals":
1169
+ return fixed(kind.values.length > 0xff ? 2 : 1);
1170
+ case "blob":
1171
+ return fixed(BLOB_SIZE, true);
1172
+ case "datatype":
1173
+ return fixed(DATATYPES[kind.name].reduce(function (total, _a) {
1174
+ var _b = __read(_a, 1), width = _b[0];
1175
+ return total + WIDTH_SIZE[width];
1176
+ }, 0));
1177
+ case "cframe":
1178
+ return fixed(CFRAME_COMPONENTS * 4);
1179
+ case "enum":
1180
+ return fixed(2);
1181
+ case "optional": {
1182
+ var inner = layoutOf(kind.inner);
1183
+ return { size: undefined, min: 1, blobs: inner.blobs, zeros: inner.zeros };
1184
+ }
1185
+ case "array":
1186
+ case "set": {
1187
+ var element = layoutOf(kind.element);
1188
+ // A zero-size element is a fixed size of 0; a type still being measured reports `min` 0 too.
1189
+ return { size: undefined, min: 1, blobs: element.blobs, zeros: element.size === 0 || element.zeros };
1190
+ }
1191
+ case "map": {
1192
+ var key = layoutOf(kind.key);
1193
+ var value = layoutOf(kind.value);
1194
+ return {
1195
+ size: undefined,
1196
+ min: 1,
1197
+ blobs: key.blobs || value.blobs,
1198
+ zeros: (key.size === 0 && value.size === 0) || key.zeros || value.zeros,
1199
+ };
1200
+ }
1201
+ case "list": {
1202
+ var layout = sumLayouts(kind.elements.map(function (element) { return layoutOf(element); }));
1203
+ if (kind.rest) {
1204
+ var rest = layoutOf(kind.rest);
1205
+ return {
1206
+ size: undefined,
1207
+ min: layout.min + 1,
1208
+ blobs: layout.blobs || rest.blobs,
1209
+ zeros: layout.zeros || rest.size === 0 || rest.zeros,
1210
+ };
1211
+ }
1212
+ return layout;
1213
+ }
1214
+ case "object":
1215
+ return sumLayouts(kind.fields.map(function (field) { return layoutOf(field.shape); }));
1216
+ case "union": {
1217
+ var members = kind.alternatives.map(function (alternative) { return layoutOf(alternative.shape); });
1218
+ var sizes = new Set(members.map(function (member) { return member.size; }));
1219
+ var size = sizes.size === 1 && !sizes.has(undefined) ? 1 + members[0].size : undefined;
1220
+ return {
1221
+ size: size,
1222
+ min: 1 + Math.min.apply(Math, __spreadArray([], __read(members.map(function (member) { return member.min; })), false)),
1223
+ blobs: members.some(function (member) { return member.blobs; }),
1224
+ zeros: members.some(function (member) { return member.zeros; }),
1225
+ };
1226
+ }
1227
+ }
1228
+ }
1229
+ function sumLayouts(layouts) {
1230
+ var e_11, _a;
1231
+ var size = 0;
1232
+ var min = 0;
1233
+ var blobs = false;
1234
+ var zeros = false;
1235
+ try {
1236
+ for (var layouts_1 = __values(layouts), layouts_1_1 = layouts_1.next(); !layouts_1_1.done; layouts_1_1 = layouts_1.next()) {
1237
+ var layout = layouts_1_1.value;
1238
+ size = size === undefined || layout.size === undefined ? undefined : size + layout.size;
1239
+ min += layout.min;
1240
+ blobs || (blobs = layout.blobs);
1241
+ zeros || (zeros = layout.zeros);
1242
+ }
1243
+ }
1244
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
1245
+ finally {
1246
+ try {
1247
+ if (layouts_1_1 && !layouts_1_1.done && (_a = layouts_1.return)) _a.call(layouts_1);
1248
+ }
1249
+ finally { if (e_11) throw e_11.error; }
1250
+ }
1251
+ return { size: size, min: min, blobs: blobs, zeros: zeros };
1252
+ }
1253
+ // --- hoisting ------------------------------------------------------------------------------------
1254
+ /** Named object-like types with a variable size get their own functions; everything else inlines. */
1255
+ function hoist(type) {
1256
+ var existing = hoisted.get(type);
1257
+ if (existing)
1258
+ return existing;
1259
+ var name = hoistName(type);
1260
+ if (name === undefined)
1261
+ return;
1262
+ var layout = layoutOf(type);
1263
+ if (layout.size !== undefined)
1264
+ return;
1265
+ // Only structured types are worth a function; a named alias of a string or an array inlines.
1266
+ var structure = describe(type).kind;
1267
+ if (structure !== "object" && structure !== "union" && structure !== "list")
1268
+ return;
1269
+ var info = { size: uid("s_".concat(name)), write: uid("w_".concat(name)), read: uid("r_".concat(name)), layout: layout };
1270
+ hoisted.set(type, info);
1271
+ var withBlobs = function (list, entry) { return (layout.blobs ? __spreadArray(__spreadArray([], __read(list), false), [entry], false) : list); };
1272
+ var blobsParameter = ["blobs", T.blobs()];
1273
+ var writeParameters = [
1274
+ ["buf", T.buffer()],
1275
+ ["o", T.number()],
1276
+ ["v", T.unknown()],
1277
+ ];
1278
+ var readParameters = [
1279
+ ["buf", T.buffer()],
1280
+ ["o", T.number()],
1281
+ ];
1282
+ declarations.push(letDecl(info.size, undefined, T.fn([["v", T.unknown()]], T.number())));
1283
+ declarations.push(letDecl(info.write, undefined, T.fn(withBlobs(writeParameters, blobsParameter), T.number())));
1284
+ declarations.push(letDecl(info.read, undefined, T.fn(withBlobs(readParameters, blobsParameter), T.tuple([T.unknown(), T.number()]))));
1285
+ // The bodies are built after the identifiers exist, which is what lets a type refer to itself.
1286
+ var kind = describe(type);
1287
+ trail.push(type);
1288
+ var value = parameter("v");
1289
+ var sizeBody = new Array();
1290
+ var size = emitSize(kind, value, sizeBody);
1291
+ sizeBody.push(factory_1.f.returnStatement(size));
1292
+ definitions.push(assign(info.size, factory_1.f.arrowFunction(factory_1.f.block(sizeBody), [factory_1.f.parameterDeclaration(value)])));
1293
+ var buf = uid("buf");
1294
+ var o = uid("o");
1295
+ var blobs = layout.blobs ? uid("blobs") : undefined;
1296
+ var parameters = function () {
1297
+ var names = [];
1298
+ for (var _i = 0; _i < arguments.length; _i++) {
1299
+ names[_i] = arguments[_i];
1300
+ }
1301
+ return names.map(function (id) { return factory_1.f.parameterDeclaration(id); });
1302
+ };
1303
+ var writeBody = new Array();
1304
+ var writeCtx = { buf: buf, blobs: blobs, cursor: { variable: o, base: o, offset: 0 }, out: writeBody };
1305
+ emitWrite(kind, value, writeCtx);
1306
+ sync(writeCtx);
1307
+ writeBody.push(factory_1.f.returnStatement(o));
1308
+ definitions.push(assign(info.write, factory_1.f.arrowFunction(factory_1.f.block(writeBody), parameters.apply(void 0, __spreadArray([], __read(withBlobs([buf, o, value], blobs)), false)))));
1309
+ var readBody = new Array();
1310
+ var readCtx = { buf: buf, blobs: blobs, cursor: { variable: o, base: o, offset: 0 }, out: readBody };
1311
+ var result = emitRead(kind, readCtx);
1312
+ var bound = factory_1.f.is.identifier(result) || isLiteral(result) ? result : bind(readBody, result, "value");
1313
+ sync(readCtx);
1314
+ readBody.push(factory_1.f.returnStatement(factory_1.f.call("$tuple", [bound, o])));
1315
+ definitions.push(assign(info.read, factory_1.f.arrowFunction(factory_1.f.block(readBody), parameters.apply(void 0, __spreadArray([], __read(withBlobs([buf, o], blobs)), false)))));
1316
+ trail.pop();
1317
+ return info;
1318
+ }
1319
+ function hoistName(type) {
1320
+ var _a, _b, _c;
1321
+ if ((type.flags & (typescript_1.default.TypeFlags.Object | typescript_1.default.TypeFlags.UnionOrIntersection)) === 0)
1322
+ return;
1323
+ if ((0, buildGuardFromType_1.isInstanceType)(type) || (0, buildGuardFromType_1.getLiteral)(type) !== undefined)
1324
+ return;
1325
+ var symbolName = (_b = (_a = type.aliasSymbol) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : (_c = type.symbol) === null || _c === void 0 ? void 0 : _c.name;
1326
+ if (symbolName === undefined || symbolName === "__type" || symbolName === "__object")
1327
+ return;
1328
+ if (symbolName === "Array" || symbolName === "ReadonlyArray")
1329
+ return;
1330
+ if (DATATYPES[symbolName] !== undefined || symbolName === "CFrame")
1331
+ return;
1332
+ return symbolName.replace(/\W/g, "_");
1333
+ }
1334
+ function guardFor(type) {
1335
+ var guard = guards.get(type);
1336
+ if (!guard) {
1337
+ guard = uid("guard");
1338
+ tables.push(constDecl(guard, (0, buildGuardFromType_1.buildGuardFromType)(state, diagnosticNode, type, file)));
1339
+ guards.set(type, guard);
1340
+ }
1341
+ return guard;
1342
+ }
1343
+ /** `Enum.X` items by `Value`, built once: values are not always below 256, so they are sent as u16. */
1344
+ function enumTableFor(name) {
1345
+ var table = enumTables.get(name);
1346
+ if (!table) {
1347
+ table = uid("enum_".concat(name));
1348
+ var item = uid("item");
1349
+ tables.push(constDecl(table, construct("Map", []), factory_1.f.referenceType("Map", [T.number(), T.enumItem()])), forOf(item, factory_1.f.call(prop(prop("Enum", name), "GetEnumItems"), []), [
1350
+ factory_1.f.statement(factory_1.f.call(prop(table, "set"), [prop(item, "Value"), item])),
1351
+ ]));
1352
+ enumTables.set(name, table);
1353
+ }
1354
+ return table;
1355
+ }
1356
+ /** A literal union's members as a list (decode) and as a lookup from member to index (encode). */
1357
+ function literalTablesFor(kind) {
1358
+ var entry = literalTables.get(kind);
1359
+ if (!entry) {
1360
+ entry = { list: uid("literals"), index: uid("literalIndex") };
1361
+ // The key type is spelled out: inferred from the pairs, TypeScript would pick the first literal's
1362
+ // type (`"lit"`, or one EnumItem interface) and reject the others.
1363
+ var pairs = kind.values.map(function (value, index) { return factory_1.f.array([value, num(index)], false); });
1364
+ tables.push(constDecl(entry.list, factory_1.f.array(kind.values, false), T.blobs()), constDecl(entry.index, construct("Map", [factory_1.f.array(pairs)], [T.defined(), T.number()]), factory_1.f.referenceType("Map", [T.defined(), T.number()])));
1365
+ literalTables.set(kind, entry);
1366
+ }
1367
+ return entry;
1368
+ }
1369
+ // --- varints -------------------------------------------------------------------------------------
1370
+ /**
1371
+ * The LEB128 helpers, hoisted once per file: seven bits per byte, low bits first, the high bit set
1372
+ * on every byte but the last. Reading gives up after five bytes, so a hostile buffer cannot loop.
1373
+ */
1374
+ function varintHelpers() {
1375
+ if (varint)
1376
+ return varint;
1377
+ varint = { size: uid("vsize"), write: uid("vwrite"), read: uid("vread") };
1378
+ var below = function (value, limit) { return factory_1.f.binary(value, typescript_1.default.SyntaxKind.LessThanToken, num(limit)); };
1379
+ var parameter = function (id, type) { return factory_1.f.parameterDeclaration(id, type); };
1380
+ // vsize: `n < 128 ? 1 : n < 16384 ? 2 : ... : 5`
1381
+ var sizeOf = uid("n");
1382
+ var size = num(VARINT_MAX_BYTES);
1383
+ for (var bytes = VARINT_MAX_BYTES - 1; bytes >= 1; bytes--) {
1384
+ size = conditional(below(sizeOf, Math.pow(128, bytes)), num(bytes), size);
1385
+ }
1386
+ tables.push(constDecl(varint.size, factory_1.f.arrowFunction(size, [parameter(sizeOf, T.number())])));
1387
+ // vwrite: continuation bytes while 128 or more remain, then the last byte; returns the new position.
1388
+ var wbuf = uid("buf");
1389
+ var wo = uid("o");
1390
+ var wn = uid("n");
1391
+ tables.push(constDecl(varint.write, factory_1.f.arrowFunction(factory_1.f.block([
1392
+ factory.createWhileStatement(factory_1.f.binary(wn, typescript_1.default.SyntaxKind.GreaterThanEqualsToken, num(128)), factory_1.f.block([
1393
+ factory_1.f.statement(bufferCall("writeu8", [
1394
+ wbuf,
1395
+ wo,
1396
+ factory_1.f.binary(factory_1.f.binary(wn, typescript_1.default.SyntaxKind.PercentToken, num(128)), typescript_1.default.SyntaxKind.PlusToken, num(128)),
1397
+ ])),
1398
+ addAssign(wo, num(1)),
1399
+ assign(wn, factory_1.f.call(prop("math", "floor"), [factory_1.f.binary(wn, typescript_1.default.SyntaxKind.SlashToken, num(128))])),
1400
+ ])),
1401
+ factory_1.f.statement(bufferCall("writeu8", [wbuf, wo, wn])),
1402
+ factory_1.f.returnStatement(add(wo, 1)),
1403
+ ]), [parameter(wbuf, T.buffer()), parameter(wo, T.number()), parameter(wn, T.number())])));
1404
+ // vread: `n += (b % 128) * scale` per byte until one is below 128; returns the value and position.
1405
+ var rbuf = uid("buf");
1406
+ var ro = uid("o");
1407
+ var rn = uid("n");
1408
+ var byte = uid("b");
1409
+ var scale = uid("scale");
1410
+ tables.push(constDecl(varint.read, factory_1.f.arrowFunction(factory_1.f.block([
1411
+ letDecl(rn, num(0)),
1412
+ letDecl(scale, num(1)),
1413
+ factory.createWhileStatement(factory_1.f.bool(true), factory_1.f.block([
1414
+ constDecl(byte, bufferCall("readu8", [rbuf, ro])),
1415
+ addAssign(ro, num(1)),
1416
+ addAssign(rn, factory_1.f.binary(factory_1.f.binary(byte, typescript_1.default.SyntaxKind.PercentToken, num(128)), typescript_1.default.SyntaxKind.AsteriskToken, scale)),
1417
+ ifStatement(below(byte, 128), [factory_1.f.returnStatement(factory_1.f.call("$tuple", [rn, ro]))]),
1418
+ factory_1.f.statement(factory_1.f.binary(scale, typescript_1.default.SyntaxKind.AsteriskEqualsToken, num(128))),
1419
+ ifStatement(factory_1.f.binary(scale, typescript_1.default.SyntaxKind.GreaterThanToken, num(Math.pow(128, (VARINT_MAX_BYTES - 1)))), [raise(MALFORMED)]),
1420
+ ])),
1421
+ ]), [parameter(rbuf, T.buffer()), parameter(ro, T.number())])));
1422
+ return varint;
1423
+ }
1424
+ /** `o = vwrite(buf, o, n)`: the cursor re-bases on the position variable. */
1425
+ function writeVarint(ctx, n) {
1426
+ var variable = ctx.cursor.variable;
1427
+ if (!variable)
1428
+ throw new Error("Flamework: a varint inside a fixed layout");
1429
+ ctx.out.push(assign(variable, factory_1.f.call(varintHelpers().write, [ctx.buf, at(ctx), n])));
1430
+ ctx.cursor.base = variable;
1431
+ ctx.cursor.offset = 0;
1432
+ }
1433
+ /** `const [n, o2] = vread(buf, o)`: the cursor re-bases on the position that came back. */
1434
+ function readVarint(ctx, hint) {
1435
+ if (hint === void 0) { hint = "n"; }
1436
+ if (!ctx.cursor.variable)
1437
+ throw new Error("Flamework: a varint inside a fixed layout");
1438
+ var n = uid(hint);
1439
+ var next = uid("o");
1440
+ ctx.out.push(constDecl(factory_1.f.arrayBindingDeclaration([n, next]), factory_1.f.call(varintHelpers().read, [ctx.buf, at(ctx)])));
1441
+ ctx.cursor.base = next;
1442
+ ctx.cursor.offset = 0;
1443
+ return n;
1444
+ }
1445
+ /** `<prefix> + length`: a constant prefix for a branded width, `vsize(length)` otherwise. */
1446
+ function sizeWithLength(out, width, length) {
1447
+ if (width !== "v")
1448
+ return add(length, WIDTH_SIZE[width]);
1449
+ var bound = bind(out, length, "length");
1450
+ return add(factory_1.f.call(varintHelpers().size, [bound]), bound);
1451
+ }
1452
+ /** `vsize(n) + n * size` for a run of `n` fixed-size elements. */
1453
+ function countedSize(prefix, count, size) {
1454
+ if (size === 0)
1455
+ return prefix;
1456
+ return add(prefix, factory_1.f.binary(count, typescript_1.default.SyntaxKind.AsteriskToken, num(size)));
1457
+ }
1458
+ /**
1459
+ * Sets and maps have no cheap length, so the elements are counted in the pass that measures them:
1460
+ * `let size = 0, n = 0; for (...) { n += 1; size += <element>; } size += vsize(n)`.
1461
+ */
1462
+ function countedInPass(out, collection, binding, element) {
1463
+ var total = uid("size");
1464
+ var count = uid("n");
1465
+ out.push(letDecl(total, num(0)), letDecl(count, num(0)));
1466
+ var body = new Array();
1467
+ body.push(addAssign(count, num(1)));
1468
+ body.push(addAssign(total, element(body)));
1469
+ out.push(forOf(binding, collection, body));
1470
+ out.push(addAssign(total, factory_1.f.call(varintHelpers().size, [count])));
1471
+ return total;
1472
+ }
1473
+ // --- unions --------------------------------------------------------------------------------------
1474
+ /**
1475
+ * The order the members are tested in when encoding: every member with a test of its own first,
1476
+ * a blob that matches anything last. The tag written is still the member's own index.
1477
+ */
1478
+ function evaluationOrder(union) {
1479
+ var catchAll = function (index) {
1480
+ var kind = describe(union.alternatives[index].shape);
1481
+ return kind.kind === "blob" && kind.typeofName === undefined ? 1 : 0;
1482
+ };
1483
+ return union.alternatives.map(function (_, index) { return index; }).sort(function (a, b) { return catchAll(a) - catchAll(b); });
1484
+ }
1485
+ /**
1486
+ * A cheap test for an object member of a union: its discriminant compared (`v.kind == "a"`), or
1487
+ * else the presence of a required key no other object member has (`v.Coins ~= nil`). Neither
1488
+ * leaves a guard in the output; a member with no such test falls back to one.
1489
+ */
1490
+ function objectTest(union, kind, record) {
1491
+ var discriminant = discriminantOf(union);
1492
+ var field = discriminant !== undefined ? kind.fields.find(function (field) { return field.name === discriminant; }) : undefined;
1493
+ if (field) {
1494
+ var constant = describe(field.shape);
1495
+ return equals(fieldAccess(record, field.name), constant.value);
1496
+ }
1497
+ // A collection among the members could hold any key, so presence is only trusted when the
1498
+ // other members are objects or not tables at all.
1499
+ var others = union.alternatives.map(function (other) { return describe(other.shape); }).filter(function (other) { return other !== kind; });
1500
+ if (others.some(function (other) { return TABLE_KINDS.has(other.kind) && other.kind !== "object"; }))
1501
+ return;
1502
+ var unique = kind.fields.find(function (candidate) {
1503
+ var shape = describe(candidate.shape);
1504
+ if (shape.kind === "optional" || shape.kind === "nothing")
1505
+ return false;
1506
+ return others.every(function (other) { return other.kind !== "object" || !other.fields.some(function (field) { return field.name === candidate.name; }); });
1507
+ });
1508
+ if (unique)
1509
+ return notNil(fieldAccess(record, unique.name));
1510
+ }
1511
+ /**
1512
+ * A property every object member has with a distinct literal type (`kind: "circle"` against
1513
+ * `kind: "rect"`). Comparing it is cheaper than a guard and leaves no guard in the output.
1514
+ */
1515
+ function discriminantOf(union) {
1516
+ var e_12, _a;
1517
+ var _b, _c;
1518
+ if (discriminants.has(union))
1519
+ return discriminants.get(union);
1520
+ var objects = union.alternatives
1521
+ .map(function (alternative) { return describe(alternative.shape); })
1522
+ .filter(function (kind) { return kind.kind === "object"; });
1523
+ var discriminant;
1524
+ var _loop_1 = function (field) {
1525
+ var seen = new Set();
1526
+ var distinct = objects.every(function (object) {
1527
+ var candidate = object.fields.find(function (other) { return other.name === field.name; });
1528
+ var kind = candidate && describe(candidate.shape);
1529
+ if (!kind || kind.kind !== "constant")
1530
+ return false;
1531
+ var text = printLiteral(kind.value);
1532
+ if (seen.has(text))
1533
+ return false;
1534
+ seen.add(text);
1535
+ return true;
1536
+ });
1537
+ if (distinct) {
1538
+ discriminant = field.name;
1539
+ return "break";
1540
+ }
1541
+ };
1542
+ try {
1543
+ for (var _d = __values((_c = (_b = objects[0]) === null || _b === void 0 ? void 0 : _b.fields) !== null && _c !== void 0 ? _c : []), _e = _d.next(); !_e.done; _e = _d.next()) {
1544
+ var field = _e.value;
1545
+ var state_1 = _loop_1(field);
1546
+ if (state_1 === "break")
1547
+ break;
1548
+ }
1549
+ }
1550
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
1551
+ finally {
1552
+ try {
1553
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
1554
+ }
1555
+ finally { if (e_12) throw e_12.error; }
1556
+ }
1557
+ discriminants.set(union, discriminant);
1558
+ return discriminant;
1559
+ }
1560
+ // --- cursor --------------------------------------------------------------------------------------
1561
+ function at(ctx, extra) {
1562
+ if (extra === void 0) { extra = 0; }
1563
+ var offset = ctx.cursor.offset + extra;
1564
+ return ctx.cursor.base ? add(ctx.cursor.base, offset) : num(offset);
1565
+ }
1566
+ /** Folds the pending constant (and any hoisted read's result) into the position variable. */
1567
+ function sync(ctx) {
1568
+ var cursor = ctx.cursor;
1569
+ if (cursor.base === cursor.variable && cursor.offset === 0)
1570
+ return;
1571
+ if (!cursor.variable)
1572
+ throw new Error("Flamework: a variable-size write inside a fixed layout");
1573
+ if (cursor.base === cursor.variable) {
1574
+ ctx.out.push(addAssign(cursor.variable, num(cursor.offset)));
1575
+ }
1576
+ else {
1577
+ ctx.out.push(assign(cursor.variable, at(ctx)));
1578
+ }
1579
+ cursor.base = cursor.variable;
1580
+ cursor.offset = 0;
1581
+ }
1582
+ /** Moves the position by a runtime amount, folding the pending constant into the same statement. */
1583
+ function advanceBy(ctx, amount) {
1584
+ var cursor = ctx.cursor;
1585
+ if (!cursor.variable)
1586
+ throw new Error("Flamework: a variable-size write inside a fixed layout");
1587
+ if (cursor.base === cursor.variable) {
1588
+ ctx.out.push(addAssign(cursor.variable, add(amount, cursor.offset)));
1589
+ }
1590
+ else {
1591
+ ctx.out.push(assign(cursor.variable, add(at(ctx), amount)));
1592
+ }
1593
+ cursor.base = cursor.variable;
1594
+ cursor.offset = 0;
1595
+ }
1596
+ /**
1597
+ * Statements for a conditional or repeated body. In a variable layout the body starts from the
1598
+ * synced position variable and leaves it exact; in a fixed layout it inherits the offset and the
1599
+ * caller advances past it.
1600
+ */
1601
+ function branch(ctx, build) {
1602
+ if (ctx.cursor.variable) {
1603
+ sync(ctx);
1604
+ var child_1 = __assign(__assign({}, ctx), { cursor: { variable: ctx.cursor.variable, base: ctx.cursor.variable, offset: 0 }, out: [] });
1605
+ build(child_1);
1606
+ sync(child_1);
1607
+ return child_1.out;
1608
+ }
1609
+ var child = __assign(__assign({}, ctx), { cursor: __assign({}, ctx.cursor), out: [] });
1610
+ build(child);
1611
+ return child.out;
1612
+ }
1613
+ /**
1614
+ * A value as a local: identifiers and casts of identifiers are returned as they are, except for a
1615
+ * parameter, which is copied so that the macros it reaches see a `const` (see {@link parameters}).
1616
+ */
1617
+ function bind(out, value, hint, type) {
1618
+ if (!type) {
1619
+ if (factory_1.f.is.identifier(value) && !isParameterReference(value))
1620
+ return value;
1621
+ if (typescript_1.default.isAsExpression(value) && factory_1.f.is.identifier(value.expression))
1622
+ return value;
1623
+ }
1624
+ var id = uid(hint);
1625
+ out.push(constDecl(id, value, type));
1626
+ return id;
1627
+ }
1628
+ function isNilLiteral(expression) {
1629
+ return typescript_1.default.isIdentifier(expression) && expression.text === "undefined";
1630
+ }
1631
+ function isLiteral(expression) {
1632
+ return factory_1.f.is.string(expression) || factory_1.f.is.number(expression) || factory_1.f.is.bool(expression) || factory_1.f.is.nil(expression);
1633
+ }
1634
+ function fieldAccess(object, name) {
1635
+ return IDENTIFIER.test(name)
1636
+ ? factory.createPropertyAccessExpression(object, name)
1637
+ : factory.createElementAccessExpression(object, factory_1.f.string(name));
1638
+ }
1639
+ function path(object, names) {
1640
+ return names.reduce(function (current, name) { return prop(current, name); }, object);
1641
+ }
1642
+ // --- size ----------------------------------------------------------------------------------------
1643
+ /** The byte count of `value`: a constant for fixed layouts, otherwise an expression (plus statements). */
1644
+ function emitSize(shape, value, out) {
1645
+ var e_13, _a, e_14, _b;
1646
+ var layout = layoutOf(shape);
1647
+ if (layout.size !== undefined)
1648
+ return num(layout.size);
1649
+ if (!isKind(shape)) {
1650
+ var info = hoist(shape);
1651
+ if (info)
1652
+ return factory_1.f.call(info.size, [value]);
1653
+ }
1654
+ var kind = describe(shape);
1655
+ switch (kind.kind) {
1656
+ case "string":
1657
+ return sizeWithLength(out, kind.length, factory_1.f.call(prop(factory_1.f.as(value, T.string()), "size"), []));
1658
+ case "buffer":
1659
+ return sizeWithLength(out, kind.length, bufferCall("len", [factory_1.f.as(value, T.buffer())]));
1660
+ case "varint":
1661
+ return factory_1.f.call(varintHelpers().size, [factory_1.f.as(value, T.number())]);
1662
+ case "optional": {
1663
+ if (isNilLiteral(value))
1664
+ return num(1);
1665
+ var inner = layoutOf(kind.inner);
1666
+ var v = bind(out, value, "v");
1667
+ if (inner.size !== undefined) {
1668
+ return conditional(notNil(v), num(1 + inner.size), num(1));
1669
+ }
1670
+ var total = uid("size");
1671
+ out.push(letDecl(total, num(1)));
1672
+ var body = new Array();
1673
+ body.push(addAssign(total, emitSize(kind.inner, v, body)));
1674
+ out.push(ifStatement(notNil(v), body));
1675
+ return total;
1676
+ }
1677
+ case "array": {
1678
+ var array = bind(out, factory_1.f.as(value, T.array()), "array");
1679
+ var count = bind(out, factory_1.f.call(prop(array, "size"), []), "n");
1680
+ var prefix = factory_1.f.call(varintHelpers().size, [count]);
1681
+ var element = layoutOf(kind.element);
1682
+ if (element.size !== undefined)
1683
+ return countedSize(prefix, count, element.size);
1684
+ var total = uid("size");
1685
+ var item = uid("item");
1686
+ out.push(letDecl(total, prefix));
1687
+ var body = new Array();
1688
+ body.push(addAssign(total, emitSize(kind.element, item, body)));
1689
+ out.push(forOf(item, array, body));
1690
+ return total;
1691
+ }
1692
+ case "set": {
1693
+ var set = bind(out, factory_1.f.as(value, T.set()), "set");
1694
+ var element_1 = layoutOf(kind.element);
1695
+ var item_1 = uid("item");
1696
+ return countedInPass(out, set, item_1, function (body) {
1697
+ return element_1.size !== undefined ? num(element_1.size) : emitSize(kind.element, item_1, body);
1698
+ });
1699
+ }
1700
+ case "map": {
1701
+ var map = bind(out, factory_1.f.as(value, T.map()), "map");
1702
+ var key_1 = uid("key");
1703
+ var entry_1 = uid("entry");
1704
+ return countedInPass(out, map, factory_1.f.arrayBindingDeclaration([key_1, entry_1]), function (body) {
1705
+ return add(emitSize(kind.key, key_1, body), emitSize(kind.value, entry_1, body));
1706
+ });
1707
+ }
1708
+ case "list": {
1709
+ var list_1 = bind(out, factory_1.f.as(value, T.array()), "list");
1710
+ var total_1 = new Sum();
1711
+ kind.elements.forEach(function (element, index) {
1712
+ total_1.add(emitSize(element, factory_1.f.elementAccessExpression(list_1, num(index)), out));
1713
+ });
1714
+ if (kind.rest) {
1715
+ var rest = layoutOf(kind.rest);
1716
+ var count = restCount(out, list_1, kind.elements.length);
1717
+ var prefix = factory_1.f.call(varintHelpers().size, [count]);
1718
+ if (rest.size !== undefined) {
1719
+ total_1.add(countedSize(prefix, count, rest.size));
1720
+ return total_1.build();
1721
+ }
1722
+ var sum = uid("size");
1723
+ var index = uid("i");
1724
+ total_1.add(prefix);
1725
+ out.push(letDecl(sum, total_1.build()));
1726
+ var body = new Array();
1727
+ var element = factory_1.f.elementAccessExpression(list_1, factory_1.f.binary(index, typescript_1.default.SyntaxKind.MinusToken, num(1)));
1728
+ body.push(addAssign(sum, emitSize(kind.rest, element, body)));
1729
+ out.push(forOf(index, range(num(kind.elements.length + 1), add(count, kind.elements.length)), body));
1730
+ return sum;
1731
+ }
1732
+ return total_1.build();
1733
+ }
1734
+ case "object": {
1735
+ var object = bind(out, factory_1.f.as(value, T.record()), "object");
1736
+ var total = new Sum();
1737
+ try {
1738
+ for (var _c = __values(kind.fields), _d = _c.next(); !_d.done; _d = _c.next()) {
1739
+ var field = _d.value;
1740
+ total.add(emitSize(field.shape, fieldAccess(object, field.name), out));
1741
+ }
1742
+ }
1743
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
1744
+ finally {
1745
+ try {
1746
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
1747
+ }
1748
+ finally { if (e_13) throw e_13.error; }
1749
+ }
1750
+ return total.build();
1751
+ }
1752
+ case "union": {
1753
+ var v = bind(out, value, "v");
1754
+ var total = uid("size");
1755
+ out.push(letDecl(total, num(1)));
1756
+ var chain = void 0;
1757
+ try {
1758
+ for (var _e = __values(evaluationOrder(kind).reverse()), _f = _e.next(); !_f.done; _f = _e.next()) {
1759
+ var i = _f.value;
1760
+ var alternative = kind.alternatives[i];
1761
+ var layout_1 = layoutOf(alternative.shape);
1762
+ var body = new Array();
1763
+ var size = layout_1.size !== undefined ? num(layout_1.size) : emitSize(alternative.shape, v, body);
1764
+ if (!(factory_1.f.is.number(size) && size.text === "0"))
1765
+ body.push(addAssign(total, size));
1766
+ if (body.length === 0 && chain === undefined)
1767
+ continue;
1768
+ chain = ifStatement(discriminate(kind, i, v), body, chain);
1769
+ }
1770
+ }
1771
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
1772
+ finally {
1773
+ try {
1774
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
1775
+ }
1776
+ finally { if (e_14) throw e_14.error; }
1777
+ }
1778
+ if (chain)
1779
+ out.push(chain);
1780
+ return total;
1781
+ }
1782
+ default:
1783
+ throw new Error("Flamework: '".concat(kind.kind, "' has a fixed size"));
1784
+ }
1785
+ }
1786
+ // --- write ---------------------------------------------------------------------------------------
1787
+ function emitWrite(shape, value, ctx) {
1788
+ var e_15, _a, e_16, _b, e_17, _c, e_18, _d;
1789
+ if (!isKind(shape)) {
1790
+ var info = hoist(shape);
1791
+ if (info) {
1792
+ var args = [ctx.buf, at(ctx), value];
1793
+ if (info.layout.blobs)
1794
+ args.push(ctx.blobs);
1795
+ ctx.out.push(assign(ctx.cursor.variable, factory_1.f.call(info.write, args)));
1796
+ ctx.cursor.base = ctx.cursor.variable;
1797
+ ctx.cursor.offset = 0;
1798
+ return;
1799
+ }
1800
+ }
1801
+ var kind = describe(shape);
1802
+ switch (kind.kind) {
1803
+ case "number":
1804
+ ctx.out.push(factory_1.f.statement(bufferCall("write".concat(kind.width), [ctx.buf, at(ctx), factory_1.f.as(value, T.number())])));
1805
+ ctx.cursor.offset += WIDTH_SIZE[kind.width];
1806
+ return;
1807
+ case "varint":
1808
+ writeVarint(ctx, factory_1.f.as(value, T.number()));
1809
+ return;
1810
+ case "boolean":
1811
+ // The value is the condition rather than `value === true`: an argument packed at its
1812
+ // call site can be a literal, and `false === true` is a comparison TypeScript rejects
1813
+ // when it checks the emitted code.
1814
+ ctx.out.push(factory_1.f.statement(bufferCall("writeu8", [ctx.buf, at(ctx), conditional(value, num(1), num(0))])));
1815
+ ctx.cursor.offset += 1;
1816
+ return;
1817
+ case "string": {
1818
+ var text = bind(ctx.out, factory_1.f.as(value, T.string()), "text");
1819
+ var length_1 = bind(ctx.out, factory_1.f.call(prop(text, "size"), []), "length");
1820
+ writeLength(ctx, kind.length, length_1, "string");
1821
+ ctx.out.push(factory_1.f.statement(bufferCall("writestring", [ctx.buf, at(ctx), text])));
1822
+ advanceBy(ctx, length_1);
1823
+ return;
1824
+ }
1825
+ case "buffer": {
1826
+ var bytes = bind(ctx.out, factory_1.f.as(value, T.buffer()), "bytes");
1827
+ var length_2 = bind(ctx.out, bufferCall("len", [bytes]), "length");
1828
+ writeLength(ctx, kind.length, length_2, "buffer");
1829
+ ctx.out.push(factory_1.f.statement(bufferCall("copy", [ctx.buf, at(ctx), bytes])));
1830
+ advanceBy(ctx, length_2);
1831
+ return;
1832
+ }
1833
+ case "constant":
1834
+ case "nothing":
1835
+ return;
1836
+ case "literals": {
1837
+ var index = literalTablesFor(kind).index;
1838
+ var v = bind(ctx.out, value, "v");
1839
+ var slot = bind(ctx.out, factory_1.f.call(prop(index, "get"), [factory_1.f.as(v, T.defined())]), "index");
1840
+ ctx.out.push(ifStatement(isNil(slot), [raise("value is not one of the literals its type allows")]));
1841
+ var width = kind.values.length > 0xff ? "u16" : "u8";
1842
+ ctx.out.push(factory_1.f.statement(bufferCall("write".concat(width), [ctx.buf, at(ctx), slot])));
1843
+ ctx.cursor.offset += WIDTH_SIZE[width];
1844
+ return;
1845
+ }
1846
+ case "blob": {
1847
+ var blob = bind(ctx.out, value, "blob");
1848
+ var blobs = ctx.blobs;
1849
+ ctx.out.push(ifStatement(notNil(blob), [
1850
+ factory_1.f.statement(factory_1.f.call(prop(blobs, "push"), [factory_1.f.as(blob, T.defined())])),
1851
+ factory_1.f.statement(bufferCall("writeu32", [ctx.buf, at(ctx), factory_1.f.call(prop(blobs, "size"), [])])),
1852
+ ], [factory_1.f.statement(bufferCall("writeu32", [ctx.buf, at(ctx), num(0)]))]));
1853
+ ctx.cursor.offset += BLOB_SIZE;
1854
+ return;
1855
+ }
1856
+ case "datatype": {
1857
+ var datatype = bind(ctx.out, factory_1.f.as(value, factory_1.f.referenceType(kind.name)), kind.name.toLowerCase());
1858
+ try {
1859
+ for (var _e = __values(DATATYPES[kind.name]), _f = _e.next(); !_f.done; _f = _e.next()) {
1860
+ var _g = __read(_f.value, 2), width = _g[0], names = _g[1];
1861
+ ctx.out.push(factory_1.f.statement(bufferCall("write".concat(width), [ctx.buf, at(ctx), path(datatype, names)])));
1862
+ ctx.cursor.offset += WIDTH_SIZE[width];
1863
+ }
1864
+ }
1865
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
1866
+ finally {
1867
+ try {
1868
+ if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
1869
+ }
1870
+ finally { if (e_15) throw e_15.error; }
1871
+ }
1872
+ return;
1873
+ }
1874
+ case "cframe": {
1875
+ var components = Array.from({ length: CFRAME_COMPONENTS }, function (_, i) { return uid("c".concat(i)); });
1876
+ ctx.out.push(constDecl(factory_1.f.arrayBindingDeclaration(components), factory_1.f.call(prop(factory_1.f.as(value, factory_1.f.referenceType("CFrame")), "GetComponents"), [])));
1877
+ try {
1878
+ for (var components_1 = __values(components), components_1_1 = components_1.next(); !components_1_1.done; components_1_1 = components_1.next()) {
1879
+ var component = components_1_1.value;
1880
+ ctx.out.push(factory_1.f.statement(bufferCall("writef32", [ctx.buf, at(ctx), component])));
1881
+ ctx.cursor.offset += 4;
1882
+ }
1883
+ }
1884
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
1885
+ finally {
1886
+ try {
1887
+ if (components_1_1 && !components_1_1.done && (_b = components_1.return)) _b.call(components_1);
1888
+ }
1889
+ finally { if (e_16) throw e_16.error; }
1890
+ }
1891
+ return;
1892
+ }
1893
+ case "enum":
1894
+ ctx.out.push(factory_1.f.statement(bufferCall("writeu16", [ctx.buf, at(ctx), prop(factory_1.f.as(value, T.enumItem()), "Value")])));
1895
+ ctx.cursor.offset += 2;
1896
+ return;
1897
+ case "optional": {
1898
+ if (isNilLiteral(value)) {
1899
+ ctx.out.push(factory_1.f.statement(bufferCall("writeu8", [ctx.buf, at(ctx), num(0)])));
1900
+ ctx.cursor.offset += 1;
1901
+ return;
1902
+ }
1903
+ var v_1 = bind(ctx.out, value, "v");
1904
+ ctx.out.push(factory_1.f.statement(bufferCall("writeu8", [ctx.buf, at(ctx), conditional(notNil(v_1), num(1), num(0))])));
1905
+ ctx.cursor.offset += 1;
1906
+ ctx.out.push(ifStatement(notNil(v_1), branch(ctx, function (child) { return emitWrite(kind.inner, v_1, child); })));
1907
+ return;
1908
+ }
1909
+ case "array": {
1910
+ var array = bind(ctx.out, factory_1.f.as(value, T.array()), "array");
1911
+ writeVarint(ctx, factory_1.f.call(prop(array, "size"), []));
1912
+ var item_2 = uid("item");
1913
+ ctx.out.push(forOf(item_2, array, branch(ctx, function (child) { return emitWrite(kind.element, item_2, child); })));
1914
+ return;
1915
+ }
1916
+ case "set": {
1917
+ var set = bind(ctx.out, factory_1.f.as(value, T.set()), "set");
1918
+ writeCounted(ctx, set, function (item, child) { return emitWrite(kind.element, item, child); });
1919
+ return;
1920
+ }
1921
+ case "map": {
1922
+ var map = bind(ctx.out, factory_1.f.as(value, T.map()), "map");
1923
+ var key_2 = uid("key");
1924
+ var entry_2 = uid("entry");
1925
+ writeCounted(ctx, map, function (_, child) {
1926
+ emitWrite(kind.key, key_2, child);
1927
+ emitWrite(kind.value, entry_2, child);
1928
+ }, factory_1.f.arrayBindingDeclaration([key_2, entry_2]));
1929
+ return;
1930
+ }
1931
+ case "list": {
1932
+ var list_2 = bind(ctx.out, factory_1.f.as(value, T.array()), "list");
1933
+ kind.elements.forEach(function (element, index) {
1934
+ emitWrite(element, factory_1.f.elementAccessExpression(list_2, num(index)), ctx);
1935
+ });
1936
+ if (kind.rest) {
1937
+ var count = restCount(ctx.out, list_2, kind.elements.length);
1938
+ writeVarint(ctx, count);
1939
+ var index = uid("i");
1940
+ var element_2 = factory_1.f.elementAccessExpression(list_2, factory_1.f.binary(index, typescript_1.default.SyntaxKind.MinusToken, num(1)));
1941
+ ctx.out.push(forOf(index, range(num(kind.elements.length + 1), add(count, kind.elements.length)), branch(ctx, function (child) { return emitWrite(kind.rest, element_2, child); })));
1942
+ }
1943
+ return;
1944
+ }
1945
+ case "object": {
1946
+ var object = bind(ctx.out, factory_1.f.as(value, T.record()), "object");
1947
+ try {
1948
+ for (var _h = __values(kind.fields), _j = _h.next(); !_j.done; _j = _h.next()) {
1949
+ var field = _j.value;
1950
+ emitWrite(field.shape, fieldAccess(object, field.name), ctx);
1951
+ }
1952
+ }
1953
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
1954
+ finally {
1955
+ try {
1956
+ if (_j && !_j.done && (_c = _h.return)) _c.call(_h);
1957
+ }
1958
+ finally { if (e_17) throw e_17.error; }
1959
+ }
1960
+ return;
1961
+ }
1962
+ case "union": {
1963
+ var v_2 = bind(ctx.out, value, "v");
1964
+ var layout = layoutOf(kind);
1965
+ // Who moves the position past the union. In a fixed layout the branches write at
1966
+ // literal offsets and move nothing, so the size is added here; in a variable one
1967
+ // `branch` syncs the position variable on the way out of every branch, and the
1968
+ // union has already been stepped over. Adding it in both cases steps over it
1969
+ // twice, which puts the next write a union's worth too far along and runs the
1970
+ // element past what the size pass budgeted.
1971
+ var isFixedLayout = ctx.cursor.variable === undefined;
1972
+ var start = ctx.cursor.offset;
1973
+ var chain = factory_1.f.block([raise("value matches none of the union's members")]);
1974
+ var _loop_2 = function (i) {
1975
+ var alternative = kind.alternatives[i];
1976
+ var body = branch(ctx, function (child) {
1977
+ child.out.push(factory_1.f.statement(bufferCall("writeu8", [child.buf, at(child), num(i)])));
1978
+ child.cursor.offset += 1;
1979
+ emitWrite(alternative.shape, v_2, child);
1980
+ });
1981
+ chain = ifStatement(discriminate(kind, i, v_2), body, chain);
1982
+ };
1983
+ try {
1984
+ for (var _k = __values(evaluationOrder(kind).reverse()), _l = _k.next(); !_l.done; _l = _k.next()) {
1985
+ var i = _l.value;
1986
+ _loop_2(i);
1987
+ }
1988
+ }
1989
+ catch (e_18_1) { e_18 = { error: e_18_1 }; }
1990
+ finally {
1991
+ try {
1992
+ if (_l && !_l.done && (_d = _k.return)) _d.call(_k);
1993
+ }
1994
+ finally { if (e_18) throw e_18.error; }
1995
+ }
1996
+ ctx.out.push(chain);
1997
+ if (isFixedLayout && layout.size !== undefined)
1998
+ ctx.cursor.offset = start + layout.size;
1999
+ return;
2000
+ }
2001
+ }
2002
+ }
2003
+ /** How many rest elements a list holds: never negative, since absent trailing optionals shorten it. */
2004
+ function restCount(out, list, fixed) {
2005
+ var count = uid("count");
2006
+ var length = factory_1.f.binary(factory_1.f.call(prop(list, "size"), []), typescript_1.default.SyntaxKind.MinusToken, num(fixed));
2007
+ out.push(constDecl(count, factory_1.f.call(prop("math", "max"), [length, num(0)])));
2008
+ return count;
2009
+ }
2010
+ /** The length prefix of a string or buffer: a varint, or a fixed width refusing what it cannot hold. */
2011
+ function writeLength(ctx, width, length, what) {
2012
+ if (width === "v")
2013
+ return writeVarint(ctx, length);
2014
+ if (width !== "u32") {
2015
+ ctx.out.push(ifStatement(factory_1.f.binary(length, typescript_1.default.SyntaxKind.GreaterThanToken, num(LENGTH_MAX[width])), [
2016
+ raise("".concat(what, " is longer than its ").concat(width, " length prefix allows")),
2017
+ ]));
2018
+ }
2019
+ ctx.out.push(factory_1.f.statement(bufferCall("write".concat(width), [ctx.buf, at(ctx), length])));
2020
+ ctx.cursor.offset += WIDTH_SIZE[width];
2021
+ }
2022
+ /** Sets and maps have no cheap length: they are counted (a loop, in roblox-ts) before the elements. */
2023
+ function writeCounted(ctx, collection, body, binding) {
2024
+ var count = bind(ctx.out, factory_1.f.call(prop(collection, "size"), []), "n");
2025
+ writeVarint(ctx, count);
2026
+ var item = uid("item");
2027
+ ctx.out.push(forOf(binding !== null && binding !== void 0 ? binding : item, collection, branch(ctx, function (child) { return body(item, child); })));
2028
+ }
2029
+ /** The test that tells member `index` of `union` apart from the others, given a value. */
2030
+ function discriminate(union, index, value) {
2031
+ var alternative = union.alternatives[index];
2032
+ var kind = describe(alternative.shape);
2033
+ if (kind.kind === "object") {
2034
+ var test = objectTest(union, kind, factory_1.f.as(value, T.record()));
2035
+ if (test) {
2036
+ // Indexing is only safe once the value is known to be a table.
2037
+ var tables_1 = union.alternatives.every(function (other) { return TABLE_KINDS.has(describe(other.shape).kind); });
2038
+ return tables_1 ? test : factory_1.f.binary(typeOfIs(value, "table"), typescript_1.default.SyntaxKind.AmpersandAmpersandToken, test);
2039
+ }
2040
+ }
2041
+ switch (kind.kind) {
2042
+ case "number":
2043
+ case "varint":
2044
+ return typeOfIs(value, "number");
2045
+ case "string":
2046
+ return typeOfIs(value, "string");
2047
+ case "boolean":
2048
+ return typeOfIs(value, "boolean");
2049
+ case "buffer":
2050
+ return typeOfIs(value, "buffer");
2051
+ case "datatype":
2052
+ return typeOfIs(value, kind.name);
2053
+ case "cframe":
2054
+ return typeOfIs(value, "CFrame");
2055
+ case "enum":
2056
+ return factory_1.f.binary(typeOfIs(value, "EnumItem"), typescript_1.default.SyntaxKind.AmpersandAmpersandToken, equals(prop(factory_1.f.as(value, T.enumItem()), "EnumType"), prop("Enum", kind.name)));
2057
+ case "literals":
2058
+ return notNil(factory_1.f.call(prop(literalTablesFor(kind).index, "get"), [factory_1.f.as(value, T.defined())]));
2059
+ case "constant":
2060
+ return equals(value, kind.value);
2061
+ case "nothing":
2062
+ return isNil(value);
2063
+ case "blob":
2064
+ return kind.typeofName !== undefined ? typeOfIs(value, kind.typeofName) : factory_1.f.bool(true);
2065
+ default:
2066
+ if (!alternative.type)
2067
+ throw new Error("Flamework: cannot discriminate a synthetic ".concat(kind.kind));
2068
+ return factory_1.f.call(guardFor(alternative.type), [value]);
2069
+ }
2070
+ }
2071
+ // --- read ----------------------------------------------------------------------------------------
2072
+ /**
2073
+ * The value read at the cursor. Fixed-size reads are plain expressions; anything that moves the
2074
+ * position by a runtime amount is bound to a local first, so the expressions of one container are
2075
+ * always evaluated against a position that no later read has changed.
2076
+ */
2077
+ function emitRead(shape, ctx) {
2078
+ if (!isKind(shape)) {
2079
+ var info = hoist(shape);
2080
+ if (info) {
2081
+ var args = [ctx.buf, at(ctx)];
2082
+ if (info.layout.blobs)
2083
+ args.push(ctx.blobs);
2084
+ var value = uid("value");
2085
+ var next = uid("o");
2086
+ ctx.out.push(constDecl(factory_1.f.arrayBindingDeclaration([value, next]), factory_1.f.call(info.read, args)));
2087
+ ctx.cursor.base = next;
2088
+ ctx.cursor.offset = 0;
2089
+ return value;
2090
+ }
2091
+ }
2092
+ var kind = describe(shape);
2093
+ switch (kind.kind) {
2094
+ case "number": {
2095
+ var read = bufferCall("read".concat(kind.width), [ctx.buf, at(ctx)]);
2096
+ ctx.cursor.offset += WIDTH_SIZE[kind.width];
2097
+ return read;
2098
+ }
2099
+ case "varint":
2100
+ return readVarint(ctx);
2101
+ case "boolean": {
2102
+ var read = factory_1.f.binary(bufferCall("readu8", [ctx.buf, at(ctx)]), typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, num(0));
2103
+ ctx.cursor.offset += 1;
2104
+ return read;
2105
+ }
2106
+ case "string": {
2107
+ var length_3 = readLength(ctx, kind.length);
2108
+ var text = bind(ctx.out, bufferCall("readstring", [ctx.buf, at(ctx), length_3]), "text");
2109
+ advanceBy(ctx, length_3);
2110
+ return text;
2111
+ }
2112
+ case "buffer": {
2113
+ var length_4 = readLength(ctx, kind.length);
2114
+ // `buffer.copy` bounds-checks, but only after `buffer.create` has allocated the announced
2115
+ // length: a hostile length must be refused against what is left before it drives that.
2116
+ var remaining = factory_1.f.binary(bufferCall("len", [ctx.buf]), typescript_1.default.SyntaxKind.MinusToken, at(ctx));
2117
+ ctx.out.push(ifStatement(factory_1.f.binary(length_4, typescript_1.default.SyntaxKind.GreaterThanToken, remaining), [raise(MALFORMED)]));
2118
+ var bytes = bind(ctx.out, bufferCall("create", [length_4]), "bytes");
2119
+ ctx.out.push(factory_1.f.statement(bufferCall("copy", [bytes, num(0), ctx.buf, at(ctx), length_4])));
2120
+ advanceBy(ctx, length_4);
2121
+ return bytes;
2122
+ }
2123
+ case "constant":
2124
+ return kind.value;
2125
+ case "nothing":
2126
+ return factory_1.f.nil();
2127
+ case "literals": {
2128
+ var list = literalTablesFor(kind).list;
2129
+ var width = kind.values.length > 0xff ? "u16" : "u8";
2130
+ var index = bufferCall("read".concat(width), [ctx.buf, at(ctx)]);
2131
+ ctx.cursor.offset += WIDTH_SIZE[width];
2132
+ var value = bind(ctx.out, factory_1.f.elementAccessExpression(list, index), "literal");
2133
+ ctx.out.push(ifStatement(isNil(value), [raise(MALFORMED)]));
2134
+ return value;
2135
+ }
2136
+ case "blob": {
2137
+ var index = bufferCall("readu32", [ctx.buf, at(ctx)]);
2138
+ ctx.cursor.offset += BLOB_SIZE;
2139
+ // 1-based on the wire; roblox-ts adds the one back when indexing an array.
2140
+ return factory_1.f.elementAccessExpression(ctx.blobs, factory_1.f.binary(index, typescript_1.default.SyntaxKind.MinusToken, num(1)));
2141
+ }
2142
+ case "datatype": {
2143
+ var args = DATATYPES[kind.name].map(function (_a) {
2144
+ var _b = __read(_a, 1), width = _b[0];
2145
+ var read = bufferCall("read".concat(width), [ctx.buf, at(ctx)]);
2146
+ ctx.cursor.offset += WIDTH_SIZE[width];
2147
+ return read;
2148
+ });
2149
+ return construct(kind.name, args);
2150
+ }
2151
+ case "cframe": {
2152
+ var args = Array.from({ length: CFRAME_COMPONENTS }, function () {
2153
+ var read = bufferCall("readf32", [ctx.buf, at(ctx)]);
2154
+ ctx.cursor.offset += 4;
2155
+ return read;
2156
+ });
2157
+ return construct("CFrame", args);
2158
+ }
2159
+ case "enum": {
2160
+ var index = bufferCall("readu16", [ctx.buf, at(ctx)]);
2161
+ ctx.cursor.offset += 2;
2162
+ return factory_1.f.call(prop(enumTableFor(kind.name), "get"), [index]);
2163
+ }
2164
+ case "optional": {
2165
+ var present = bind(ctx.out, factory_1.f.binary(bufferCall("readu8", [ctx.buf, at(ctx)]), typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken, num(0)), "present");
2166
+ ctx.cursor.offset += 1;
2167
+ var value_1 = uid("value");
2168
+ ctx.out.push(letDecl(value_1, undefined, T.unknown()));
2169
+ ctx.out.push(ifStatement(present, branch(ctx, function (child) { return child.out.push(assign(value_1, emitRead(kind.inner, child))); })));
2170
+ return value_1;
2171
+ }
2172
+ case "array": {
2173
+ var count = readCount(ctx, layoutOf(kind.element).min);
2174
+ var array_1 = bind(ctx.out, construct("Array", [count]), "array", T.array());
2175
+ var index_1 = uid("i");
2176
+ ctx.out.push(forOf(index_1, range(num(1), count), branch(ctx, function (child) {
2177
+ var element = emitRead(kind.element, child);
2178
+ child.out.push(assign(factory_1.f.elementAccessExpression(array_1, factory_1.f.binary(index_1, typescript_1.default.SyntaxKind.MinusToken, num(1))), element));
2179
+ })));
2180
+ return array_1;
2181
+ }
2182
+ case "set": {
2183
+ var count = readCount(ctx, layoutOf(kind.element).min);
2184
+ var set_1 = bind(ctx.out, construct("Set", []), "set", T.set());
2185
+ ctx.out.push(forOf(uid("_"), range(num(1), count), branch(ctx, function (child) {
2186
+ var element = emitRead(kind.element, child);
2187
+ child.out.push(factory_1.f.statement(factory_1.f.call(prop(set_1, "add"), [factory_1.f.as(element, T.defined())])));
2188
+ })));
2189
+ return set_1;
2190
+ }
2191
+ case "map": {
2192
+ var count = readCount(ctx, layoutOf(kind.key).min + layoutOf(kind.value).min);
2193
+ var map_1 = bind(ctx.out, construct("Map", []), "map", T.map());
2194
+ ctx.out.push(forOf(uid("_"), range(num(1), count), branch(ctx, function (child) {
2195
+ var key = readInto(kind.key, child, "key");
2196
+ var value = readInto(kind.value, child, "entry");
2197
+ child.out.push(factory_1.f.statement(factory_1.f.call(prop(map_1, "set"), [key, value])));
2198
+ })));
2199
+ return map_1;
2200
+ }
2201
+ case "list": {
2202
+ var elements = kind.elements.map(function (element) { return readInto(element, ctx, "arg"); });
2203
+ // Typed explicitly: an empty list would otherwise be an implicit `any[]`.
2204
+ if (!kind.rest)
2205
+ return factory_1.f.as(factory_1.f.array(elements, false), T.array());
2206
+ var count = readCount(ctx, layoutOf(kind.rest).min);
2207
+ var list_3 = bind(ctx.out, factory_1.f.array(elements, false), "list", T.array());
2208
+ var index_2 = uid("i");
2209
+ ctx.out.push(forOf(index_2, range(num(1), count), branch(ctx, function (child) {
2210
+ var element = emitRead(kind.rest, child);
2211
+ child.out.push(assign(factory_1.f.elementAccessExpression(list_3, factory_1.f.binary(index_2, typescript_1.default.SyntaxKind.PlusToken, num(kind.elements.length - 1))), element));
2212
+ })));
2213
+ return list_3;
2214
+ }
2215
+ case "object": {
2216
+ var fields = kind.fields.map(function (field) {
2217
+ return factory_1.f.propertyAssignmentDeclaration(field.name, readInto(field.shape, ctx, field.name));
2218
+ });
2219
+ return factory_1.f.object(fields);
2220
+ }
2221
+ case "union": {
2222
+ var layout = layoutOf(kind);
2223
+ // As in {@link emitWrite}: only a fixed layout's branches leave the position where
2224
+ // they found it, so only there does the union's size get added on top of them.
2225
+ var isFixedLayout = ctx.cursor.variable === undefined;
2226
+ var tag = bind(ctx.out, bufferCall("readu8", [ctx.buf, at(ctx)]), "tag");
2227
+ ctx.cursor.offset += 1;
2228
+ var start = ctx.cursor.offset;
2229
+ var value_2 = uid("value");
2230
+ ctx.out.push(letDecl(value_2, undefined, T.unknown()));
2231
+ var chain = factory_1.f.block([raise(MALFORMED)]);
2232
+ var _loop_3 = function (i) {
2233
+ var alternative = kind.alternatives[i];
2234
+ var body = branch(ctx, function (child) {
2235
+ return child.out.push(assign(value_2, emitRead(alternative.shape, child)));
2236
+ });
2237
+ chain = ifStatement(equals(tag, num(i)), body, chain);
2238
+ };
2239
+ for (var i = kind.alternatives.length - 1; i >= 0; i--) {
2240
+ _loop_3(i);
2241
+ }
2242
+ ctx.out.push(chain);
2243
+ if (isFixedLayout && layout.size !== undefined)
2244
+ ctx.cursor.offset = start + layout.size - 1;
2245
+ return value_2;
2246
+ }
2247
+ }
2248
+ }
2249
+ /** A read whose result is bound to a local in variable layouts; see {@link emitRead}. */
2250
+ function readInto(shape, ctx, hint) {
2251
+ var value = emitRead(shape, ctx);
2252
+ if (!ctx.cursor.variable || factory_1.f.is.identifier(value) || isLiteral(value))
2253
+ return value;
2254
+ return bind(ctx.out, value, localName(hint));
2255
+ }
2256
+ /**
2257
+ * A field's name as a local, kept clear of the globals the generated code reaches for.
2258
+ *
2259
+ * A synthesised local is renamed when it clashes with a name the file it lands in already uses,
2260
+ * but a global is declared elsewhere, so nothing renames a local named after one. A field named
2261
+ * after its own datatype -- `readonly CFrame: CFrame` -- then reads back as
2262
+ * `const CFrame = new CFrame(...)`. The Luau that lowers to is correct, since its right-hand
2263
+ * side is the outer binding; the intermediate TypeScript is checked before it is lowered, and a
2264
+ * `const` in its own initializer is an error there. The same goes for a field named `buffer` or
2265
+ * `Map`, which the reads that follow it would resolve to instead of the global.
2266
+ */
2267
+ function localName(hint) {
2268
+ var name = hint.replace(/\W/g, "_");
2269
+ var global = typeChecker.resolveName(name, undefined, GLOBAL_MEANING, false);
2270
+ return global !== undefined ? "v_".concat(name) : name;
2271
+ }
2272
+ function readLength(ctx, width) {
2273
+ if (width === "v")
2274
+ return readVarint(ctx, "length");
2275
+ var length = bind(ctx.out, bufferCall("read".concat(width), [ctx.buf, at(ctx)]), "length");
2276
+ ctx.cursor.offset += WIDTH_SIZE[width];
2277
+ return length;
2278
+ }
2279
+ /**
2280
+ * An element count from the buffer, refused when the elements it announces could not fit in what
2281
+ * is left: a hostile count must not drive a huge allocation or a long loop. Elements that take no
2282
+ * bytes cannot be bounded that way; they are tallied across the whole payload instead, and the
2283
+ * tally is capped, so that nesting cannot multiply what one count may announce.
2284
+ */
2285
+ function readCount(ctx, minimumElementSize) {
2286
+ var count = readVarint(ctx, "count");
2287
+ sync(ctx);
2288
+ if (minimumElementSize === 0) {
2289
+ var tally = zeroTally();
2290
+ ctx.out.push(addAssign(tally, count));
2291
+ ctx.out.push(ifStatement(factory_1.f.binary(tally, typescript_1.default.SyntaxKind.GreaterThanToken, num(ZERO_SIZE_COUNT_MAX)), [
2292
+ raise(MALFORMED),
2293
+ ]));
2294
+ return count;
2295
+ }
2296
+ var remaining = factory_1.f.binary(bufferCall("len", [ctx.buf]), typescript_1.default.SyntaxKind.MinusToken, ctx.cursor.variable);
2297
+ var needed = minimumElementSize > 1 ? factory_1.f.binary(count, typescript_1.default.SyntaxKind.AsteriskToken, num(minimumElementSize)) : count;
2298
+ ctx.out.push(ifStatement(factory_1.f.binary(needed, typescript_1.default.SyntaxKind.GreaterThanToken, remaining), [raise(MALFORMED)]));
2299
+ return count;
2300
+ }
2301
+ /** The file's zero-size element tally, declared once ahead of the hoisted functions that share it. */
2302
+ function zeroTally() {
2303
+ if (!zeros) {
2304
+ zeros = uid("zeros");
2305
+ tables.push(letDecl(zeros, num(0)));
2306
+ }
2307
+ return zeros;
2308
+ }
2309
+ }