@exornea/zeno-compiler 1.7.0 → 2.2.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.
- package/bin/zeno-codegen.mjs +79 -31
- package/bin/zeno-diff-layout.mjs +60 -0
- package/bin/zeno-inspect.mjs +67 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +48 -26
- package/dist/analyzer.js.map +1 -1
- package/dist/emitter-ast.d.ts +8 -0
- package/dist/emitter-ast.d.ts.map +1 -0
- package/dist/emitter-ast.js +34 -0
- package/dist/emitter-ast.js.map +1 -0
- package/dist/emitter-capabilities.d.ts +8 -0
- package/dist/emitter-capabilities.d.ts.map +1 -0
- package/dist/emitter-capabilities.js +59 -0
- package/dist/emitter-capabilities.js.map +1 -0
- package/dist/emitter-input.d.ts +3 -0
- package/dist/emitter-input.d.ts.map +1 -0
- package/dist/emitter-input.js +45 -0
- package/dist/emitter-input.js.map +1 -0
- package/dist/emitter-names.d.ts +5 -0
- package/dist/emitter-names.d.ts.map +1 -0
- package/dist/emitter-names.js +10 -0
- package/dist/emitter-names.js.map +1 -0
- package/dist/emitter-runtime-imports.d.ts +3 -0
- package/dist/emitter-runtime-imports.d.ts.map +1 -0
- package/dist/emitter-runtime-imports.js +73 -0
- package/dist/emitter-runtime-imports.js.map +1 -0
- package/dist/emitter-scan-kernels.d.ts +10 -0
- package/dist/emitter-scan-kernels.d.ts.map +1 -0
- package/dist/emitter-scan-kernels.js +174 -0
- package/dist/emitter-scan-kernels.js.map +1 -0
- package/dist/emitter-template.d.ts +7 -0
- package/dist/emitter-template.d.ts.map +1 -1
- package/dist/emitter-template.js +8 -3
- package/dist/emitter-template.js.map +1 -1
- package/dist/emitter.d.ts +8 -1
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +247 -335
- package/dist/emitter.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/layout-manifest.d.ts +37 -0
- package/dist/layout-manifest.d.ts.map +1 -0
- package/dist/layout-manifest.js +251 -0
- package/dist/layout-manifest.js.map +1 -0
- package/dist/lowering.d.ts +5 -2
- package/dist/lowering.d.ts.map +1 -1
- package/dist/lowering.js +86 -27
- package/dist/lowering.js.map +1 -1
- package/dist/source-map.d.ts +10 -0
- package/dist/source-map.d.ts.map +1 -0
- package/dist/source-map.js +371 -0
- package/dist/source-map.js.map +1 -0
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +34 -16
- package/dist/validator.js.map +1 -1
- package/package.json +5 -3
package/dist/emitter.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { scalarGetterMethod, scalarSetterMethod, scalarTsType, } from "@exornea/zeno-schema";
|
|
2
|
-
import {
|
|
2
|
+
import { emitFixedArrayFieldAccessor, emitFixedArrayObjectFieldWrite, } from "./emitter-fixed-array.js";
|
|
3
|
+
import { emitAstCheckedSource } from "./emitter-ast.js";
|
|
4
|
+
import { canEmitObjectWriter, hasTailWriterFields, isTailWriterField, structFieldHasTailFields, } from "./emitter-capabilities.js";
|
|
5
|
+
import { emitInputInterface } from "./emitter-input.js";
|
|
6
|
+
import { encodingLiteral, toLittleEndianLiteral, toPascalCase } from "./emitter-names.js";
|
|
7
|
+
import { collectRuntimeImports } from "./emitter-runtime-imports.js";
|
|
8
|
+
import { emitScalarScanKernels, emitScanRangeHelper, hasScanKernels, normalizeScanKernelMode, } from "./emitter-scan-kernels.js";
|
|
3
9
|
import { method } from "./emitter-template.js";
|
|
10
|
+
import { createProjectionSourceMap } from "./source-map.js";
|
|
4
11
|
export function emitStructView(layout, options = {}) {
|
|
5
12
|
return emitProjectionFile([layout], options);
|
|
6
13
|
}
|
|
7
14
|
export function emitProjectionFile(layouts, options = {}) {
|
|
15
|
+
return emitAstCheckedSource(emitProjectionFileText(layouts, options), "zeno.view.ts").code;
|
|
16
|
+
}
|
|
17
|
+
function emitProjectionFileText(layouts, options = {}) {
|
|
8
18
|
const lines = [];
|
|
9
19
|
const runtimeImports = collectRuntimeImports(layouts);
|
|
10
20
|
const layoutMap = new Map(layouts.map((layout) => [layout.name, layout]));
|
|
@@ -20,109 +30,13 @@ export function emitProjectionFile(layouts, options = {}) {
|
|
|
20
30
|
}
|
|
21
31
|
return lines.join("\n");
|
|
22
32
|
}
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
for (const field of layout.fields) {
|
|
31
|
-
collectFieldRuntimeImports(field, imports);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return Array.from(imports).sort();
|
|
35
|
-
}
|
|
36
|
-
function collectFieldRuntimeImports(field, imports) {
|
|
37
|
-
switch (field.kind) {
|
|
38
|
-
case "fixed-bytes":
|
|
39
|
-
imports.add("fixedBytesView");
|
|
40
|
-
imports.add("writeFixedBytes");
|
|
41
|
-
return;
|
|
42
|
-
case "fixed-string":
|
|
43
|
-
imports.add("decodeFixedText");
|
|
44
|
-
imports.add("fixedBytesView");
|
|
45
|
-
imports.add("writeFixedText");
|
|
46
|
-
return;
|
|
47
|
-
case "dynamic-string":
|
|
48
|
-
imports.add("Utf8SpanView");
|
|
49
|
-
return;
|
|
50
|
-
case "dynamic-bytes":
|
|
51
|
-
imports.add("BytesSpanView");
|
|
52
|
-
return;
|
|
53
|
-
case "vector":
|
|
54
|
-
switch (field.element.kind) {
|
|
55
|
-
case "scalar":
|
|
56
|
-
imports.add("ScalarVectorView");
|
|
57
|
-
return;
|
|
58
|
-
case "dynamic-string":
|
|
59
|
-
imports.add("Utf8VectorView");
|
|
60
|
-
return;
|
|
61
|
-
case "dynamic-bytes":
|
|
62
|
-
imports.add("BytesVectorView");
|
|
63
|
-
return;
|
|
64
|
-
case "fixed-bytes":
|
|
65
|
-
imports.add("FixedBytesVectorView");
|
|
66
|
-
return;
|
|
67
|
-
case "fixed-string":
|
|
68
|
-
imports.add("FixedStringVectorView");
|
|
69
|
-
return;
|
|
70
|
-
case "struct":
|
|
71
|
-
imports.add("StructVectorView");
|
|
72
|
-
return;
|
|
73
|
-
case "pointer":
|
|
74
|
-
imports.add("PointerVectorView");
|
|
75
|
-
return;
|
|
76
|
-
default:
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
case "fixed-array":
|
|
80
|
-
collectFixedArrayRuntimeImports(field.element, imports);
|
|
81
|
-
return;
|
|
82
|
-
default:
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function emitInputInterface(layout) {
|
|
87
|
-
const lines = [`export interface ${layout.name}ViewInput {`];
|
|
88
|
-
for (const field of layout.fields) {
|
|
89
|
-
lines.push(` readonly ${field.name}: ${fieldInputType(field)};`);
|
|
90
|
-
}
|
|
91
|
-
lines.push("}");
|
|
92
|
-
return lines;
|
|
93
|
-
}
|
|
94
|
-
function fieldInputType(field) {
|
|
95
|
-
switch (field.kind) {
|
|
96
|
-
case "scalar":
|
|
97
|
-
return scalarTsType(field.scalar);
|
|
98
|
-
case "fixed-bytes":
|
|
99
|
-
case "dynamic-bytes":
|
|
100
|
-
return "ArrayLike<number> | Uint8Array";
|
|
101
|
-
case "fixed-string":
|
|
102
|
-
case "dynamic-string":
|
|
103
|
-
return "string";
|
|
104
|
-
case "struct":
|
|
105
|
-
return `${field.typeName}ViewInput`;
|
|
106
|
-
case "pointer":
|
|
107
|
-
return "number | null";
|
|
108
|
-
case "fixed-array":
|
|
109
|
-
return `readonly ${fixedArrayInputElementType(field.element)}[]`;
|
|
110
|
-
case "vector":
|
|
111
|
-
switch (field.element.kind) {
|
|
112
|
-
case "scalar":
|
|
113
|
-
return `readonly ${scalarTsType(field.element.scalar)}[]`;
|
|
114
|
-
case "fixed-bytes":
|
|
115
|
-
case "dynamic-bytes":
|
|
116
|
-
return "readonly (ArrayLike<number> | Uint8Array)[]";
|
|
117
|
-
case "fixed-string":
|
|
118
|
-
case "dynamic-string":
|
|
119
|
-
return "readonly string[]";
|
|
120
|
-
case "struct":
|
|
121
|
-
return `readonly ${field.element.typeName}ViewInput[]`;
|
|
122
|
-
case "pointer":
|
|
123
|
-
return "readonly (number | null)[]";
|
|
124
|
-
}
|
|
125
|
-
}
|
|
33
|
+
export function emitProjectionFileWithSourceMap(layouts, generatedFile, options = {}) {
|
|
34
|
+
const sourceMapUrl = `${generatedFile.split(/[\\/]/).pop() ?? generatedFile}.map`;
|
|
35
|
+
const code = `${emitAstCheckedSource(emitProjectionFileText(layouts, options), generatedFile).code}\n//# sourceMappingURL=${sourceMapUrl}\n`;
|
|
36
|
+
return {
|
|
37
|
+
code,
|
|
38
|
+
sourceMap: createProjectionSourceMap(code, layouts, generatedFile),
|
|
39
|
+
};
|
|
126
40
|
}
|
|
127
41
|
function emitLayoutConstants(layout) {
|
|
128
42
|
const lines = [];
|
|
@@ -134,8 +48,8 @@ function emitLayoutConstants(layout) {
|
|
|
134
48
|
return lines;
|
|
135
49
|
}
|
|
136
50
|
function emitStructClass(layout, options, layoutMap) {
|
|
137
|
-
const scalarFields = layout.fields.filter((field) => field.kind === "scalar");
|
|
138
51
|
const littleEndianDefault = toLittleEndianLiteral(layout);
|
|
52
|
+
const scanKernelMode = normalizeScanKernelMode(options.scanKernels);
|
|
139
53
|
const lines = [`export class ${layout.name}View extends ProjectionView {`];
|
|
140
54
|
lines.push(...method `
|
|
141
55
|
static readonly byteLength = ${layout.byteLength};
|
|
@@ -162,60 +76,25 @@ private static assertPointerTargetRange(view: DataView, targetOffset: number, by
|
|
|
162
76
|
}
|
|
163
77
|
}`);
|
|
164
78
|
}
|
|
165
|
-
if (
|
|
79
|
+
if (hasScanKernels(layout, scanKernelMode)) {
|
|
166
80
|
lines.push("");
|
|
167
|
-
lines.push(...
|
|
81
|
+
lines.push(...emitScanRangeHelper(layout));
|
|
168
82
|
}
|
|
169
83
|
lines.push("");
|
|
170
|
-
|
|
171
|
-
lines.push(...method `
|
|
172
|
-
constructor(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}) {
|
|
173
|
-
super(view, baseOffset, littleEndian);
|
|
174
|
-
this.$refreshOffsets();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private $refreshOffsets(): void {
|
|
178
|
-
${scalarFields.map((field) => ` this.$${field.name}Offset = this.baseOffset + ${field.offset};`)}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
override rebase(baseOffset: number): this {
|
|
182
|
-
super.rebase(baseOffset);
|
|
183
|
-
this.$refreshOffsets();
|
|
184
|
-
return this;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
override rebaseUnchecked(baseOffset: number): this {
|
|
188
|
-
super.rebaseUnchecked(baseOffset);
|
|
189
|
-
this.$refreshOffsets();
|
|
190
|
-
return this;
|
|
191
|
-
}`);
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
lines.push(...method `
|
|
84
|
+
lines.push(...method `
|
|
195
85
|
constructor(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}) {
|
|
196
86
|
super(view, baseOffset, littleEndian);
|
|
197
87
|
}`);
|
|
198
|
-
}
|
|
199
88
|
lines.push("");
|
|
200
89
|
lines.push(...method `
|
|
201
90
|
static at(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${layout.name}View {
|
|
202
91
|
return new ${layout.name}View(view, baseOffset, littleEndian);
|
|
203
92
|
}`);
|
|
204
93
|
lines.push("");
|
|
205
|
-
|
|
206
|
-
lines.push(...method `
|
|
207
|
-
moveTo(index: number): this {
|
|
208
|
-
this.moveToIndex(index, ${layout.name}View.byteLength);
|
|
209
|
-
this.$refreshOffsets();
|
|
210
|
-
return this;
|
|
211
|
-
}`);
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
lines.push(...method `
|
|
94
|
+
lines.push(...method `
|
|
215
95
|
moveTo(index: number): this {
|
|
216
96
|
return this.moveToIndex(index, ${layout.name}View.byteLength);
|
|
217
97
|
}`);
|
|
218
|
-
}
|
|
219
98
|
lines.push("");
|
|
220
99
|
lines.push(...method `
|
|
221
100
|
moveToUnchecked(index: number): this {
|
|
@@ -237,14 +116,14 @@ moveToUnchecked(index: number): this {
|
|
|
237
116
|
lines.push("");
|
|
238
117
|
}
|
|
239
118
|
for (const field of layout.fields) {
|
|
240
|
-
const staticAccessorLines = emitStaticFieldAccessor(layout, field);
|
|
119
|
+
const staticAccessorLines = emitStaticFieldAccessor(layout, field, scanKernelMode);
|
|
241
120
|
for (const line of staticAccessorLines) {
|
|
242
121
|
lines.push(line);
|
|
243
122
|
}
|
|
244
123
|
if (staticAccessorLines.length > 0) {
|
|
245
124
|
lines.push("");
|
|
246
125
|
}
|
|
247
|
-
for (const line of emitField(layout, field
|
|
126
|
+
for (const line of emitField(layout, field)) {
|
|
248
127
|
lines.push(line);
|
|
249
128
|
}
|
|
250
129
|
lines.push("");
|
|
@@ -315,6 +194,18 @@ static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${scalar
|
|
|
315
194
|
return method `
|
|
316
195
|
static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${field.element.typeName}ViewInput[]) {
|
|
317
196
|
return writer.writeStructVector(${layout.name}View.${field.name}Offset, values, ${field.element.byteLength}, (view, value, baseOffset, littleEndian) => ${field.element.typeName}View.write(view, value, baseOffset, littleEndian), ${alignment});
|
|
197
|
+
}`;
|
|
198
|
+
},
|
|
199
|
+
"dynamic-struct": ({ layout, layoutMap, field, pascalName }) => {
|
|
200
|
+
const elementLayout = layoutMap.get(field.element.typeName);
|
|
201
|
+
const alignment = elementLayout?.alignment ?? 1;
|
|
202
|
+
const elementByteLength = elementLayout?.byteLength ?? field.element.byteLength;
|
|
203
|
+
const writeElement = elementLayout !== undefined && hasTailWriterFields(elementLayout, layoutMap)
|
|
204
|
+
? `${field.element.typeName}View.writeInto(view, elementWriter, value, baseOffset, littleEndian)`
|
|
205
|
+
: `${field.element.typeName}View.write(view, value, baseOffset, littleEndian)`;
|
|
206
|
+
return method `
|
|
207
|
+
static write${pascalName}(writer: DynamicLayoutWriter, values: readonly ${field.element.typeName}ViewInput[]) {
|
|
208
|
+
return writer.writeDynamicStructVector(${layout.name}View.${field.name}Offset, values, ${elementByteLength}, (view, elementWriter, value, baseOffset, littleEndian) => ${writeElement}, ${alignment});
|
|
318
209
|
}`;
|
|
319
210
|
},
|
|
320
211
|
pointer: ({ layout, field, pascalName }) => method `
|
|
@@ -334,20 +225,28 @@ function emitObjectWriterMethod(layout, layoutMap) {
|
|
|
334
225
|
const returnType = hasTailFields ? "DynamicLayoutWriter" : "void";
|
|
335
226
|
const bodyLines = [];
|
|
336
227
|
if (hasTailFields) {
|
|
337
|
-
bodyLines.push(` const writer = ${layout.name}View.createWriter(view, baseOffset, ${layout.name}View.byteLength, littleEndian)
|
|
338
|
-
}
|
|
339
|
-
for (const field of layout.fields) {
|
|
340
|
-
bodyLines.push(...emitObjectFieldWrite(layout, field));
|
|
228
|
+
bodyLines.push(` const writer = ${layout.name}View.createWriter(view, baseOffset, ${layout.name}View.byteLength, littleEndian);`, ` ${layout.name}View.writeInto(view, writer, value, baseOffset, littleEndian);`, " return writer;");
|
|
341
229
|
}
|
|
342
|
-
|
|
343
|
-
|
|
230
|
+
else {
|
|
231
|
+
for (const field of layout.fields) {
|
|
232
|
+
bodyLines.push(...emitObjectFieldWrite(layout, layoutMap, field));
|
|
233
|
+
}
|
|
344
234
|
}
|
|
345
|
-
|
|
235
|
+
const lines = method `
|
|
346
236
|
static write(view: DataView, value: ${layout.name}ViewInput, baseOffset = 0, littleEndian = ${toLittleEndianLiteral(layout)}): ${returnType} {
|
|
347
237
|
${bodyLines}
|
|
348
238
|
}`;
|
|
239
|
+
if (hasTailFields) {
|
|
240
|
+
const writeIntoLines = layout.fields.flatMap((field) => emitObjectFieldWriteAtBase(layout, layoutMap, field));
|
|
241
|
+
lines.push("");
|
|
242
|
+
lines.push(...method `
|
|
243
|
+
static writeInto(view: DataView, writer: DynamicLayoutWriter, value: ${layout.name}ViewInput, baseOffset = 0, littleEndian = ${toLittleEndianLiteral(layout)}): void {
|
|
244
|
+
${writeIntoLines}
|
|
245
|
+
}`);
|
|
246
|
+
}
|
|
247
|
+
return lines;
|
|
349
248
|
}
|
|
350
|
-
function emitObjectFieldWrite(layout, field) {
|
|
249
|
+
function emitObjectFieldWrite(layout, layoutMap, field) {
|
|
351
250
|
const pascalName = toPascalCase(field.name);
|
|
352
251
|
switch (field.kind) {
|
|
353
252
|
case "scalar":
|
|
@@ -366,6 +265,11 @@ function emitObjectFieldWrite(layout, field) {
|
|
|
366
265
|
case "dynamic-bytes":
|
|
367
266
|
return [` ${layout.name}View.write${pascalName}(writer, value.${field.name});`];
|
|
368
267
|
case "struct":
|
|
268
|
+
if (structFieldHasTailFields(layoutMap, field.typeName)) {
|
|
269
|
+
return [
|
|
270
|
+
` ${field.typeName}View.writeInto(view, writer, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
|
|
271
|
+
];
|
|
272
|
+
}
|
|
369
273
|
return [
|
|
370
274
|
` ${field.typeName}View.write(view, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
|
|
371
275
|
];
|
|
@@ -379,13 +283,140 @@ function emitObjectFieldWrite(layout, field) {
|
|
|
379
283
|
return [` ${layout.name}View.write${pascalName}(writer, value.${field.name});`];
|
|
380
284
|
}
|
|
381
285
|
}
|
|
382
|
-
function
|
|
286
|
+
function emitObjectFieldWriteAtBase(layout, layoutMap, field) {
|
|
287
|
+
const pascalName = toPascalCase(field.name);
|
|
288
|
+
switch (field.kind) {
|
|
289
|
+
case "scalar":
|
|
290
|
+
return [
|
|
291
|
+
` ${layout.name}View.set${pascalName}(view, value.${field.name}, baseOffset, littleEndian);`,
|
|
292
|
+
];
|
|
293
|
+
case "fixed-bytes":
|
|
294
|
+
return [
|
|
295
|
+
` writeFixedBytes(view.buffer, view.byteOffset + baseOffset + ${field.offset}, ${field.byteLength}, value.${field.name});`,
|
|
296
|
+
];
|
|
297
|
+
case "fixed-string":
|
|
298
|
+
return [
|
|
299
|
+
` writeFixedText(view.buffer, view.byteOffset + baseOffset + ${field.offset}, ${field.byteLength}, value.${field.name}, ${encodingLiteral(field.encoding)});`,
|
|
300
|
+
];
|
|
301
|
+
case "dynamic-string":
|
|
302
|
+
return [
|
|
303
|
+
` writer.writeTextAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${encodingLiteral(field.encoding)});`,
|
|
304
|
+
];
|
|
305
|
+
case "dynamic-bytes":
|
|
306
|
+
return [
|
|
307
|
+
` writer.writeBytesAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name});`,
|
|
308
|
+
];
|
|
309
|
+
case "struct":
|
|
310
|
+
if (structFieldHasTailFields(layoutMap, field.typeName)) {
|
|
311
|
+
return [
|
|
312
|
+
` ${field.typeName}View.writeInto(view, writer, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
|
|
313
|
+
];
|
|
314
|
+
}
|
|
315
|
+
return [
|
|
316
|
+
` ${field.typeName}View.write(view, value.${field.name}, baseOffset + ${field.offset}, littleEndian);`,
|
|
317
|
+
];
|
|
318
|
+
case "pointer":
|
|
319
|
+
return [
|
|
320
|
+
` ${layout.name}View.set${pascalName}TargetOffset(view, value.${field.name}, baseOffset, littleEndian);`,
|
|
321
|
+
];
|
|
322
|
+
case "fixed-array":
|
|
323
|
+
return emitFixedArrayObjectFieldWrite(field, encodingLiteral);
|
|
324
|
+
case "vector":
|
|
325
|
+
return emitVectorObjectFieldWriteAtBase(layout, layoutMap, field);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function emitVectorObjectFieldWriteAtBase(layout, layoutMap, field) {
|
|
329
|
+
switch (field.element.kind) {
|
|
330
|
+
case "dynamic-string":
|
|
331
|
+
return [
|
|
332
|
+
` writer.writeTextVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${encodingLiteral(field.element.encoding)});`,
|
|
333
|
+
];
|
|
334
|
+
case "dynamic-bytes":
|
|
335
|
+
return [
|
|
336
|
+
` writer.writeBytesVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name});`,
|
|
337
|
+
];
|
|
338
|
+
case "fixed-bytes":
|
|
339
|
+
return [
|
|
340
|
+
` writer.writeFixedBytesVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.byteLength});`,
|
|
341
|
+
];
|
|
342
|
+
case "fixed-string":
|
|
343
|
+
return [
|
|
344
|
+
` writer.writeFixedTextVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.byteLength}, ${encodingLiteral(field.element.encoding)});`,
|
|
345
|
+
];
|
|
346
|
+
case "scalar":
|
|
347
|
+
return [
|
|
348
|
+
` writer.writeScalarVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, "${field.element.scalar}", value.${field.name});`,
|
|
349
|
+
];
|
|
350
|
+
case "struct": {
|
|
351
|
+
const elementLayout = layoutMap.get(field.element.typeName);
|
|
352
|
+
const alignment = elementLayout?.alignment ?? 1;
|
|
353
|
+
return [
|
|
354
|
+
` writer.writeStructVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.byteLength}, (view, elementValue, elementBaseOffset, elementLittleEndian) => ${field.element.typeName}View.write(view, elementValue, elementBaseOffset, elementLittleEndian), ${alignment});`,
|
|
355
|
+
];
|
|
356
|
+
}
|
|
357
|
+
case "dynamic-struct": {
|
|
358
|
+
const elementLayout = layoutMap.get(field.element.typeName);
|
|
359
|
+
const alignment = elementLayout?.alignment ?? 1;
|
|
360
|
+
const elementByteLength = elementLayout?.byteLength ?? field.element.byteLength;
|
|
361
|
+
const writeElement = elementLayout !== undefined && hasTailWriterFields(elementLayout, layoutMap)
|
|
362
|
+
? `${field.element.typeName}View.writeInto(view, elementWriter, elementValue, elementBaseOffset, elementLittleEndian)`
|
|
363
|
+
: `${field.element.typeName}View.write(view, elementValue, elementBaseOffset, elementLittleEndian)`;
|
|
364
|
+
return [
|
|
365
|
+
` writer.writeDynamicStructVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${elementByteLength}, (view, elementWriter, elementValue, elementBaseOffset, elementLittleEndian) => ${writeElement}, ${alignment});`,
|
|
366
|
+
];
|
|
367
|
+
}
|
|
368
|
+
case "pointer":
|
|
369
|
+
return [
|
|
370
|
+
` writer.writePointerVectorAtBase(baseOffset, ${layout.name}View.${field.name}Offset, value.${field.name}, ${field.element.targetTypeName}View.byteLength);`,
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function emitStaticFieldAccessor(layout, field, scanKernelMode) {
|
|
383
375
|
if (field.kind === "pointer") {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
376
|
+
return emitStaticPointerAccessor(layout, field);
|
|
377
|
+
}
|
|
378
|
+
return field.kind === "scalar" ? emitStaticScalarAccessor(layout, field, scanKernelMode) : [];
|
|
379
|
+
}
|
|
380
|
+
function emitStaticScalarAccessor(layout, field, scanKernelMode) {
|
|
381
|
+
const getterMethod = scalarGetterMethod(field.scalar);
|
|
382
|
+
const setterMethod = scalarSetterMethod(field.scalar);
|
|
383
|
+
const typeName = scalarTsType(field.scalar);
|
|
384
|
+
const littleEndianDefault = toLittleEndianLiteral(layout);
|
|
385
|
+
const endianArg = field.byteLength === 1 || field.scalar === "bool" ? "" : ", littleEndian";
|
|
386
|
+
const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
|
|
387
|
+
const pascalName = toPascalCase(field.name);
|
|
388
|
+
return [
|
|
389
|
+
` static get${pascalName}(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${typeName} {`,
|
|
390
|
+
emitStaticScalarReadBody(field, getterMethod, `baseOffset + ${field.offset}`, endianArg),
|
|
391
|
+
" }",
|
|
392
|
+
` static set${pascalName}(view: DataView, value: ${typeName}, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {`,
|
|
393
|
+
emitStaticScalarWriteBody(field, setterMethod, `baseOffset + ${field.offset}`, endianArg),
|
|
394
|
+
" }",
|
|
395
|
+
` static get${pascalName}At(view: DataView, index: number, littleEndian = ${littleEndianDefault}): ${typeName} {`,
|
|
396
|
+
emitStaticScalarReadBody(field, getterMethod, indexOffset, endianArg),
|
|
397
|
+
" }",
|
|
398
|
+
` static set${pascalName}At(view: DataView, value: ${typeName}, index: number, littleEndian = ${littleEndianDefault}): void {`,
|
|
399
|
+
emitStaticScalarWriteBody(field, setterMethod, indexOffset, endianArg),
|
|
400
|
+
" }",
|
|
401
|
+
...emitScalarScanKernels(layout, field, getterMethod, littleEndianDefault, pascalName, scanKernelMode),
|
|
402
|
+
];
|
|
403
|
+
}
|
|
404
|
+
function emitStaticScalarReadBody(field, getterMethod, offsetExpr, endianArg) {
|
|
405
|
+
return field.scalar === "bool"
|
|
406
|
+
? ` return view.${getterMethod}(${offsetExpr}) !== 0;`
|
|
407
|
+
: ` return view.${getterMethod}(${offsetExpr}${endianArg});`;
|
|
408
|
+
}
|
|
409
|
+
function emitStaticScalarWriteBody(field, setterMethod, offsetExpr, endianArg) {
|
|
410
|
+
return field.scalar === "bool"
|
|
411
|
+
? ` view.${setterMethod}(${offsetExpr}, value ? 1 : 0);`
|
|
412
|
+
: ` view.${setterMethod}(${offsetExpr}, value${endianArg});`;
|
|
413
|
+
}
|
|
414
|
+
function emitStaticPointerAccessor(layout, field) {
|
|
415
|
+
const pascalName = toPascalCase(field.name);
|
|
416
|
+
const littleEndianDefault = toLittleEndianLiteral(layout);
|
|
417
|
+
const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
|
|
418
|
+
const pointerPosition = `baseOffset + ${field.offset}`;
|
|
419
|
+
return method `
|
|
389
420
|
static getRaw${pascalName}RelativeOffset(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): number {
|
|
390
421
|
return view.getUint32(baseOffset + ${field.offset}, littleEndian);
|
|
391
422
|
}
|
|
@@ -448,87 +479,17 @@ static set${pascalName}RelativeOffsetAt(view: DataView, value: number | null, in
|
|
|
448
479
|
${layout.name}View.assertPointer32Payload(value);
|
|
449
480
|
view.setInt32(${indexOffset}, value, littleEndian);
|
|
450
481
|
}`;
|
|
451
|
-
}
|
|
452
|
-
if (field.kind !== "scalar") {
|
|
453
|
-
return [];
|
|
454
|
-
}
|
|
455
|
-
const getterMethod = scalarGetterMethod(field.scalar);
|
|
456
|
-
const setterMethod = scalarSetterMethod(field.scalar);
|
|
457
|
-
const typeName = scalarTsType(field.scalar);
|
|
458
|
-
const littleEndianDefault = toLittleEndianLiteral(layout);
|
|
459
|
-
const endianArg = field.byteLength === 1 || field.scalar === "bool" ? "" : ", littleEndian";
|
|
460
|
-
const getterBody = field.scalar === "bool"
|
|
461
|
-
? ` return view.${getterMethod}(baseOffset + ${field.offset}) !== 0;`
|
|
462
|
-
: ` return view.${getterMethod}(baseOffset + ${field.offset}${endianArg});`;
|
|
463
|
-
const setterBody = field.scalar === "bool"
|
|
464
|
-
? ` view.${setterMethod}(baseOffset + ${field.offset}, value ? 1 : 0);`
|
|
465
|
-
: ` view.${setterMethod}(baseOffset + ${field.offset}, value${endianArg});`;
|
|
466
|
-
const indexOffset = `index * ${layout.byteLength} + ${field.offset}`;
|
|
467
|
-
const indexGetterBody = field.scalar === "bool"
|
|
468
|
-
? ` return view.${getterMethod}(${indexOffset}) !== 0;`
|
|
469
|
-
: ` return view.${getterMethod}(${indexOffset}${endianArg});`;
|
|
470
|
-
const indexSetterBody = field.scalar === "bool"
|
|
471
|
-
? ` view.${setterMethod}(${indexOffset}, value ? 1 : 0);`
|
|
472
|
-
: ` view.${setterMethod}(${indexOffset}, value${endianArg});`;
|
|
473
|
-
const pascalName = toPascalCase(field.name);
|
|
474
|
-
return [
|
|
475
|
-
` static get${pascalName}(view: DataView, baseOffset = 0, littleEndian = ${littleEndianDefault}): ${typeName} {`,
|
|
476
|
-
getterBody,
|
|
477
|
-
" }",
|
|
478
|
-
` static set${pascalName}(view: DataView, value: ${typeName}, baseOffset = 0, littleEndian = ${littleEndianDefault}): void {`,
|
|
479
|
-
setterBody,
|
|
480
|
-
" }",
|
|
481
|
-
` static get${pascalName}At(view: DataView, index: number, littleEndian = ${littleEndianDefault}): ${typeName} {`,
|
|
482
|
-
indexGetterBody,
|
|
483
|
-
" }",
|
|
484
|
-
` static set${pascalName}At(view: DataView, value: ${typeName}, index: number, littleEndian = ${littleEndianDefault}): void {`,
|
|
485
|
-
indexSetterBody,
|
|
486
|
-
" }",
|
|
487
|
-
...emitScalarSumKernel(layout, field, getterMethod, littleEndianDefault, pascalName),
|
|
488
|
-
];
|
|
489
|
-
}
|
|
490
|
-
function emitScalarSumKernel(layout, field, getterMethod, littleEndianDefault, pascalName) {
|
|
491
|
-
if (!isNumberSumScalar(field.scalar)) {
|
|
492
|
-
return [];
|
|
493
|
-
}
|
|
494
|
-
const endianArg = field.byteLength === 1 ? "" : ", littleEndian";
|
|
495
|
-
return method `
|
|
496
|
-
static sum${pascalName}(view: DataView, count: number, baseOffset = 0, littleEndian = ${littleEndianDefault}): number {
|
|
497
|
-
if (!Number.isInteger(count) || count < 0) {
|
|
498
|
-
throw new RangeError(\`Invalid record count: \${count}\`);
|
|
499
|
-
}
|
|
500
|
-
if (!Number.isFinite(baseOffset) || !Number.isInteger(baseOffset) || baseOffset < 0) {
|
|
501
|
-
throw new RangeError(\`Invalid base offset: \${baseOffset}\`);
|
|
502
|
-
}
|
|
503
|
-
if (count === 0) {
|
|
504
|
-
return 0;
|
|
505
|
-
}
|
|
506
|
-
const start = baseOffset + ${field.offset};
|
|
507
|
-
const limit = start + count * ${layout.byteLength};
|
|
508
|
-
const lastByte = start + (count - 1) * ${layout.byteLength} + ${field.byteLength};
|
|
509
|
-
if (lastByte > view.byteLength) {
|
|
510
|
-
throw new RangeError(\`scan range exceeds DataView length \${view.byteLength}\`);
|
|
511
|
-
}
|
|
512
|
-
let sum = 0;
|
|
513
|
-
for (let offset = start; offset < limit; offset += ${layout.byteLength}) {
|
|
514
|
-
sum += view.${getterMethod}(offset${endianArg});
|
|
515
|
-
}
|
|
516
|
-
return sum;
|
|
517
|
-
}`;
|
|
518
|
-
}
|
|
519
|
-
function isNumberSumScalar(kind) {
|
|
520
|
-
return kind !== "i64" && kind !== "u64" && kind !== "bool";
|
|
521
482
|
}
|
|
522
|
-
function emitField(layout, field
|
|
483
|
+
function emitField(layout, field) {
|
|
484
|
+
// This switch intentionally mirrors the Layout IR field-kind surface.
|
|
485
|
+
// Split it only when a dispatch table removes real emitter complexity.
|
|
523
486
|
switch (field.kind) {
|
|
524
487
|
case "scalar": {
|
|
525
488
|
const getterMethod = scalarGetterMethod(field.scalar);
|
|
526
489
|
const setterMethod = scalarSetterMethod(field.scalar);
|
|
527
490
|
const typeName = scalarTsType(field.scalar);
|
|
528
491
|
const getterArgs = field.byteLength === 1 || field.scalar === "bool" ? "" : ", this.littleEndian";
|
|
529
|
-
const instanceOffset =
|
|
530
|
-
? `this.$${field.name}Offset`
|
|
531
|
-
: `this.baseOffset + ${field.offset}`;
|
|
492
|
+
const instanceOffset = `this.baseOffset + ${field.offset}`;
|
|
532
493
|
const getterBody = field.scalar === "bool"
|
|
533
494
|
? `return this.view.${getterMethod}(${instanceOffset}) !== 0;`
|
|
534
495
|
: `return this.view.${getterMethod}(${instanceOffset}${getterArgs});`;
|
|
@@ -575,8 +536,58 @@ ${field.name}View(): ${field.typeName}View {
|
|
|
575
536
|
return new ${field.typeName}View(this.view, this.absoluteOffset(${field.offset}), this.littleEndian);
|
|
576
537
|
}`;
|
|
577
538
|
case "pointer": {
|
|
578
|
-
|
|
579
|
-
|
|
539
|
+
return emitPointerField(layout, field);
|
|
540
|
+
}
|
|
541
|
+
case "fixed-array":
|
|
542
|
+
return emitFixedArrayFieldAccessor(field, encodingLiteral);
|
|
543
|
+
case "vector":
|
|
544
|
+
switch (field.element.kind) {
|
|
545
|
+
case "scalar":
|
|
546
|
+
return method `
|
|
547
|
+
${field.name}View(): ScalarVectorView<${scalarTsType(field.element.scalar)}> {
|
|
548
|
+
return new ScalarVectorView(this.view, ${field.offset}, "${field.element.scalar}", this.baseOffset, this.littleEndian);
|
|
549
|
+
}`;
|
|
550
|
+
case "dynamic-string":
|
|
551
|
+
return method `
|
|
552
|
+
${field.name}View(): Utf8VectorView {
|
|
553
|
+
return new Utf8VectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
|
|
554
|
+
}`;
|
|
555
|
+
case "dynamic-bytes":
|
|
556
|
+
return method `
|
|
557
|
+
${field.name}View(): BytesVectorView {
|
|
558
|
+
return new BytesVectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian);
|
|
559
|
+
}`;
|
|
560
|
+
case "fixed-bytes":
|
|
561
|
+
return method `
|
|
562
|
+
${field.name}View(): FixedBytesVectorView {
|
|
563
|
+
return new FixedBytesVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian);
|
|
564
|
+
}`;
|
|
565
|
+
case "fixed-string":
|
|
566
|
+
return method `
|
|
567
|
+
${field.name}View(): FixedStringVectorView {
|
|
568
|
+
return new FixedStringVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
|
|
569
|
+
}`;
|
|
570
|
+
case "struct":
|
|
571
|
+
return method `
|
|
572
|
+
${field.name}View(): StructVectorView<${field.element.typeName}View> {
|
|
573
|
+
return new StructVectorView(this.view, ${field.offset}, ${field.element.byteLength}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
|
|
574
|
+
}`;
|
|
575
|
+
case "dynamic-struct":
|
|
576
|
+
return method `
|
|
577
|
+
${field.name}View(): DynamicStructVectorView<${field.element.typeName}View> {
|
|
578
|
+
return new DynamicStructVectorView(this.view, ${field.offset}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
|
|
579
|
+
}`;
|
|
580
|
+
case "pointer":
|
|
581
|
+
return method `
|
|
582
|
+
${field.name}View(): PointerVectorView<${field.element.targetTypeName}View> {
|
|
583
|
+
return new PointerVectorView(this.view, ${field.offset}, ${field.element.targetTypeName}View.byteLength, (view, baseOffset, littleEndian) => new ${field.element.targetTypeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
|
|
584
|
+
}`;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function emitPointerField(layout, field) {
|
|
589
|
+
const pascalName = toPascalCase(field.name);
|
|
590
|
+
return method `
|
|
580
591
|
get raw${pascalName}RelativeOffset(): number {
|
|
581
592
|
return this.view.getUint32(this.baseOffset + ${field.offset}, this.littleEndian);
|
|
582
593
|
}
|
|
@@ -643,104 +654,5 @@ ${field.name}Into(out: ${field.targetTypeName}View): boolean {
|
|
|
643
654
|
out.moveToOffset(targetOffset, ${field.targetTypeName}View.byteLength);
|
|
644
655
|
return true;
|
|
645
656
|
}`;
|
|
646
|
-
}
|
|
647
|
-
case "fixed-array":
|
|
648
|
-
return emitFixedArrayFieldAccessor(field, encodingLiteral);
|
|
649
|
-
case "vector":
|
|
650
|
-
switch (field.element.kind) {
|
|
651
|
-
case "scalar":
|
|
652
|
-
return method `
|
|
653
|
-
${field.name}View(): ScalarVectorView<${scalarTsType(field.element.scalar)}> {
|
|
654
|
-
return new ScalarVectorView(this.view, ${field.offset}, "${field.element.scalar}", this.baseOffset, this.littleEndian);
|
|
655
|
-
}`;
|
|
656
|
-
case "dynamic-string":
|
|
657
|
-
return method `
|
|
658
|
-
${field.name}View(): Utf8VectorView {
|
|
659
|
-
return new Utf8VectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
|
|
660
|
-
}`;
|
|
661
|
-
case "dynamic-bytes":
|
|
662
|
-
return method `
|
|
663
|
-
${field.name}View(): BytesVectorView {
|
|
664
|
-
return new BytesVectorView(this.view, ${field.offset}, this.baseOffset, this.littleEndian);
|
|
665
|
-
}`;
|
|
666
|
-
case "fixed-bytes":
|
|
667
|
-
return method `
|
|
668
|
-
${field.name}View(): FixedBytesVectorView {
|
|
669
|
-
return new FixedBytesVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian);
|
|
670
|
-
}`;
|
|
671
|
-
case "fixed-string":
|
|
672
|
-
return method `
|
|
673
|
-
${field.name}View(): FixedStringVectorView {
|
|
674
|
-
return new FixedStringVectorView(this.view, ${field.offset}, ${field.element.byteLength}, this.baseOffset, this.littleEndian, ${encodingLiteral(field.element.encoding)});
|
|
675
|
-
}`;
|
|
676
|
-
case "struct":
|
|
677
|
-
return method `
|
|
678
|
-
${field.name}View(): StructVectorView<${field.element.typeName}View> {
|
|
679
|
-
return new StructVectorView(this.view, ${field.offset}, ${field.element.byteLength}, (view, baseOffset, littleEndian) => new ${field.element.typeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
|
|
680
|
-
}`;
|
|
681
|
-
case "pointer":
|
|
682
|
-
return method `
|
|
683
|
-
${field.name}View(): PointerVectorView<${field.element.targetTypeName}View> {
|
|
684
|
-
return new PointerVectorView(this.view, ${field.offset}, ${field.element.targetTypeName}View.byteLength, (view, baseOffset, littleEndian) => new ${field.element.targetTypeName}View(view, baseOffset, littleEndian), this.baseOffset, this.littleEndian);
|
|
685
|
-
}`;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
function toPascalCase(name) {
|
|
690
|
-
return name.slice(0, 1).toUpperCase() + name.slice(1);
|
|
691
|
-
}
|
|
692
|
-
function toLittleEndianLiteral(layout) {
|
|
693
|
-
return layout.endianness === "little" ? "true" : "false";
|
|
694
|
-
}
|
|
695
|
-
function encodingLiteral(encoding) {
|
|
696
|
-
return encoding === "ascii" ? "\"ascii\"" : "\"utf8\"";
|
|
697
|
-
}
|
|
698
|
-
function hasTailWriterFields(layout, layoutMap) {
|
|
699
|
-
return layout.fields.some((field) => isTailWriterField(field, layoutMap));
|
|
700
|
-
}
|
|
701
|
-
function isTailWriterField(field, layoutMap) {
|
|
702
|
-
switch (field.kind) {
|
|
703
|
-
case "dynamic-string":
|
|
704
|
-
case "dynamic-bytes":
|
|
705
|
-
return true;
|
|
706
|
-
case "vector":
|
|
707
|
-
return (field.element.kind === "scalar" ||
|
|
708
|
-
field.element.kind === "fixed-bytes" ||
|
|
709
|
-
field.element.kind === "fixed-string" ||
|
|
710
|
-
field.element.kind === "dynamic-string" ||
|
|
711
|
-
field.element.kind === "dynamic-bytes" ||
|
|
712
|
-
field.element.kind === "pointer" ||
|
|
713
|
-
canWriteStructVectorElement(field.element, layoutMap));
|
|
714
|
-
default:
|
|
715
|
-
return false;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
function canEmitObjectWriter(layout, layoutMap) {
|
|
719
|
-
return layout.fields.every((field) => canWriteObjectField(field, layoutMap));
|
|
720
|
-
}
|
|
721
|
-
function canWriteObjectField(field, layoutMap) {
|
|
722
|
-
switch (field.kind) {
|
|
723
|
-
case "scalar":
|
|
724
|
-
case "fixed-bytes":
|
|
725
|
-
case "fixed-string":
|
|
726
|
-
case "dynamic-string":
|
|
727
|
-
case "dynamic-bytes":
|
|
728
|
-
case "struct":
|
|
729
|
-
case "pointer":
|
|
730
|
-
return true;
|
|
731
|
-
case "fixed-array":
|
|
732
|
-
return (field.element.kind !== "struct" ||
|
|
733
|
-
canWriteFixedArrayStructElement(field.element, layoutMap, hasTailWriterFields));
|
|
734
|
-
case "vector":
|
|
735
|
-
return (field.element.kind !== "struct" ||
|
|
736
|
-
canWriteStructVectorElement(field.element, layoutMap));
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
function canWriteStructVectorElement(element, layoutMap) {
|
|
740
|
-
if (element.kind !== "struct") {
|
|
741
|
-
return false;
|
|
742
|
-
}
|
|
743
|
-
const elementLayout = layoutMap.get(element.typeName);
|
|
744
|
-
return elementLayout !== undefined && !hasTailWriterFields(elementLayout, layoutMap);
|
|
745
657
|
}
|
|
746
658
|
//# sourceMappingURL=emitter.js.map
|