@tsonic/emitter 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/array.test.d.ts +1 -1
- package/dist/array.test.js +17 -16
- package/dist/array.test.js.map +1 -1
- package/dist/core/attributes.test.js +2 -2
- package/dist/core/attributes.test.js.map +1 -1
- package/dist/core/imports.d.ts.map +1 -1
- package/dist/core/imports.js +1 -9
- package/dist/core/imports.js.map +1 -1
- package/dist/core/module-emitter/static-container.d.ts.map +1 -1
- package/dist/core/module-emitter/static-container.js +3 -1
- package/dist/core/module-emitter/static-container.js.map +1 -1
- package/dist/core/unsafe.d.ts +11 -0
- package/dist/core/unsafe.d.ts.map +1 -0
- package/dist/core/unsafe.js +264 -0
- package/dist/core/unsafe.js.map +1 -0
- package/dist/emitter-types/context.d.ts +1 -1
- package/dist/emitter-types/context.d.ts.map +1 -1
- package/dist/emitter-types/context.js +4 -3
- package/dist/emitter-types/context.js.map +1 -1
- package/dist/emitter-types/core.d.ts +4 -2
- package/dist/emitter-types/core.d.ts.map +1 -1
- package/dist/emitter-types/fqn.d.ts +0 -7
- package/dist/emitter-types/fqn.d.ts.map +1 -1
- package/dist/emitter-types/fqn.js +0 -8
- package/dist/emitter-types/fqn.js.map +1 -1
- package/dist/expression-emitter.d.ts.map +1 -1
- package/dist/expression-emitter.js +248 -70
- package/dist/expression-emitter.js.map +1 -1
- package/dist/expressions/access.d.ts.map +1 -1
- package/dist/expressions/access.js +36 -40
- package/dist/expressions/access.js.map +1 -1
- package/dist/expressions/calls.d.ts.map +1 -1
- package/dist/expressions/calls.js +233 -70
- package/dist/expressions/calls.js.map +1 -1
- package/dist/expressions/collections.d.ts.map +1 -1
- package/dist/expressions/collections.js +37 -41
- package/dist/expressions/collections.js.map +1 -1
- package/dist/expressions/functions.d.ts.map +1 -1
- package/dist/expressions/functions.js +30 -10
- package/dist/expressions/functions.js.map +1 -1
- package/dist/expressions/identifiers.d.ts.map +1 -1
- package/dist/expressions/identifiers.js +5 -30
- package/dist/expressions/identifiers.js.map +1 -1
- package/dist/expressions/index.test.js +87 -198
- package/dist/expressions/index.test.js.map +1 -1
- package/dist/expressions/literals.d.ts +4 -5
- package/dist/expressions/literals.d.ts.map +1 -1
- package/dist/expressions/literals.js +62 -15
- package/dist/expressions/literals.js.map +1 -1
- package/dist/expressions/operators.d.ts +0 -4
- package/dist/expressions/operators.d.ts.map +1 -1
- package/dist/expressions/operators.js +49 -19
- package/dist/expressions/operators.js.map +1 -1
- package/dist/expressions/other.d.ts.map +1 -1
- package/dist/expressions/other.js +21 -3
- package/dist/expressions/other.js.map +1 -1
- package/dist/extension-methods.test.d.ts +5 -0
- package/dist/extension-methods.test.d.ts.map +1 -0
- package/dist/extension-methods.test.js +110 -0
- package/dist/extension-methods.test.js.map +1 -0
- package/dist/generator-exchange.d.ts.map +1 -1
- package/dist/generator-exchange.js +2 -1
- package/dist/generator-exchange.js.map +1 -1
- package/dist/generator.test.js +9 -9
- package/dist/generator.test.js.map +1 -1
- package/dist/golden-tests/discovery.d.ts +6 -11
- package/dist/golden-tests/discovery.d.ts.map +1 -1
- package/dist/golden-tests/discovery.js +14 -79
- package/dist/golden-tests/discovery.js.map +1 -1
- package/dist/golden-tests/index.d.ts +1 -1
- package/dist/golden-tests/index.d.ts.map +1 -1
- package/dist/golden-tests/index.js.map +1 -1
- package/dist/golden-tests/runner.d.ts.map +1 -1
- package/dist/golden-tests/runner.js +6 -7
- package/dist/golden-tests/runner.js.map +1 -1
- package/dist/golden-tests/types.d.ts +0 -7
- package/dist/golden-tests/types.d.ts.map +1 -1
- package/dist/golden.test.d.ts +3 -10
- package/dist/golden.test.d.ts.map +1 -1
- package/dist/golden.test.js +8 -24
- package/dist/golden.test.js.map +1 -1
- package/dist/hierarchical-bindings.test.js +17 -11
- package/dist/hierarchical-bindings.test.js.map +1 -1
- package/dist/integration.test.js +61 -20
- package/dist/integration.test.js.map +1 -1
- package/dist/invariants/emitter-globals-subset.test.d.ts +1 -1
- package/dist/invariants/emitter-globals-subset.test.js +34 -74
- package/dist/invariants/emitter-globals-subset.test.js.map +1 -1
- package/dist/invariants/numeric-proof-contract.test.js +3 -3
- package/dist/invariants/numeric-proof-contract.test.js.map +1 -1
- package/dist/patterns.d.ts +98 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +366 -0
- package/dist/patterns.js.map +1 -0
- package/dist/patterns.test.d.ts +15 -0
- package/dist/patterns.test.d.ts.map +1 -0
- package/dist/patterns.test.js +1262 -0
- package/dist/patterns.test.js.map +1 -0
- package/dist/specialization/generation.d.ts.map +1 -1
- package/dist/specialization/generation.js +2 -2
- package/dist/specialization/generation.js.map +1 -1
- package/dist/specialization/substitution.d.ts.map +1 -1
- package/dist/specialization/substitution.js +4 -0
- package/dist/specialization/substitution.js.map +1 -1
- package/dist/specialization/type-aliases.test.js +1 -1
- package/dist/specialization/type-aliases.test.js.map +1 -1
- package/dist/statement-emitter.d.ts.map +1 -1
- package/dist/statement-emitter.js +1 -1
- package/dist/statement-emitter.js.map +1 -1
- package/dist/statements/blocks.d.ts.map +1 -1
- package/dist/statements/blocks.js +14 -45
- package/dist/statements/blocks.js.map +1 -1
- package/dist/statements/classes/index.d.ts +1 -1
- package/dist/statements/classes/index.d.ts.map +1 -1
- package/dist/statements/classes/index.js +1 -1
- package/dist/statements/classes/index.js.map +1 -1
- package/dist/statements/classes/inline-types.d.ts.map +1 -1
- package/dist/statements/classes/inline-types.js +11 -1
- package/dist/statements/classes/inline-types.js.map +1 -1
- package/dist/statements/classes/members/constructors.d.ts.map +1 -1
- package/dist/statements/classes/members/constructors.js +30 -12
- package/dist/statements/classes/members/constructors.js.map +1 -1
- package/dist/statements/classes/members/methods.d.ts.map +1 -1
- package/dist/statements/classes/members/methods.js +51 -23
- package/dist/statements/classes/members/methods.js.map +1 -1
- package/dist/statements/classes/members/orchestrator.d.ts.map +1 -1
- package/dist/statements/classes/members/orchestrator.js +1 -3
- package/dist/statements/classes/members/orchestrator.js.map +1 -1
- package/dist/statements/classes/members/properties.d.ts.map +1 -1
- package/dist/statements/classes/members/properties.js +56 -9
- package/dist/statements/classes/members/properties.js.map +1 -1
- package/dist/statements/classes/parameters.d.ts +43 -1
- package/dist/statements/classes/parameters.d.ts.map +1 -1
- package/dist/statements/classes/parameters.js +73 -13
- package/dist/statements/classes/parameters.js.map +1 -1
- package/dist/statements/classes/properties.d.ts.map +1 -1
- package/dist/statements/classes/properties.js +1 -1
- package/dist/statements/classes/properties.js.map +1 -1
- package/dist/statements/classes.d.ts +1 -1
- package/dist/statements/classes.d.ts.map +1 -1
- package/dist/statements/classes.js +1 -1
- package/dist/statements/classes.js.map +1 -1
- package/dist/statements/control/conditionals.d.ts.map +1 -1
- package/dist/statements/control/conditionals.js +78 -23
- package/dist/statements/control/conditionals.js.map +1 -1
- package/dist/statements/control/loops.d.ts.map +1 -1
- package/dist/statements/control/loops.js +25 -5
- package/dist/statements/control/loops.js.map +1 -1
- package/dist/statements/declarations/classes.d.ts.map +1 -1
- package/dist/statements/declarations/classes.js +128 -10
- package/dist/statements/declarations/classes.js.map +1 -1
- package/dist/statements/declarations/functions.d.ts.map +1 -1
- package/dist/statements/declarations/functions.js +28 -12
- package/dist/statements/declarations/functions.js.map +1 -1
- package/dist/statements/declarations/interfaces.d.ts.map +1 -1
- package/dist/statements/declarations/interfaces.js +50 -7
- package/dist/statements/declarations/interfaces.js.map +1 -1
- package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
- package/dist/statements/declarations/type-aliases.js +6 -1
- package/dist/statements/declarations/type-aliases.js.map +1 -1
- package/dist/statements/declarations/variables.d.ts.map +1 -1
- package/dist/statements/declarations/variables.js +177 -37
- package/dist/statements/declarations/variables.js.map +1 -1
- package/dist/statements/index.test.js +7 -5
- package/dist/statements/index.test.js.map +1 -1
- package/dist/types/arrays.d.ts +3 -9
- package/dist/types/arrays.d.ts.map +1 -1
- package/dist/types/arrays.js +5 -19
- package/dist/types/arrays.js.map +1 -1
- package/dist/types/parameters.d.ts.map +1 -1
- package/dist/types/parameters.js +36 -1
- package/dist/types/parameters.js.map +1 -1
- package/dist/types/parameters.test.js +1 -1
- package/dist/types/parameters.test.js.map +1 -1
- package/dist/types/references.d.ts.map +1 -1
- package/dist/types/references.js +86 -51
- package/dist/types/references.js.map +1 -1
- package/dist/types/references.test.js +35 -95
- package/dist/types/references.test.js.map +1 -1
- package/dist/types/unions.d.ts.map +1 -1
- package/dist/types/unions.js +20 -0
- package/dist/types/unions.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,1262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive Tests for Destructuring Pattern Lowering
|
|
3
|
+
*
|
|
4
|
+
* Tests all forms of destructuring:
|
|
5
|
+
* - Variable declaration destructuring (array and object)
|
|
6
|
+
* - For-of loop destructuring
|
|
7
|
+
* - Parameter destructuring
|
|
8
|
+
* - Assignment destructuring
|
|
9
|
+
* - Nested patterns
|
|
10
|
+
* - Rest elements
|
|
11
|
+
* - Default values
|
|
12
|
+
* - Edge cases
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it } from "mocha";
|
|
15
|
+
import { expect } from "chai";
|
|
16
|
+
import { emitModule } from "./emitter.js";
|
|
17
|
+
// Helper to create a minimal module with a function
|
|
18
|
+
const createModule = (statements, isStatic = false) => ({
|
|
19
|
+
kind: "module",
|
|
20
|
+
filePath: "/src/test.ts",
|
|
21
|
+
namespace: "TestApp",
|
|
22
|
+
className: "test",
|
|
23
|
+
isStaticContainer: isStatic,
|
|
24
|
+
imports: [],
|
|
25
|
+
body: isStatic
|
|
26
|
+
? statements
|
|
27
|
+
: [
|
|
28
|
+
{
|
|
29
|
+
kind: "functionDeclaration",
|
|
30
|
+
name: "testFunc",
|
|
31
|
+
parameters: [],
|
|
32
|
+
returnType: { kind: "voidType" },
|
|
33
|
+
body: {
|
|
34
|
+
kind: "blockStatement",
|
|
35
|
+
statements,
|
|
36
|
+
},
|
|
37
|
+
isExported: true,
|
|
38
|
+
isAsync: false,
|
|
39
|
+
isGenerator: false,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
exports: [],
|
|
43
|
+
});
|
|
44
|
+
// Helper to create array type
|
|
45
|
+
const arrayType = (elementType) => ({
|
|
46
|
+
kind: "arrayType",
|
|
47
|
+
elementType,
|
|
48
|
+
});
|
|
49
|
+
// Helper to create string type
|
|
50
|
+
const stringType = { kind: "primitiveType", name: "string" };
|
|
51
|
+
// Helper to create number type
|
|
52
|
+
const numberType = { kind: "primitiveType", name: "number" };
|
|
53
|
+
describe("Destructuring Pattern Lowering", () => {
|
|
54
|
+
describe("Variable Declaration - Array Patterns", () => {
|
|
55
|
+
it("should lower simple array destructuring", () => {
|
|
56
|
+
const module = createModule([
|
|
57
|
+
{
|
|
58
|
+
kind: "variableDeclaration",
|
|
59
|
+
declarationKind: "const",
|
|
60
|
+
declarations: [
|
|
61
|
+
{
|
|
62
|
+
kind: "variableDeclarator",
|
|
63
|
+
name: {
|
|
64
|
+
kind: "arrayPattern",
|
|
65
|
+
elements: [
|
|
66
|
+
{
|
|
67
|
+
pattern: { kind: "identifierPattern", name: "a" },
|
|
68
|
+
isRest: false,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pattern: { kind: "identifierPattern", name: "b" },
|
|
72
|
+
isRest: false,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
type: arrayType(numberType),
|
|
77
|
+
initializer: { kind: "identifier", name: "arr" },
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
isExported: false,
|
|
81
|
+
},
|
|
82
|
+
]);
|
|
83
|
+
const result = emitModule(module);
|
|
84
|
+
// Should create temp variable
|
|
85
|
+
expect(result).to.include("var __arr0 = arr;");
|
|
86
|
+
// Should emit indexed access for each element with element type
|
|
87
|
+
expect(result).to.include("double a = __arr0[0];");
|
|
88
|
+
expect(result).to.include("double b = __arr0[1];");
|
|
89
|
+
});
|
|
90
|
+
it("should lower array destructuring with rest element", () => {
|
|
91
|
+
const module = createModule([
|
|
92
|
+
{
|
|
93
|
+
kind: "variableDeclaration",
|
|
94
|
+
declarationKind: "const",
|
|
95
|
+
declarations: [
|
|
96
|
+
{
|
|
97
|
+
kind: "variableDeclarator",
|
|
98
|
+
name: {
|
|
99
|
+
kind: "arrayPattern",
|
|
100
|
+
elements: [
|
|
101
|
+
{
|
|
102
|
+
pattern: { kind: "identifierPattern", name: "first" },
|
|
103
|
+
isRest: false,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
pattern: { kind: "identifierPattern", name: "rest" },
|
|
107
|
+
isRest: true,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
type: arrayType(stringType),
|
|
112
|
+
initializer: { kind: "identifier", name: "items" },
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
isExported: false,
|
|
116
|
+
},
|
|
117
|
+
]);
|
|
118
|
+
const result = emitModule(module);
|
|
119
|
+
// Should use ArrayHelpers.Slice for rest (typed with element type)
|
|
120
|
+
expect(result).to.include("string first = __arr0[0];");
|
|
121
|
+
expect(result).to.include("Tsonic.Runtime.ArrayHelpers.Slice(__arr0, 1)");
|
|
122
|
+
});
|
|
123
|
+
it("should handle holes in array patterns", () => {
|
|
124
|
+
const module = createModule([
|
|
125
|
+
{
|
|
126
|
+
kind: "variableDeclaration",
|
|
127
|
+
declarationKind: "const",
|
|
128
|
+
declarations: [
|
|
129
|
+
{
|
|
130
|
+
kind: "variableDeclarator",
|
|
131
|
+
name: {
|
|
132
|
+
kind: "arrayPattern",
|
|
133
|
+
elements: [
|
|
134
|
+
{
|
|
135
|
+
pattern: { kind: "identifierPattern", name: "a" },
|
|
136
|
+
isRest: false,
|
|
137
|
+
},
|
|
138
|
+
undefined, // hole
|
|
139
|
+
{
|
|
140
|
+
pattern: { kind: "identifierPattern", name: "c" },
|
|
141
|
+
isRest: false,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
type: arrayType(numberType),
|
|
146
|
+
initializer: { kind: "identifier", name: "arr" },
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
isExported: false,
|
|
150
|
+
},
|
|
151
|
+
]);
|
|
152
|
+
const result = emitModule(module);
|
|
153
|
+
// Should skip index 1 (the hole) - with types
|
|
154
|
+
expect(result).to.include("double a = __arr0[0];");
|
|
155
|
+
expect(result).to.include("double c = __arr0[2];");
|
|
156
|
+
// Should NOT have index 1
|
|
157
|
+
expect(result).to.not.include("__arr0[1]");
|
|
158
|
+
});
|
|
159
|
+
it("should handle nested array patterns", () => {
|
|
160
|
+
const module = createModule([
|
|
161
|
+
{
|
|
162
|
+
kind: "variableDeclaration",
|
|
163
|
+
declarationKind: "const",
|
|
164
|
+
declarations: [
|
|
165
|
+
{
|
|
166
|
+
kind: "variableDeclarator",
|
|
167
|
+
name: {
|
|
168
|
+
kind: "arrayPattern",
|
|
169
|
+
elements: [
|
|
170
|
+
{
|
|
171
|
+
pattern: {
|
|
172
|
+
kind: "arrayPattern",
|
|
173
|
+
elements: [
|
|
174
|
+
{
|
|
175
|
+
pattern: { kind: "identifierPattern", name: "a" },
|
|
176
|
+
isRest: false,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
pattern: { kind: "identifierPattern", name: "b" },
|
|
180
|
+
isRest: false,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
isRest: false,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
pattern: { kind: "identifierPattern", name: "c" },
|
|
188
|
+
isRest: false,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
type: arrayType(arrayType(numberType)),
|
|
193
|
+
initializer: { kind: "identifier", name: "nested" },
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
isExported: false,
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
const result = emitModule(module);
|
|
200
|
+
// Should create nested temp variables
|
|
201
|
+
expect(result).to.include("var __arr0 = nested;");
|
|
202
|
+
expect(result).to.include("var __arr1 = __arr0[0];");
|
|
203
|
+
// Inner elements get element type (double)
|
|
204
|
+
expect(result).to.include("double a = __arr1[0];");
|
|
205
|
+
expect(result).to.include("double b = __arr1[1];");
|
|
206
|
+
// Outer second element gets array element type (double[])
|
|
207
|
+
expect(result).to.include("double[] c = __arr0[1];");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe("Variable Declaration - Object Patterns", () => {
|
|
211
|
+
// Helper to create a reference type with structural members (required by emitter)
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
213
|
+
const refTypeWithMembers = (name, members) => ({
|
|
214
|
+
kind: "referenceType",
|
|
215
|
+
name,
|
|
216
|
+
resolvedClrType: name, // Required for emitter to resolve the type
|
|
217
|
+
structuralMembers: members,
|
|
218
|
+
});
|
|
219
|
+
it("should lower simple object destructuring", () => {
|
|
220
|
+
const personMembers = [
|
|
221
|
+
{
|
|
222
|
+
kind: "propertySignature",
|
|
223
|
+
name: "name",
|
|
224
|
+
type: stringType,
|
|
225
|
+
isOptional: false,
|
|
226
|
+
isReadonly: false,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
kind: "propertySignature",
|
|
230
|
+
name: "age",
|
|
231
|
+
type: numberType,
|
|
232
|
+
isOptional: false,
|
|
233
|
+
isReadonly: false,
|
|
234
|
+
},
|
|
235
|
+
];
|
|
236
|
+
const personType = refTypeWithMembers("Person", personMembers);
|
|
237
|
+
const module = createModule([
|
|
238
|
+
{
|
|
239
|
+
kind: "variableDeclaration",
|
|
240
|
+
declarationKind: "const",
|
|
241
|
+
declarations: [
|
|
242
|
+
{
|
|
243
|
+
kind: "variableDeclarator",
|
|
244
|
+
name: {
|
|
245
|
+
kind: "objectPattern",
|
|
246
|
+
properties: [
|
|
247
|
+
{
|
|
248
|
+
kind: "property",
|
|
249
|
+
key: "name",
|
|
250
|
+
value: { kind: "identifierPattern", name: "name" },
|
|
251
|
+
shorthand: true,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
kind: "property",
|
|
255
|
+
key: "age",
|
|
256
|
+
value: { kind: "identifierPattern", name: "age" },
|
|
257
|
+
shorthand: true,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
type: personType,
|
|
262
|
+
initializer: { kind: "identifier", name: "person" },
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
isExported: false,
|
|
266
|
+
},
|
|
267
|
+
]);
|
|
268
|
+
const result = emitModule(module);
|
|
269
|
+
// Should create temp variable
|
|
270
|
+
expect(result).to.include("var __obj0 = person;");
|
|
271
|
+
// Should emit property access with types from structuralMembers
|
|
272
|
+
expect(result).to.include("string name = __obj0.name;");
|
|
273
|
+
expect(result).to.include("double age = __obj0.age;");
|
|
274
|
+
});
|
|
275
|
+
it("should handle object property renaming", () => {
|
|
276
|
+
const personMembers = [
|
|
277
|
+
{
|
|
278
|
+
kind: "propertySignature",
|
|
279
|
+
name: "firstName",
|
|
280
|
+
type: stringType,
|
|
281
|
+
isOptional: false,
|
|
282
|
+
isReadonly: false,
|
|
283
|
+
},
|
|
284
|
+
];
|
|
285
|
+
const personType = refTypeWithMembers("Person", personMembers);
|
|
286
|
+
const module = createModule([
|
|
287
|
+
{
|
|
288
|
+
kind: "variableDeclaration",
|
|
289
|
+
declarationKind: "const",
|
|
290
|
+
declarations: [
|
|
291
|
+
{
|
|
292
|
+
kind: "variableDeclarator",
|
|
293
|
+
name: {
|
|
294
|
+
kind: "objectPattern",
|
|
295
|
+
properties: [
|
|
296
|
+
{
|
|
297
|
+
kind: "property",
|
|
298
|
+
key: "firstName",
|
|
299
|
+
value: { kind: "identifierPattern", name: "name" },
|
|
300
|
+
shorthand: false,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
type: personType,
|
|
305
|
+
initializer: { kind: "identifier", name: "person" },
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
isExported: false,
|
|
309
|
+
},
|
|
310
|
+
]);
|
|
311
|
+
const result = emitModule(module);
|
|
312
|
+
// Should use original key but target name, with type
|
|
313
|
+
expect(result).to.include("string name = __obj0.firstName;");
|
|
314
|
+
});
|
|
315
|
+
it("should handle nested object patterns", () => {
|
|
316
|
+
const innerMembers = [
|
|
317
|
+
{
|
|
318
|
+
kind: "propertySignature",
|
|
319
|
+
name: "inner",
|
|
320
|
+
type: stringType,
|
|
321
|
+
isOptional: false,
|
|
322
|
+
isReadonly: false,
|
|
323
|
+
},
|
|
324
|
+
];
|
|
325
|
+
const innerType = refTypeWithMembers("Inner", innerMembers);
|
|
326
|
+
const outerMembers = [
|
|
327
|
+
{
|
|
328
|
+
kind: "propertySignature",
|
|
329
|
+
name: "outer",
|
|
330
|
+
type: innerType,
|
|
331
|
+
isOptional: false,
|
|
332
|
+
isReadonly: false,
|
|
333
|
+
},
|
|
334
|
+
];
|
|
335
|
+
const outerType = refTypeWithMembers("Outer", outerMembers);
|
|
336
|
+
const module = createModule([
|
|
337
|
+
{
|
|
338
|
+
kind: "variableDeclaration",
|
|
339
|
+
declarationKind: "const",
|
|
340
|
+
declarations: [
|
|
341
|
+
{
|
|
342
|
+
kind: "variableDeclarator",
|
|
343
|
+
name: {
|
|
344
|
+
kind: "objectPattern",
|
|
345
|
+
properties: [
|
|
346
|
+
{
|
|
347
|
+
kind: "property",
|
|
348
|
+
key: "outer",
|
|
349
|
+
value: {
|
|
350
|
+
kind: "objectPattern",
|
|
351
|
+
properties: [
|
|
352
|
+
{
|
|
353
|
+
kind: "property",
|
|
354
|
+
key: "inner",
|
|
355
|
+
value: { kind: "identifierPattern", name: "value" },
|
|
356
|
+
shorthand: false,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
shorthand: false,
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
},
|
|
364
|
+
type: outerType,
|
|
365
|
+
initializer: { kind: "identifier", name: "obj" },
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
isExported: false,
|
|
369
|
+
},
|
|
370
|
+
]);
|
|
371
|
+
const result = emitModule(module);
|
|
372
|
+
// Should create nested temp variables
|
|
373
|
+
expect(result).to.include("var __obj0 = obj;");
|
|
374
|
+
expect(result).to.include("var __obj1 = __obj0.outer;");
|
|
375
|
+
expect(result).to.include("string value = __obj1.inner;");
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
describe("For-of Loop Destructuring", () => {
|
|
379
|
+
it("should lower array destructuring in for-of loop", () => {
|
|
380
|
+
const module = createModule([
|
|
381
|
+
{
|
|
382
|
+
kind: "forOfStatement",
|
|
383
|
+
variable: {
|
|
384
|
+
kind: "arrayPattern",
|
|
385
|
+
elements: [
|
|
386
|
+
{
|
|
387
|
+
pattern: { kind: "identifierPattern", name: "key" },
|
|
388
|
+
isRest: false,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
pattern: { kind: "identifierPattern", name: "value" },
|
|
392
|
+
isRest: false,
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
expression: {
|
|
397
|
+
kind: "identifier",
|
|
398
|
+
name: "entries",
|
|
399
|
+
inferredType: arrayType(arrayType(stringType)),
|
|
400
|
+
},
|
|
401
|
+
body: {
|
|
402
|
+
kind: "blockStatement",
|
|
403
|
+
statements: [
|
|
404
|
+
{
|
|
405
|
+
kind: "expressionStatement",
|
|
406
|
+
expression: {
|
|
407
|
+
kind: "call",
|
|
408
|
+
callee: {
|
|
409
|
+
kind: "memberAccess",
|
|
410
|
+
object: { kind: "identifier", name: "console" },
|
|
411
|
+
property: "log",
|
|
412
|
+
isComputed: false,
|
|
413
|
+
isOptional: false,
|
|
414
|
+
},
|
|
415
|
+
arguments: [
|
|
416
|
+
{ kind: "identifier", name: "key" },
|
|
417
|
+
{ kind: "identifier", name: "value" },
|
|
418
|
+
],
|
|
419
|
+
isOptional: false,
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
},
|
|
424
|
+
isAwait: false,
|
|
425
|
+
},
|
|
426
|
+
]);
|
|
427
|
+
const result = emitModule(module);
|
|
428
|
+
// Should use temp variable in foreach
|
|
429
|
+
expect(result).to.include("foreach (var __item in entries)");
|
|
430
|
+
// Should destructure inside the loop (with types from element type)
|
|
431
|
+
expect(result).to.include("var __arr0 = __item;");
|
|
432
|
+
expect(result).to.include("string key = __arr0[0];");
|
|
433
|
+
expect(result).to.include("string value = __arr0[1];");
|
|
434
|
+
});
|
|
435
|
+
it("should use simple variable for identifier pattern", () => {
|
|
436
|
+
const module = createModule([
|
|
437
|
+
{
|
|
438
|
+
kind: "forOfStatement",
|
|
439
|
+
variable: { kind: "identifierPattern", name: "item" },
|
|
440
|
+
expression: { kind: "identifier", name: "items" },
|
|
441
|
+
body: {
|
|
442
|
+
kind: "blockStatement",
|
|
443
|
+
statements: [],
|
|
444
|
+
},
|
|
445
|
+
isAwait: false,
|
|
446
|
+
},
|
|
447
|
+
]);
|
|
448
|
+
const result = emitModule(module);
|
|
449
|
+
// Should use simple foreach without lowering
|
|
450
|
+
expect(result).to.include("foreach (var item in items)");
|
|
451
|
+
// Should NOT create temp variable
|
|
452
|
+
expect(result).to.not.include("__item");
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
describe("Parameter Destructuring", () => {
|
|
456
|
+
it("should lower array destructuring in function parameters", () => {
|
|
457
|
+
const module = {
|
|
458
|
+
kind: "module",
|
|
459
|
+
filePath: "/src/test.ts",
|
|
460
|
+
namespace: "TestApp",
|
|
461
|
+
className: "test",
|
|
462
|
+
isStaticContainer: true,
|
|
463
|
+
imports: [],
|
|
464
|
+
body: [
|
|
465
|
+
{
|
|
466
|
+
kind: "functionDeclaration",
|
|
467
|
+
name: "swap",
|
|
468
|
+
parameters: [
|
|
469
|
+
{
|
|
470
|
+
kind: "parameter",
|
|
471
|
+
pattern: {
|
|
472
|
+
kind: "arrayPattern",
|
|
473
|
+
elements: [
|
|
474
|
+
{
|
|
475
|
+
pattern: { kind: "identifierPattern", name: "a" },
|
|
476
|
+
isRest: false,
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
pattern: { kind: "identifierPattern", name: "b" },
|
|
480
|
+
isRest: false,
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
},
|
|
484
|
+
type: arrayType(numberType),
|
|
485
|
+
isOptional: false,
|
|
486
|
+
isRest: false,
|
|
487
|
+
passing: "value",
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
returnType: arrayType(numberType),
|
|
491
|
+
body: {
|
|
492
|
+
kind: "blockStatement",
|
|
493
|
+
statements: [
|
|
494
|
+
{
|
|
495
|
+
kind: "returnStatement",
|
|
496
|
+
expression: {
|
|
497
|
+
kind: "array",
|
|
498
|
+
elements: [
|
|
499
|
+
{ kind: "identifier", name: "b" },
|
|
500
|
+
{ kind: "identifier", name: "a" },
|
|
501
|
+
],
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
},
|
|
506
|
+
isExported: true,
|
|
507
|
+
isAsync: false,
|
|
508
|
+
isGenerator: false,
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
exports: [],
|
|
512
|
+
};
|
|
513
|
+
const result = emitModule(module);
|
|
514
|
+
// Should use synthetic parameter name
|
|
515
|
+
expect(result).to.include("__param0");
|
|
516
|
+
// Should destructure at start of body (with element type)
|
|
517
|
+
expect(result).to.include("double a = __arr0[0];");
|
|
518
|
+
expect(result).to.include("double b = __arr0[1];");
|
|
519
|
+
});
|
|
520
|
+
it("should handle object destructuring in function parameters", () => {
|
|
521
|
+
// Use referenceType with structuralMembers (not raw objectType)
|
|
522
|
+
const personType = {
|
|
523
|
+
kind: "referenceType",
|
|
524
|
+
name: "Person",
|
|
525
|
+
resolvedClrType: "Person",
|
|
526
|
+
structuralMembers: [
|
|
527
|
+
{
|
|
528
|
+
kind: "propertySignature",
|
|
529
|
+
name: "name",
|
|
530
|
+
type: stringType,
|
|
531
|
+
isOptional: false,
|
|
532
|
+
isReadonly: false,
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
kind: "propertySignature",
|
|
536
|
+
name: "age",
|
|
537
|
+
type: numberType,
|
|
538
|
+
isOptional: false,
|
|
539
|
+
isReadonly: false,
|
|
540
|
+
},
|
|
541
|
+
],
|
|
542
|
+
};
|
|
543
|
+
const module = {
|
|
544
|
+
kind: "module",
|
|
545
|
+
filePath: "/src/test.ts",
|
|
546
|
+
namespace: "TestApp",
|
|
547
|
+
className: "test",
|
|
548
|
+
isStaticContainer: true,
|
|
549
|
+
imports: [],
|
|
550
|
+
body: [
|
|
551
|
+
{
|
|
552
|
+
kind: "functionDeclaration",
|
|
553
|
+
name: "greet",
|
|
554
|
+
parameters: [
|
|
555
|
+
{
|
|
556
|
+
kind: "parameter",
|
|
557
|
+
pattern: {
|
|
558
|
+
kind: "objectPattern",
|
|
559
|
+
properties: [
|
|
560
|
+
{
|
|
561
|
+
kind: "property",
|
|
562
|
+
key: "name",
|
|
563
|
+
value: { kind: "identifierPattern", name: "name" },
|
|
564
|
+
shorthand: true,
|
|
565
|
+
},
|
|
566
|
+
],
|
|
567
|
+
},
|
|
568
|
+
type: personType,
|
|
569
|
+
isOptional: false,
|
|
570
|
+
isRest: false,
|
|
571
|
+
passing: "value",
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
returnType: stringType,
|
|
575
|
+
body: {
|
|
576
|
+
kind: "blockStatement",
|
|
577
|
+
statements: [
|
|
578
|
+
{
|
|
579
|
+
kind: "returnStatement",
|
|
580
|
+
expression: { kind: "identifier", name: "name" },
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
},
|
|
584
|
+
isExported: true,
|
|
585
|
+
isAsync: false,
|
|
586
|
+
isGenerator: false,
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
exports: [],
|
|
590
|
+
};
|
|
591
|
+
const result = emitModule(module);
|
|
592
|
+
// Should use synthetic parameter name
|
|
593
|
+
expect(result).to.include("__param0");
|
|
594
|
+
// Should destructure with type
|
|
595
|
+
expect(result).to.include("string name = __obj0.name;");
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
describe("Assignment Destructuring", () => {
|
|
599
|
+
it("should lower array destructuring assignment", () => {
|
|
600
|
+
const module = createModule([
|
|
601
|
+
{
|
|
602
|
+
kind: "variableDeclaration",
|
|
603
|
+
declarationKind: "let",
|
|
604
|
+
declarations: [
|
|
605
|
+
{
|
|
606
|
+
kind: "variableDeclarator",
|
|
607
|
+
name: { kind: "identifierPattern", name: "a" },
|
|
608
|
+
type: numberType,
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
kind: "variableDeclarator",
|
|
612
|
+
name: { kind: "identifierPattern", name: "b" },
|
|
613
|
+
type: numberType,
|
|
614
|
+
},
|
|
615
|
+
],
|
|
616
|
+
isExported: false,
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
kind: "expressionStatement",
|
|
620
|
+
expression: {
|
|
621
|
+
kind: "assignment",
|
|
622
|
+
operator: "=",
|
|
623
|
+
left: {
|
|
624
|
+
kind: "arrayPattern",
|
|
625
|
+
elements: [
|
|
626
|
+
{
|
|
627
|
+
pattern: { kind: "identifierPattern", name: "a" },
|
|
628
|
+
isRest: false,
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
pattern: { kind: "identifierPattern", name: "b" },
|
|
632
|
+
isRest: false,
|
|
633
|
+
},
|
|
634
|
+
],
|
|
635
|
+
},
|
|
636
|
+
right: {
|
|
637
|
+
kind: "identifier",
|
|
638
|
+
name: "arr",
|
|
639
|
+
inferredType: arrayType(numberType),
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
]);
|
|
644
|
+
const result = emitModule(module);
|
|
645
|
+
// Should emit sequence expression for assignment
|
|
646
|
+
expect(result).to.include("__t0 = arr");
|
|
647
|
+
expect(result).to.include("a = __t0[0]");
|
|
648
|
+
expect(result).to.include("b = __t0[1]");
|
|
649
|
+
});
|
|
650
|
+
it("should handle identifier pattern assignment (simple case)", () => {
|
|
651
|
+
const module = createModule([
|
|
652
|
+
{
|
|
653
|
+
kind: "variableDeclaration",
|
|
654
|
+
declarationKind: "let",
|
|
655
|
+
declarations: [
|
|
656
|
+
{
|
|
657
|
+
kind: "variableDeclarator",
|
|
658
|
+
name: { kind: "identifierPattern", name: "x" },
|
|
659
|
+
type: numberType,
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
isExported: false,
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
kind: "expressionStatement",
|
|
666
|
+
expression: {
|
|
667
|
+
kind: "assignment",
|
|
668
|
+
operator: "=",
|
|
669
|
+
left: { kind: "identifierPattern", name: "x" },
|
|
670
|
+
right: { kind: "literal", value: 42 },
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
]);
|
|
674
|
+
const result = emitModule(module);
|
|
675
|
+
// Should emit simple assignment
|
|
676
|
+
expect(result).to.include("x = 42");
|
|
677
|
+
// Should NOT create temp variable for simple case
|
|
678
|
+
expect(result).to.not.include("__t");
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
describe("Mixed and Complex Patterns", () => {
|
|
682
|
+
it("should handle mixed array and object destructuring", () => {
|
|
683
|
+
// Element type is referenceType with structuralMembers
|
|
684
|
+
const itemType = {
|
|
685
|
+
kind: "referenceType",
|
|
686
|
+
name: "Item",
|
|
687
|
+
resolvedClrType: "Item",
|
|
688
|
+
structuralMembers: [
|
|
689
|
+
{
|
|
690
|
+
kind: "propertySignature",
|
|
691
|
+
name: "id",
|
|
692
|
+
type: numberType,
|
|
693
|
+
isOptional: false,
|
|
694
|
+
isReadonly: false,
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
};
|
|
698
|
+
const mixedType = {
|
|
699
|
+
kind: "arrayType",
|
|
700
|
+
elementType: itemType,
|
|
701
|
+
};
|
|
702
|
+
const module = createModule([
|
|
703
|
+
{
|
|
704
|
+
kind: "variableDeclaration",
|
|
705
|
+
declarationKind: "const",
|
|
706
|
+
declarations: [
|
|
707
|
+
{
|
|
708
|
+
kind: "variableDeclarator",
|
|
709
|
+
name: {
|
|
710
|
+
kind: "arrayPattern",
|
|
711
|
+
elements: [
|
|
712
|
+
{
|
|
713
|
+
pattern: {
|
|
714
|
+
kind: "objectPattern",
|
|
715
|
+
properties: [
|
|
716
|
+
{
|
|
717
|
+
kind: "property",
|
|
718
|
+
key: "id",
|
|
719
|
+
value: { kind: "identifierPattern", name: "firstId" },
|
|
720
|
+
shorthand: false,
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
},
|
|
724
|
+
isRest: false,
|
|
725
|
+
},
|
|
726
|
+
],
|
|
727
|
+
},
|
|
728
|
+
type: mixedType,
|
|
729
|
+
initializer: { kind: "identifier", name: "items" },
|
|
730
|
+
},
|
|
731
|
+
],
|
|
732
|
+
isExported: false,
|
|
733
|
+
},
|
|
734
|
+
]);
|
|
735
|
+
const result = emitModule(module);
|
|
736
|
+
// Should handle array then object destructuring
|
|
737
|
+
// Note: temp counter is shared, so arr gets 0, obj gets 1
|
|
738
|
+
expect(result).to.include("var __arr0 = items;");
|
|
739
|
+
expect(result).to.include("var __obj1 = __arr0[0];");
|
|
740
|
+
expect(result).to.include("double firstId = __obj1.id;");
|
|
741
|
+
});
|
|
742
|
+
it("should handle deeply nested patterns", () => {
|
|
743
|
+
const deepType = {
|
|
744
|
+
kind: "arrayType",
|
|
745
|
+
elementType: {
|
|
746
|
+
kind: "arrayType",
|
|
747
|
+
elementType: {
|
|
748
|
+
kind: "arrayType",
|
|
749
|
+
elementType: numberType,
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
};
|
|
753
|
+
const module = createModule([
|
|
754
|
+
{
|
|
755
|
+
kind: "variableDeclaration",
|
|
756
|
+
declarationKind: "const",
|
|
757
|
+
declarations: [
|
|
758
|
+
{
|
|
759
|
+
kind: "variableDeclarator",
|
|
760
|
+
name: {
|
|
761
|
+
kind: "arrayPattern",
|
|
762
|
+
elements: [
|
|
763
|
+
{
|
|
764
|
+
pattern: {
|
|
765
|
+
kind: "arrayPattern",
|
|
766
|
+
elements: [
|
|
767
|
+
{
|
|
768
|
+
pattern: {
|
|
769
|
+
kind: "arrayPattern",
|
|
770
|
+
elements: [
|
|
771
|
+
{
|
|
772
|
+
pattern: {
|
|
773
|
+
kind: "identifierPattern",
|
|
774
|
+
name: "deepValue",
|
|
775
|
+
},
|
|
776
|
+
isRest: false,
|
|
777
|
+
},
|
|
778
|
+
],
|
|
779
|
+
},
|
|
780
|
+
isRest: false,
|
|
781
|
+
},
|
|
782
|
+
],
|
|
783
|
+
},
|
|
784
|
+
isRest: false,
|
|
785
|
+
},
|
|
786
|
+
],
|
|
787
|
+
},
|
|
788
|
+
type: deepType,
|
|
789
|
+
initializer: { kind: "identifier", name: "deep" },
|
|
790
|
+
},
|
|
791
|
+
],
|
|
792
|
+
isExported: false,
|
|
793
|
+
},
|
|
794
|
+
]);
|
|
795
|
+
const result = emitModule(module);
|
|
796
|
+
// Should create multiple nesting levels
|
|
797
|
+
expect(result).to.include("var __arr0 = deep;");
|
|
798
|
+
expect(result).to.include("var __arr1 = __arr0[0];");
|
|
799
|
+
expect(result).to.include("var __arr2 = __arr1[0];");
|
|
800
|
+
// Deepest level gets the element type
|
|
801
|
+
expect(result).to.include("double deepValue = __arr2[0];");
|
|
802
|
+
});
|
|
803
|
+
it("should handle array inside object pattern", () => {
|
|
804
|
+
// Object with array property
|
|
805
|
+
const containerType = {
|
|
806
|
+
kind: "referenceType",
|
|
807
|
+
name: "Container",
|
|
808
|
+
resolvedClrType: "Container",
|
|
809
|
+
structuralMembers: [
|
|
810
|
+
{
|
|
811
|
+
kind: "propertySignature",
|
|
812
|
+
name: "items",
|
|
813
|
+
type: arrayType(numberType),
|
|
814
|
+
isOptional: false,
|
|
815
|
+
isReadonly: false,
|
|
816
|
+
},
|
|
817
|
+
],
|
|
818
|
+
};
|
|
819
|
+
const module = createModule([
|
|
820
|
+
{
|
|
821
|
+
kind: "variableDeclaration",
|
|
822
|
+
declarationKind: "const",
|
|
823
|
+
declarations: [
|
|
824
|
+
{
|
|
825
|
+
kind: "variableDeclarator",
|
|
826
|
+
name: {
|
|
827
|
+
kind: "objectPattern",
|
|
828
|
+
properties: [
|
|
829
|
+
{
|
|
830
|
+
kind: "property",
|
|
831
|
+
key: "items",
|
|
832
|
+
value: {
|
|
833
|
+
kind: "arrayPattern",
|
|
834
|
+
elements: [
|
|
835
|
+
{
|
|
836
|
+
pattern: { kind: "identifierPattern", name: "first" },
|
|
837
|
+
isRest: false,
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
pattern: {
|
|
841
|
+
kind: "identifierPattern",
|
|
842
|
+
name: "second",
|
|
843
|
+
},
|
|
844
|
+
isRest: false,
|
|
845
|
+
},
|
|
846
|
+
],
|
|
847
|
+
},
|
|
848
|
+
shorthand: false,
|
|
849
|
+
},
|
|
850
|
+
],
|
|
851
|
+
},
|
|
852
|
+
type: containerType,
|
|
853
|
+
initializer: { kind: "identifier", name: "container" },
|
|
854
|
+
},
|
|
855
|
+
],
|
|
856
|
+
isExported: false,
|
|
857
|
+
},
|
|
858
|
+
]);
|
|
859
|
+
const result = emitModule(module);
|
|
860
|
+
// Should create object temp, then array temp for property
|
|
861
|
+
// Note: temp counter is shared, so obj gets 0, arr gets 1
|
|
862
|
+
expect(result).to.include("var __obj0 = container;");
|
|
863
|
+
expect(result).to.include("var __arr1 = __obj0.items;");
|
|
864
|
+
expect(result).to.include("double first = __arr1[0];");
|
|
865
|
+
expect(result).to.include("double second = __arr1[1];");
|
|
866
|
+
});
|
|
867
|
+
it("should handle deep mixed nesting (obj -> arr -> obj)", () => {
|
|
868
|
+
const innerObjType = {
|
|
869
|
+
kind: "referenceType",
|
|
870
|
+
name: "Inner",
|
|
871
|
+
resolvedClrType: "Inner",
|
|
872
|
+
structuralMembers: [
|
|
873
|
+
{
|
|
874
|
+
kind: "propertySignature",
|
|
875
|
+
name: "value",
|
|
876
|
+
type: stringType,
|
|
877
|
+
isOptional: false,
|
|
878
|
+
isReadonly: false,
|
|
879
|
+
},
|
|
880
|
+
],
|
|
881
|
+
};
|
|
882
|
+
const outerObjType = {
|
|
883
|
+
kind: "referenceType",
|
|
884
|
+
name: "Outer",
|
|
885
|
+
resolvedClrType: "Outer",
|
|
886
|
+
structuralMembers: [
|
|
887
|
+
{
|
|
888
|
+
kind: "propertySignature",
|
|
889
|
+
name: "items",
|
|
890
|
+
type: arrayType(innerObjType),
|
|
891
|
+
isOptional: false,
|
|
892
|
+
isReadonly: false,
|
|
893
|
+
},
|
|
894
|
+
],
|
|
895
|
+
};
|
|
896
|
+
const module = createModule([
|
|
897
|
+
{
|
|
898
|
+
kind: "variableDeclaration",
|
|
899
|
+
declarationKind: "const",
|
|
900
|
+
declarations: [
|
|
901
|
+
{
|
|
902
|
+
kind: "variableDeclarator",
|
|
903
|
+
name: {
|
|
904
|
+
kind: "objectPattern",
|
|
905
|
+
properties: [
|
|
906
|
+
{
|
|
907
|
+
kind: "property",
|
|
908
|
+
key: "items",
|
|
909
|
+
value: {
|
|
910
|
+
kind: "arrayPattern",
|
|
911
|
+
elements: [
|
|
912
|
+
{
|
|
913
|
+
pattern: {
|
|
914
|
+
kind: "objectPattern",
|
|
915
|
+
properties: [
|
|
916
|
+
{
|
|
917
|
+
kind: "property",
|
|
918
|
+
key: "value",
|
|
919
|
+
value: {
|
|
920
|
+
kind: "identifierPattern",
|
|
921
|
+
name: "firstValue",
|
|
922
|
+
},
|
|
923
|
+
shorthand: false,
|
|
924
|
+
},
|
|
925
|
+
],
|
|
926
|
+
},
|
|
927
|
+
isRest: false,
|
|
928
|
+
},
|
|
929
|
+
],
|
|
930
|
+
},
|
|
931
|
+
shorthand: false,
|
|
932
|
+
},
|
|
933
|
+
],
|
|
934
|
+
},
|
|
935
|
+
type: outerObjType,
|
|
936
|
+
initializer: { kind: "identifier", name: "data" },
|
|
937
|
+
},
|
|
938
|
+
],
|
|
939
|
+
isExported: false,
|
|
940
|
+
},
|
|
941
|
+
]);
|
|
942
|
+
const result = emitModule(module);
|
|
943
|
+
// obj -> arr -> obj nesting
|
|
944
|
+
// Note: temp counter is shared, so obj0, arr1, obj2
|
|
945
|
+
expect(result).to.include("var __obj0 = data;");
|
|
946
|
+
expect(result).to.include("var __arr1 = __obj0.items;");
|
|
947
|
+
expect(result).to.include("var __obj2 = __arr1[0];");
|
|
948
|
+
expect(result).to.include("string firstValue = __obj2.value;");
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
describe("Edge Cases", () => {
|
|
952
|
+
it("should handle empty array pattern", () => {
|
|
953
|
+
const module = createModule([
|
|
954
|
+
{
|
|
955
|
+
kind: "variableDeclaration",
|
|
956
|
+
declarationKind: "const",
|
|
957
|
+
declarations: [
|
|
958
|
+
{
|
|
959
|
+
kind: "variableDeclarator",
|
|
960
|
+
name: {
|
|
961
|
+
kind: "arrayPattern",
|
|
962
|
+
elements: [],
|
|
963
|
+
},
|
|
964
|
+
type: arrayType(numberType),
|
|
965
|
+
initializer: { kind: "identifier", name: "arr" },
|
|
966
|
+
},
|
|
967
|
+
],
|
|
968
|
+
isExported: false,
|
|
969
|
+
},
|
|
970
|
+
]);
|
|
971
|
+
const result = emitModule(module);
|
|
972
|
+
// Should still create temp variable (for side effect evaluation)
|
|
973
|
+
expect(result).to.include("var __arr0 = arr;");
|
|
974
|
+
// But no actual destructuring
|
|
975
|
+
expect(result).to.not.include("__arr0[");
|
|
976
|
+
});
|
|
977
|
+
it("should handle empty object pattern", () => {
|
|
978
|
+
// Use referenceType (not raw objectType)
|
|
979
|
+
const emptyType = {
|
|
980
|
+
kind: "referenceType",
|
|
981
|
+
name: "Empty",
|
|
982
|
+
resolvedClrType: "Empty",
|
|
983
|
+
structuralMembers: [],
|
|
984
|
+
};
|
|
985
|
+
const module = createModule([
|
|
986
|
+
{
|
|
987
|
+
kind: "variableDeclaration",
|
|
988
|
+
declarationKind: "const",
|
|
989
|
+
declarations: [
|
|
990
|
+
{
|
|
991
|
+
kind: "variableDeclarator",
|
|
992
|
+
name: {
|
|
993
|
+
kind: "objectPattern",
|
|
994
|
+
properties: [],
|
|
995
|
+
},
|
|
996
|
+
type: emptyType,
|
|
997
|
+
initializer: { kind: "identifier", name: "obj" },
|
|
998
|
+
},
|
|
999
|
+
],
|
|
1000
|
+
isExported: false,
|
|
1001
|
+
},
|
|
1002
|
+
]);
|
|
1003
|
+
const result = emitModule(module);
|
|
1004
|
+
// Should create temp variable
|
|
1005
|
+
expect(result).to.include("var __obj0 = obj;");
|
|
1006
|
+
});
|
|
1007
|
+
it("should escape C# keywords in destructured names", () => {
|
|
1008
|
+
const module = createModule([
|
|
1009
|
+
{
|
|
1010
|
+
kind: "variableDeclaration",
|
|
1011
|
+
declarationKind: "const",
|
|
1012
|
+
declarations: [
|
|
1013
|
+
{
|
|
1014
|
+
kind: "variableDeclarator",
|
|
1015
|
+
name: {
|
|
1016
|
+
kind: "arrayPattern",
|
|
1017
|
+
elements: [
|
|
1018
|
+
{
|
|
1019
|
+
pattern: { kind: "identifierPattern", name: "class" },
|
|
1020
|
+
isRest: false,
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
pattern: { kind: "identifierPattern", name: "namespace" },
|
|
1024
|
+
isRest: false,
|
|
1025
|
+
},
|
|
1026
|
+
],
|
|
1027
|
+
},
|
|
1028
|
+
type: arrayType(stringType),
|
|
1029
|
+
initializer: { kind: "identifier", name: "keywords" },
|
|
1030
|
+
},
|
|
1031
|
+
],
|
|
1032
|
+
isExported: false,
|
|
1033
|
+
},
|
|
1034
|
+
]);
|
|
1035
|
+
const result = emitModule(module);
|
|
1036
|
+
// Should escape C# keywords with @ prefix (with types)
|
|
1037
|
+
expect(result).to.include("string @class = __arr0[0];");
|
|
1038
|
+
expect(result).to.include("string @namespace = __arr0[1];");
|
|
1039
|
+
});
|
|
1040
|
+
it("should handle rest at different positions (rest must be last)", () => {
|
|
1041
|
+
// Rest in middle - should stop processing after rest
|
|
1042
|
+
const module = createModule([
|
|
1043
|
+
{
|
|
1044
|
+
kind: "variableDeclaration",
|
|
1045
|
+
declarationKind: "const",
|
|
1046
|
+
declarations: [
|
|
1047
|
+
{
|
|
1048
|
+
kind: "variableDeclarator",
|
|
1049
|
+
name: {
|
|
1050
|
+
kind: "arrayPattern",
|
|
1051
|
+
elements: [
|
|
1052
|
+
{
|
|
1053
|
+
pattern: { kind: "identifierPattern", name: "first" },
|
|
1054
|
+
isRest: false,
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
pattern: { kind: "identifierPattern", name: "middle" },
|
|
1058
|
+
isRest: true,
|
|
1059
|
+
},
|
|
1060
|
+
// Note: TypeScript would reject this, but testing emitter behavior
|
|
1061
|
+
],
|
|
1062
|
+
},
|
|
1063
|
+
type: arrayType(stringType),
|
|
1064
|
+
initializer: { kind: "identifier", name: "items" },
|
|
1065
|
+
},
|
|
1066
|
+
],
|
|
1067
|
+
isExported: false,
|
|
1068
|
+
},
|
|
1069
|
+
]);
|
|
1070
|
+
const result = emitModule(module);
|
|
1071
|
+
// First element gets element type
|
|
1072
|
+
expect(result).to.include("string first = __arr0[0];");
|
|
1073
|
+
// Rest gets array type
|
|
1074
|
+
expect(result).to.include("string[] middle = Tsonic.Runtime.ArrayHelpers.Slice(__arr0, 1);");
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
1077
|
+
describe("Method Parameter Destructuring", () => {
|
|
1078
|
+
it("should lower destructuring in class method parameters", () => {
|
|
1079
|
+
const module = {
|
|
1080
|
+
kind: "module",
|
|
1081
|
+
filePath: "/src/test.ts",
|
|
1082
|
+
namespace: "TestApp",
|
|
1083
|
+
className: "Calculator",
|
|
1084
|
+
isStaticContainer: false,
|
|
1085
|
+
imports: [],
|
|
1086
|
+
body: [
|
|
1087
|
+
{
|
|
1088
|
+
kind: "classDeclaration",
|
|
1089
|
+
name: "Calculator",
|
|
1090
|
+
members: [
|
|
1091
|
+
{
|
|
1092
|
+
kind: "methodDeclaration",
|
|
1093
|
+
name: "add",
|
|
1094
|
+
parameters: [
|
|
1095
|
+
{
|
|
1096
|
+
kind: "parameter",
|
|
1097
|
+
pattern: {
|
|
1098
|
+
kind: "arrayPattern",
|
|
1099
|
+
elements: [
|
|
1100
|
+
{
|
|
1101
|
+
pattern: { kind: "identifierPattern", name: "x" },
|
|
1102
|
+
isRest: false,
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
pattern: { kind: "identifierPattern", name: "y" },
|
|
1106
|
+
isRest: false,
|
|
1107
|
+
},
|
|
1108
|
+
],
|
|
1109
|
+
},
|
|
1110
|
+
type: arrayType(numberType),
|
|
1111
|
+
isOptional: false,
|
|
1112
|
+
isRest: false,
|
|
1113
|
+
passing: "value",
|
|
1114
|
+
},
|
|
1115
|
+
],
|
|
1116
|
+
returnType: numberType,
|
|
1117
|
+
body: {
|
|
1118
|
+
kind: "blockStatement",
|
|
1119
|
+
statements: [
|
|
1120
|
+
{
|
|
1121
|
+
kind: "returnStatement",
|
|
1122
|
+
expression: {
|
|
1123
|
+
kind: "binary",
|
|
1124
|
+
operator: "+",
|
|
1125
|
+
left: { kind: "identifier", name: "x" },
|
|
1126
|
+
right: { kind: "identifier", name: "y" },
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1129
|
+
],
|
|
1130
|
+
},
|
|
1131
|
+
accessibility: "public",
|
|
1132
|
+
isStatic: false,
|
|
1133
|
+
isAsync: false,
|
|
1134
|
+
isGenerator: false,
|
|
1135
|
+
},
|
|
1136
|
+
],
|
|
1137
|
+
isExported: true,
|
|
1138
|
+
typeParameters: [],
|
|
1139
|
+
implements: [],
|
|
1140
|
+
isStruct: false,
|
|
1141
|
+
},
|
|
1142
|
+
],
|
|
1143
|
+
exports: [],
|
|
1144
|
+
};
|
|
1145
|
+
const result = emitModule(module);
|
|
1146
|
+
// Should use synthetic parameter name in method signature
|
|
1147
|
+
expect(result).to.include("__param0");
|
|
1148
|
+
// Should destructure at start of method body (with element type)
|
|
1149
|
+
expect(result).to.include("double x = __arr0[0];");
|
|
1150
|
+
expect(result).to.include("double y = __arr0[1];");
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
describe("Constructor Parameter Destructuring", () => {
|
|
1154
|
+
it("should lower destructuring in constructor parameters", () => {
|
|
1155
|
+
const module = {
|
|
1156
|
+
kind: "module",
|
|
1157
|
+
filePath: "/src/test.ts",
|
|
1158
|
+
namespace: "TestApp",
|
|
1159
|
+
className: "Point",
|
|
1160
|
+
isStaticContainer: false,
|
|
1161
|
+
imports: [],
|
|
1162
|
+
body: [
|
|
1163
|
+
{
|
|
1164
|
+
kind: "classDeclaration",
|
|
1165
|
+
name: "Point",
|
|
1166
|
+
members: [
|
|
1167
|
+
{
|
|
1168
|
+
kind: "propertyDeclaration",
|
|
1169
|
+
name: "x",
|
|
1170
|
+
type: numberType,
|
|
1171
|
+
accessibility: "public",
|
|
1172
|
+
isStatic: false,
|
|
1173
|
+
isReadonly: false,
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
kind: "propertyDeclaration",
|
|
1177
|
+
name: "y",
|
|
1178
|
+
type: numberType,
|
|
1179
|
+
accessibility: "public",
|
|
1180
|
+
isStatic: false,
|
|
1181
|
+
isReadonly: false,
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
kind: "constructorDeclaration",
|
|
1185
|
+
parameters: [
|
|
1186
|
+
{
|
|
1187
|
+
kind: "parameter",
|
|
1188
|
+
pattern: {
|
|
1189
|
+
kind: "arrayPattern",
|
|
1190
|
+
elements: [
|
|
1191
|
+
{
|
|
1192
|
+
pattern: { kind: "identifierPattern", name: "x" },
|
|
1193
|
+
isRest: false,
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
pattern: { kind: "identifierPattern", name: "y" },
|
|
1197
|
+
isRest: false,
|
|
1198
|
+
},
|
|
1199
|
+
],
|
|
1200
|
+
},
|
|
1201
|
+
type: arrayType(numberType),
|
|
1202
|
+
isOptional: false,
|
|
1203
|
+
isRest: false,
|
|
1204
|
+
passing: "value",
|
|
1205
|
+
},
|
|
1206
|
+
],
|
|
1207
|
+
body: {
|
|
1208
|
+
kind: "blockStatement",
|
|
1209
|
+
statements: [
|
|
1210
|
+
{
|
|
1211
|
+
kind: "expressionStatement",
|
|
1212
|
+
expression: {
|
|
1213
|
+
kind: "assignment",
|
|
1214
|
+
operator: "=",
|
|
1215
|
+
left: {
|
|
1216
|
+
kind: "memberAccess",
|
|
1217
|
+
object: { kind: "this" },
|
|
1218
|
+
property: "x",
|
|
1219
|
+
isComputed: false,
|
|
1220
|
+
isOptional: false,
|
|
1221
|
+
},
|
|
1222
|
+
right: { kind: "identifier", name: "x" },
|
|
1223
|
+
},
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
kind: "expressionStatement",
|
|
1227
|
+
expression: {
|
|
1228
|
+
kind: "assignment",
|
|
1229
|
+
operator: "=",
|
|
1230
|
+
left: {
|
|
1231
|
+
kind: "memberAccess",
|
|
1232
|
+
object: { kind: "this" },
|
|
1233
|
+
property: "y",
|
|
1234
|
+
isComputed: false,
|
|
1235
|
+
isOptional: false,
|
|
1236
|
+
},
|
|
1237
|
+
right: { kind: "identifier", name: "y" },
|
|
1238
|
+
},
|
|
1239
|
+
},
|
|
1240
|
+
],
|
|
1241
|
+
},
|
|
1242
|
+
accessibility: "public",
|
|
1243
|
+
},
|
|
1244
|
+
],
|
|
1245
|
+
isExported: true,
|
|
1246
|
+
typeParameters: [],
|
|
1247
|
+
implements: [],
|
|
1248
|
+
isStruct: false,
|
|
1249
|
+
},
|
|
1250
|
+
],
|
|
1251
|
+
exports: [],
|
|
1252
|
+
};
|
|
1253
|
+
const result = emitModule(module);
|
|
1254
|
+
// Should use synthetic parameter name in constructor
|
|
1255
|
+
expect(result).to.include("__param0");
|
|
1256
|
+
// Should destructure at start of constructor body (with element type)
|
|
1257
|
+
expect(result).to.include("double x = __arr0[0];");
|
|
1258
|
+
expect(result).to.include("double y = __arr0[1];");
|
|
1259
|
+
});
|
|
1260
|
+
});
|
|
1261
|
+
});
|
|
1262
|
+
//# sourceMappingURL=patterns.test.js.map
|