c-next 0.1.68 → 0.1.70
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/package.json +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +240 -204
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +693 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +86 -5
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/AssignmentTargetExtractor.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/ChildStatementCollector.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/StatementExpressionCollector.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/AssignmentTargetExtractor.test.ts +2 -2
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/ChildStatementCollector.test.ts +2 -2
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/StatementExpressionCollector.test.ts +2 -2
- package/src/transpiler/output/codegen/CodeGenerator.ts +160 -742
- package/src/transpiler/output/codegen/TypeRegistrationUtils.ts +4 -6
- package/src/transpiler/output/codegen/TypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/TypeValidator.ts +7 -7
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +29 -1
- package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +4 -6
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +4 -2
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +49 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +15 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +30 -17
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +25 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +19 -8
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +9 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +41 -26
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +29 -37
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +27 -19
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +10 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +51 -33
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +9 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +6 -1
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
- package/src/transpiler/state/CodeGenState.ts +157 -5
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +274 -6
- /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
- /package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/TransitiveModificationPropagator.test.ts +0 -0
|
@@ -25,8 +25,14 @@ import HandlerTestUtils from "./handlerTestUtils";
|
|
|
25
25
|
function createMockContext(
|
|
26
26
|
overrides: Partial<IAssignmentContext> = {},
|
|
27
27
|
): IAssignmentContext {
|
|
28
|
+
// Default resolved values based on first identifier
|
|
29
|
+
const identifiers = overrides.identifiers ?? ["flags", "Running"];
|
|
30
|
+
const resolvedTarget = overrides.resolvedTarget ?? identifiers[0];
|
|
31
|
+
const resolvedBaseIdentifier =
|
|
32
|
+
overrides.resolvedBaseIdentifier ?? identifiers[0];
|
|
33
|
+
|
|
28
34
|
return {
|
|
29
|
-
identifiers
|
|
35
|
+
identifiers,
|
|
30
36
|
subscripts: [],
|
|
31
37
|
isCompound: false,
|
|
32
38
|
cnextOp: "<-",
|
|
@@ -44,6 +50,8 @@ function createMockContext(
|
|
|
44
50
|
isSimpleIdentifier: false,
|
|
45
51
|
isSimpleThisAccess: false,
|
|
46
52
|
isSimpleGlobalAccess: false,
|
|
53
|
+
resolvedTarget,
|
|
54
|
+
resolvedBaseIdentifier,
|
|
47
55
|
...overrides,
|
|
48
56
|
} as IAssignmentContext;
|
|
49
57
|
}
|
|
@@ -81,9 +89,9 @@ describe("BitmapHandlers", () => {
|
|
|
81
89
|
)?.[1];
|
|
82
90
|
|
|
83
91
|
it("generates single-bit read-modify-write", () => {
|
|
84
|
-
|
|
92
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
85
93
|
["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
86
|
-
])
|
|
94
|
+
]);
|
|
87
95
|
HandlerTestUtils.setupMockSymbols({
|
|
88
96
|
bitmapFields: new Map([
|
|
89
97
|
["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
|
|
@@ -99,9 +107,9 @@ describe("BitmapHandlers", () => {
|
|
|
99
107
|
});
|
|
100
108
|
|
|
101
109
|
it("generates single-bit write with correct offset", () => {
|
|
102
|
-
|
|
110
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
103
111
|
["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
104
|
-
])
|
|
112
|
+
]);
|
|
105
113
|
HandlerTestUtils.setupMockSymbols({
|
|
106
114
|
bitmapFields: new Map([
|
|
107
115
|
["StatusFlags", new Map([["Active", { offset: 3, width: 1 }]])],
|
|
@@ -117,9 +125,9 @@ describe("BitmapHandlers", () => {
|
|
|
117
125
|
});
|
|
118
126
|
|
|
119
127
|
it("throws on unknown bitmap field", () => {
|
|
120
|
-
|
|
128
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
121
129
|
["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
122
|
-
])
|
|
130
|
+
]);
|
|
123
131
|
HandlerTestUtils.setupMockSymbols({
|
|
124
132
|
bitmapFields: new Map([["StatusFlags", new Map()]]),
|
|
125
133
|
});
|
|
@@ -133,9 +141,9 @@ describe("BitmapHandlers", () => {
|
|
|
133
141
|
});
|
|
134
142
|
|
|
135
143
|
it("throws on compound assignment", () => {
|
|
136
|
-
|
|
144
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
137
145
|
["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
138
|
-
])
|
|
146
|
+
]);
|
|
139
147
|
HandlerTestUtils.setupMockSymbols({
|
|
140
148
|
bitmapFields: new Map([
|
|
141
149
|
["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
|
|
@@ -152,9 +160,9 @@ describe("BitmapHandlers", () => {
|
|
|
152
160
|
});
|
|
153
161
|
|
|
154
162
|
it("validates bitmap field literal", () => {
|
|
155
|
-
|
|
163
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
156
164
|
["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
157
|
-
])
|
|
165
|
+
]);
|
|
158
166
|
HandlerTestUtils.setupMockSymbols({
|
|
159
167
|
bitmapFields: new Map([
|
|
160
168
|
["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
|
|
@@ -180,9 +188,9 @@ describe("BitmapHandlers", () => {
|
|
|
180
188
|
)?.[1];
|
|
181
189
|
|
|
182
190
|
it("generates multi-bit read-modify-write with mask", () => {
|
|
183
|
-
|
|
191
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
184
192
|
["flags", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
185
|
-
])
|
|
193
|
+
]);
|
|
186
194
|
HandlerTestUtils.setupMockSymbols({
|
|
187
195
|
bitmapFields: new Map([
|
|
188
196
|
["StatusFlags", new Map([["Mode", { offset: 4, width: 3 }]])],
|
|
@@ -202,9 +210,9 @@ describe("BitmapHandlers", () => {
|
|
|
202
210
|
});
|
|
203
211
|
|
|
204
212
|
it("generates correct mask for 2-bit field", () => {
|
|
205
|
-
|
|
213
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
206
214
|
["config", { bitmapTypeName: "Config", baseType: "u8" }],
|
|
207
|
-
])
|
|
215
|
+
]);
|
|
208
216
|
HandlerTestUtils.setupMockSymbols({
|
|
209
217
|
bitmapFields: new Map([
|
|
210
218
|
["Config", new Map([["Priority", { offset: 0, width: 2 }]])],
|
|
@@ -228,9 +236,9 @@ describe("BitmapHandlers", () => {
|
|
|
228
236
|
)?.[1];
|
|
229
237
|
|
|
230
238
|
it("generates array element bitmap field assignment", () => {
|
|
231
|
-
|
|
239
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
232
240
|
["flagsArray", { bitmapTypeName: "StatusFlags", baseType: "u8" }],
|
|
233
|
-
])
|
|
241
|
+
]);
|
|
234
242
|
HandlerTestUtils.setupMockGenerator({
|
|
235
243
|
generateExpression: vi.fn().mockReturnValue("i"),
|
|
236
244
|
});
|
|
@@ -258,9 +266,9 @@ describe("BitmapHandlers", () => {
|
|
|
258
266
|
)?.[1];
|
|
259
267
|
|
|
260
268
|
it("generates struct member bitmap field assignment", () => {
|
|
261
|
-
|
|
269
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
262
270
|
["device", { baseType: "Device" }],
|
|
263
|
-
])
|
|
271
|
+
]);
|
|
264
272
|
HandlerTestUtils.setupMockSymbols({
|
|
265
273
|
bitmapFields: new Map([
|
|
266
274
|
["StatusFlags", new Map([["Active", { offset: 2, width: 1 }]])],
|
package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts
CHANGED
|
@@ -16,8 +16,15 @@ import HandlerTestUtils from "./handlerTestUtils";
|
|
|
16
16
|
function createMockContext(
|
|
17
17
|
overrides: Partial<IAssignmentContext> = {},
|
|
18
18
|
): IAssignmentContext {
|
|
19
|
+
// Default resolved values based on first identifier
|
|
20
|
+
const identifiers = overrides.identifiers ?? ["GPIO7", "DR_SET"];
|
|
21
|
+
const resolvedTarget =
|
|
22
|
+
overrides.resolvedTarget ?? `${identifiers.join("_")}[LED_BIT]`;
|
|
23
|
+
const resolvedBaseIdentifier =
|
|
24
|
+
overrides.resolvedBaseIdentifier ?? identifiers[0];
|
|
25
|
+
|
|
19
26
|
return {
|
|
20
|
-
identifiers
|
|
27
|
+
identifiers,
|
|
21
28
|
subscripts: [{ mockValue: "LED_BIT" } as never],
|
|
22
29
|
isCompound: false,
|
|
23
30
|
cnextOp: "<-",
|
|
@@ -34,6 +41,8 @@ function createMockContext(
|
|
|
34
41
|
isSimpleIdentifier: false,
|
|
35
42
|
isSimpleThisAccess: false,
|
|
36
43
|
isSimpleGlobalAccess: false,
|
|
44
|
+
resolvedTarget,
|
|
45
|
+
resolvedBaseIdentifier,
|
|
37
46
|
...overrides,
|
|
38
47
|
} as IAssignmentContext;
|
|
39
48
|
}
|
|
@@ -16,8 +16,14 @@ import HandlerTestUtils from "./handlerTestUtils";
|
|
|
16
16
|
function createMockContext(
|
|
17
17
|
overrides: Partial<IAssignmentContext> = {},
|
|
18
18
|
): IAssignmentContext {
|
|
19
|
+
// Default resolved values based on first identifier
|
|
20
|
+
const identifiers = overrides.identifiers ?? ["counter"];
|
|
21
|
+
const resolvedTarget = overrides.resolvedTarget ?? identifiers[0];
|
|
22
|
+
const resolvedBaseIdentifier =
|
|
23
|
+
overrides.resolvedBaseIdentifier ?? identifiers[0];
|
|
24
|
+
|
|
19
25
|
return {
|
|
20
|
-
identifiers
|
|
26
|
+
identifiers,
|
|
21
27
|
subscripts: [],
|
|
22
28
|
isCompound: true,
|
|
23
29
|
cnextOp: "+<-",
|
|
@@ -34,6 +40,8 @@ function createMockContext(
|
|
|
34
40
|
isSimpleIdentifier: true,
|
|
35
41
|
isSimpleThisAccess: false,
|
|
36
42
|
isSimpleGlobalAccess: false,
|
|
43
|
+
resolvedTarget,
|
|
44
|
+
resolvedBaseIdentifier,
|
|
37
45
|
...overrides,
|
|
38
46
|
} as IAssignmentContext;
|
|
39
47
|
}
|
|
@@ -63,9 +71,9 @@ describe("SpecialHandlers", () => {
|
|
|
63
71
|
specialHandlers.find(([kind]) => kind === AssignmentKind.ATOMIC_RMW)?.[1];
|
|
64
72
|
|
|
65
73
|
it("delegates to generateAtomicRMW for simple identifier", () => {
|
|
66
|
-
|
|
74
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
67
75
|
["counter", { baseType: "u32", isAtomic: true }],
|
|
68
|
-
])
|
|
76
|
+
]);
|
|
69
77
|
const generateAtomicRMW = vi.fn().mockReturnValue("LDREX/STREX pattern");
|
|
70
78
|
const generateAssignmentTarget = vi.fn().mockReturnValue("counter");
|
|
71
79
|
HandlerTestUtils.setupMockGenerator({
|
|
@@ -76,18 +84,23 @@ describe("SpecialHandlers", () => {
|
|
|
76
84
|
|
|
77
85
|
const result = getHandler()!(ctx);
|
|
78
86
|
|
|
79
|
-
expect(generateAtomicRMW).toHaveBeenCalledWith(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
expect(generateAtomicRMW).toHaveBeenCalledWith(
|
|
88
|
+
"counter",
|
|
89
|
+
"+=",
|
|
90
|
+
"1",
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
baseType: "u32",
|
|
93
|
+
isAtomic: true,
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
83
96
|
expect(result).toBe("LDREX/STREX pattern");
|
|
84
97
|
});
|
|
85
98
|
|
|
86
99
|
it("handles this.member atomic variable", () => {
|
|
87
100
|
CodeGenState.currentScope = "Motor";
|
|
88
|
-
|
|
101
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
89
102
|
["Motor_count", { baseType: "u32", isAtomic: true }],
|
|
90
|
-
])
|
|
103
|
+
]);
|
|
91
104
|
const generateAtomicRMW = vi.fn().mockReturnValue("atomic result");
|
|
92
105
|
const generateAssignmentTarget = vi.fn().mockReturnValue("Motor_count");
|
|
93
106
|
HandlerTestUtils.setupMockGenerator({
|
|
@@ -102,17 +115,22 @@ describe("SpecialHandlers", () => {
|
|
|
102
115
|
|
|
103
116
|
const result = getHandler()!(ctx);
|
|
104
117
|
|
|
105
|
-
expect(generateAtomicRMW).toHaveBeenCalledWith(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
expect(generateAtomicRMW).toHaveBeenCalledWith(
|
|
119
|
+
"Motor_count",
|
|
120
|
+
"+=",
|
|
121
|
+
"1",
|
|
122
|
+
expect.objectContaining({
|
|
123
|
+
baseType: "u32",
|
|
124
|
+
isAtomic: true,
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
109
127
|
expect(result).toBe("atomic result");
|
|
110
128
|
});
|
|
111
129
|
|
|
112
130
|
it("handles global.member atomic variable", () => {
|
|
113
|
-
|
|
131
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
114
132
|
["globalCounter", { baseType: "u32", isAtomic: true }],
|
|
115
|
-
])
|
|
133
|
+
]);
|
|
116
134
|
const generateAtomicRMW = vi.fn().mockReturnValue("global atomic result");
|
|
117
135
|
const generateAssignmentTarget = vi.fn().mockReturnValue("globalCounter");
|
|
118
136
|
HandlerTestUtils.setupMockGenerator({
|
|
@@ -131,9 +149,9 @@ describe("SpecialHandlers", () => {
|
|
|
131
149
|
});
|
|
132
150
|
|
|
133
151
|
it("handles subtract operation", () => {
|
|
134
|
-
|
|
152
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
135
153
|
["counter", { baseType: "u32", isAtomic: true }],
|
|
136
|
-
])
|
|
154
|
+
]);
|
|
137
155
|
const generateAtomicRMW = vi.fn().mockReturnValue("atomic sub");
|
|
138
156
|
const generateAssignmentTarget = vi.fn().mockReturnValue("counter");
|
|
139
157
|
HandlerTestUtils.setupMockGenerator({
|
|
@@ -163,9 +181,9 @@ describe("SpecialHandlers", () => {
|
|
|
163
181
|
)?.[1];
|
|
164
182
|
|
|
165
183
|
it("generates clamp add helper for u8", () => {
|
|
166
|
-
|
|
184
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
167
185
|
["saturated", { baseType: "u8", overflowBehavior: "clamp" }],
|
|
168
|
-
])
|
|
186
|
+
]);
|
|
169
187
|
const generateAssignmentTarget = vi.fn().mockReturnValue("saturated");
|
|
170
188
|
HandlerTestUtils.setupMockGenerator({
|
|
171
189
|
generateAssignmentTarget,
|
|
@@ -182,9 +200,9 @@ describe("SpecialHandlers", () => {
|
|
|
182
200
|
});
|
|
183
201
|
|
|
184
202
|
it("generates clamp sub helper for u16", () => {
|
|
185
|
-
|
|
203
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
186
204
|
["value", { baseType: "u16", overflowBehavior: "clamp" }],
|
|
187
|
-
])
|
|
205
|
+
]);
|
|
188
206
|
const generateAssignmentTarget = vi.fn().mockReturnValue("value");
|
|
189
207
|
HandlerTestUtils.setupMockGenerator({
|
|
190
208
|
generateAssignmentTarget,
|
|
@@ -203,9 +221,9 @@ describe("SpecialHandlers", () => {
|
|
|
203
221
|
});
|
|
204
222
|
|
|
205
223
|
it("generates clamp mul helper for u32", () => {
|
|
206
|
-
|
|
224
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
207
225
|
["result", { baseType: "u32", overflowBehavior: "clamp" }],
|
|
208
|
-
])
|
|
226
|
+
]);
|
|
209
227
|
const generateAssignmentTarget = vi.fn().mockReturnValue("result");
|
|
210
228
|
HandlerTestUtils.setupMockGenerator({
|
|
211
229
|
generateAssignmentTarget,
|
|
@@ -224,9 +242,9 @@ describe("SpecialHandlers", () => {
|
|
|
224
242
|
});
|
|
225
243
|
|
|
226
244
|
it("uses native arithmetic for float types", () => {
|
|
227
|
-
|
|
245
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
228
246
|
["f", { baseType: "f32", overflowBehavior: "clamp" }],
|
|
229
|
-
])
|
|
247
|
+
]);
|
|
230
248
|
const generateAssignmentTarget = vi.fn().mockReturnValue("f");
|
|
231
249
|
HandlerTestUtils.setupMockGenerator({
|
|
232
250
|
generateAssignmentTarget,
|
|
@@ -243,9 +261,9 @@ describe("SpecialHandlers", () => {
|
|
|
243
261
|
});
|
|
244
262
|
|
|
245
263
|
it("uses native arithmetic for f64 type", () => {
|
|
246
|
-
|
|
264
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
247
265
|
["d", { baseType: "f64", overflowBehavior: "clamp" }],
|
|
248
|
-
])
|
|
266
|
+
]);
|
|
249
267
|
const generateAssignmentTarget = vi.fn().mockReturnValue("d");
|
|
250
268
|
HandlerTestUtils.setupMockGenerator({
|
|
251
269
|
generateAssignmentTarget,
|
|
@@ -262,9 +280,9 @@ describe("SpecialHandlers", () => {
|
|
|
262
280
|
});
|
|
263
281
|
|
|
264
282
|
it("falls back to native for unsupported operators", () => {
|
|
265
|
-
|
|
283
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
266
284
|
["value", { baseType: "u32", overflowBehavior: "clamp" }],
|
|
267
|
-
])
|
|
285
|
+
]);
|
|
268
286
|
const generateAssignmentTarget = vi.fn().mockReturnValue("value");
|
|
269
287
|
HandlerTestUtils.setupMockGenerator({
|
|
270
288
|
generateAssignmentTarget,
|
|
@@ -284,9 +302,9 @@ describe("SpecialHandlers", () => {
|
|
|
284
302
|
|
|
285
303
|
it("handles this.member with clamp", () => {
|
|
286
304
|
CodeGenState.currentScope = "Motor";
|
|
287
|
-
|
|
305
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
288
306
|
["Motor_speed", { baseType: "u8", overflowBehavior: "clamp" }],
|
|
289
|
-
])
|
|
307
|
+
]);
|
|
290
308
|
const generateAssignmentTarget = vi.fn().mockReturnValue("Motor_speed");
|
|
291
309
|
HandlerTestUtils.setupMockGenerator({
|
|
292
310
|
generateAssignmentTarget,
|
|
@@ -305,9 +323,9 @@ describe("SpecialHandlers", () => {
|
|
|
305
323
|
});
|
|
306
324
|
|
|
307
325
|
it("handles global.member with clamp", () => {
|
|
308
|
-
|
|
326
|
+
HandlerTestUtils.setupMockTypeRegistry([
|
|
309
327
|
["globalValue", { baseType: "i16", overflowBehavior: "clamp" }],
|
|
310
|
-
])
|
|
328
|
+
]);
|
|
311
329
|
const generateAssignmentTarget = vi.fn().mockReturnValue("globalValue");
|
|
312
330
|
HandlerTestUtils.setupMockGenerator({
|
|
313
331
|
generateAssignmentTarget,
|
|
@@ -16,14 +16,22 @@ import HandlerTestUtils from "./handlerTestUtils";
|
|
|
16
16
|
function createMockContext(
|
|
17
17
|
overrides: Partial<IAssignmentContext> = {},
|
|
18
18
|
): IAssignmentContext {
|
|
19
|
+
// Default resolved values based on first identifier
|
|
20
|
+
const identifiers = overrides.identifiers ?? ["testVar"];
|
|
21
|
+
const resolvedTarget = overrides.resolvedTarget ?? identifiers[0];
|
|
22
|
+
const resolvedBaseIdentifier =
|
|
23
|
+
overrides.resolvedBaseIdentifier ?? identifiers[0];
|
|
24
|
+
|
|
19
25
|
return {
|
|
20
|
-
identifiers
|
|
26
|
+
identifiers,
|
|
21
27
|
subscripts: [],
|
|
22
28
|
isCompound: false,
|
|
23
29
|
cnextOp: "<-",
|
|
24
30
|
cOp: "=",
|
|
25
31
|
generatedValue: '"hello"',
|
|
26
32
|
targetCtx: {} as never,
|
|
33
|
+
resolvedTarget,
|
|
34
|
+
resolvedBaseIdentifier,
|
|
27
35
|
...overrides,
|
|
28
36
|
} as IAssignmentContext;
|
|
29
37
|
}
|
|
@@ -141,15 +141,16 @@ function createTypeInfo(overrides: Partial<TTypeInfo> = {}): TTypeInfo {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
|
-
* Set up CodeGenState
|
|
144
|
+
* Set up CodeGenState type registry with typed entries.
|
|
145
145
|
* Entries only need to specify the fields relevant to the test.
|
|
146
|
+
* Uses setVariableTypeInfo to properly populate the registry.
|
|
146
147
|
*/
|
|
147
148
|
function setupMockTypeRegistry(
|
|
148
149
|
entries: Array<[string, Partial<TTypeInfo>]>,
|
|
149
150
|
): void {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
for (const [name, partial] of entries) {
|
|
152
|
+
CodeGenState.setVariableTypeInfo(name, createTypeInfo(partial));
|
|
153
|
+
}
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
export default class HandlerTestUtils {
|
|
@@ -17,6 +17,7 @@ import IGeneratorInput from "../IGeneratorInput";
|
|
|
17
17
|
import IGeneratorState from "../IGeneratorState";
|
|
18
18
|
import IOrchestrator from "../IOrchestrator";
|
|
19
19
|
import CallExprUtils from "./CallExprUtils";
|
|
20
|
+
import CodeGenState from "../../../../state/CodeGenState";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Issue #304: Wrap argument with static_cast if it's a C++ enum class
|
|
@@ -80,7 +81,7 @@ const _generateCFunctionArg = (
|
|
|
80
81
|
// Issue #322: If getExpressionType returns null (e.g., for this.member),
|
|
81
82
|
// fall back to looking up the generated code in the type registry
|
|
82
83
|
if (!argType && !argCode.startsWith("&")) {
|
|
83
|
-
const typeInfo =
|
|
84
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
|
|
84
85
|
if (typeInfo) {
|
|
85
86
|
argType = typeInfo.baseType;
|
|
86
87
|
}
|
|
@@ -119,8 +120,15 @@ const _shouldPassByValue = (
|
|
|
119
120
|
funcExpr,
|
|
120
121
|
idx,
|
|
121
122
|
);
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
// Issue #786: For cross-file calls, check if parameter is a known primitive type.
|
|
125
|
+
// Known primitives (u8-u64, i8-i64, bool) should always be pass-by-value.
|
|
126
|
+
// This handles the case where local passByValueParams isn't populated for cross-file functions.
|
|
127
|
+
const isCrossFilePrimitive =
|
|
128
|
+
isCrossFile &&
|
|
129
|
+
CallExprUtils.isKnownPrimitiveType(targetParam.baseType) &&
|
|
130
|
+
!orchestrator.isStructType(targetParam.baseType) &&
|
|
131
|
+
!CallExprUtils.isStringType(targetParam.baseType);
|
|
124
132
|
|
|
125
133
|
// Issue #551: Unknown types (external enums, typedefs) use pass-by-value
|
|
126
134
|
const isUnknownType =
|
|
@@ -129,13 +137,13 @@ const _shouldPassByValue = (
|
|
|
129
137
|
!CallExprUtils.isStringType(targetParam.baseType) &&
|
|
130
138
|
!isFloatParam &&
|
|
131
139
|
!isEnumParam &&
|
|
132
|
-
!
|
|
140
|
+
!isCrossFilePrimitive;
|
|
133
141
|
|
|
134
142
|
return (
|
|
135
143
|
isFloatParam ||
|
|
136
144
|
isEnumParam ||
|
|
137
145
|
isPrimitivePassByValue ||
|
|
138
|
-
|
|
146
|
+
isCrossFilePrimitive ||
|
|
139
147
|
isUnknownType
|
|
140
148
|
);
|
|
141
149
|
};
|
|
@@ -259,7 +267,7 @@ const generateSafeDivMod = (
|
|
|
259
267
|
}
|
|
260
268
|
|
|
261
269
|
// Look up the type of the output parameter
|
|
262
|
-
const typeInfo =
|
|
270
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(outputArgId);
|
|
263
271
|
if (!typeInfo) {
|
|
264
272
|
throw new Error(
|
|
265
273
|
`Cannot determine type of output parameter '${outputArgId}' for ${funcName}`,
|
|
@@ -27,6 +27,7 @@ import SubscriptClassifier from "../../subscript/SubscriptClassifier";
|
|
|
27
27
|
import TYPE_WIDTH from "../../types/TYPE_WIDTH";
|
|
28
28
|
import C_TYPE_WIDTH from "../../types/C_TYPE_WIDTH";
|
|
29
29
|
import TTypeInfo from "../../types/TTypeInfo";
|
|
30
|
+
import CodeGenState from "../../../../state/CodeGenState";
|
|
30
31
|
|
|
31
32
|
// ========================================================================
|
|
32
33
|
// Tracking State
|
|
@@ -67,7 +68,7 @@ const initializeTrackingState = (
|
|
|
67
68
|
: false;
|
|
68
69
|
|
|
69
70
|
const primaryBaseType = rootIdentifier
|
|
70
|
-
?
|
|
71
|
+
? CodeGenState.getVariableTypeInfo(rootIdentifier)?.baseType
|
|
71
72
|
: undefined;
|
|
72
73
|
const currentStructType =
|
|
73
74
|
primaryBaseType && orchestrator.isKnownStruct(primaryBaseType)
|
|
@@ -162,7 +163,7 @@ const generatePostfixExpression = (
|
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
const primaryTypeInfo = rootIdentifier
|
|
165
|
-
?
|
|
166
|
+
? CodeGenState.getVariableTypeInfo(rootIdentifier)
|
|
166
167
|
: undefined;
|
|
167
168
|
|
|
168
169
|
const tracking = initializeTrackingState(
|
|
@@ -364,7 +365,7 @@ const handleGlobalPrefix = (
|
|
|
364
365
|
}
|
|
365
366
|
|
|
366
367
|
// Issue #612: Set currentStructType for global struct variables
|
|
367
|
-
const globalTypeInfo =
|
|
368
|
+
const globalTypeInfo = CodeGenState.getVariableTypeInfo(memberName);
|
|
368
369
|
if (globalTypeInfo && orchestrator.isKnownStruct(globalTypeInfo.baseType)) {
|
|
369
370
|
tracking.currentStructType = globalTypeInfo.baseType;
|
|
370
371
|
}
|
|
@@ -396,7 +397,7 @@ const handleThisScopeLength = (
|
|
|
396
397
|
|
|
397
398
|
tracking.result = `${state.currentScope}_${memberName}`;
|
|
398
399
|
tracking.resolvedIdentifier = tracking.result;
|
|
399
|
-
const resolvedTypeInfo =
|
|
400
|
+
const resolvedTypeInfo = CodeGenState.getVariableTypeInfo(tracking.result);
|
|
400
401
|
if (
|
|
401
402
|
resolvedTypeInfo &&
|
|
402
403
|
orchestrator.isKnownStruct(resolvedTypeInfo.baseType)
|
|
@@ -422,7 +423,9 @@ const resolveStringTypeInfo = (
|
|
|
422
423
|
orchestrator: IOrchestrator,
|
|
423
424
|
): TTypeInfo | undefined => {
|
|
424
425
|
const identifier = tracking.resolvedIdentifier ?? rootIdentifier;
|
|
425
|
-
const typeInfo = identifier
|
|
426
|
+
const typeInfo = identifier
|
|
427
|
+
? CodeGenState.getVariableTypeInfo(identifier)
|
|
428
|
+
: undefined;
|
|
426
429
|
if (typeInfo?.isString) {
|
|
427
430
|
return typeInfo;
|
|
428
431
|
}
|
|
@@ -650,7 +653,7 @@ const generateLengthProperty = (
|
|
|
650
653
|
|
|
651
654
|
// Fall back to checking the current resolved identifier's type
|
|
652
655
|
const typeInfo = ctx.resolvedIdentifier
|
|
653
|
-
?
|
|
656
|
+
? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
|
|
654
657
|
: undefined;
|
|
655
658
|
|
|
656
659
|
if (!typeInfo) {
|
|
@@ -931,7 +934,7 @@ const generateBitLengthProperty = (
|
|
|
931
934
|
|
|
932
935
|
// Get type info for the resolved identifier
|
|
933
936
|
const typeInfo = ctx.resolvedIdentifier
|
|
934
|
-
?
|
|
937
|
+
? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
|
|
935
938
|
: undefined;
|
|
936
939
|
|
|
937
940
|
if (!typeInfo) {
|
|
@@ -1185,7 +1188,7 @@ const generateByteLengthProperty = (
|
|
|
1185
1188
|
|
|
1186
1189
|
// Get type info for the resolved identifier
|
|
1187
1190
|
const typeInfo = ctx.resolvedIdentifier
|
|
1188
|
-
?
|
|
1191
|
+
? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
|
|
1189
1192
|
: undefined;
|
|
1190
1193
|
|
|
1191
1194
|
if (!typeInfo) {
|
|
@@ -1251,10 +1254,10 @@ const generateStructFieldElementCount = (
|
|
|
1251
1254
|
*/
|
|
1252
1255
|
const generateTypeInfoElementCount = (
|
|
1253
1256
|
ctx: IExplicitLengthContext,
|
|
1254
|
-
|
|
1257
|
+
_input: IGeneratorInput,
|
|
1255
1258
|
): string => {
|
|
1256
1259
|
const typeInfo = ctx.resolvedIdentifier
|
|
1257
|
-
?
|
|
1260
|
+
? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
|
|
1258
1261
|
: undefined;
|
|
1259
1262
|
|
|
1260
1263
|
if (!typeInfo) {
|
|
@@ -1350,7 +1353,7 @@ const generateCharCountProperty = (
|
|
|
1350
1353
|
|
|
1351
1354
|
// Get type info
|
|
1352
1355
|
const typeInfo = ctx.resolvedIdentifier
|
|
1353
|
-
?
|
|
1356
|
+
? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
|
|
1354
1357
|
: undefined;
|
|
1355
1358
|
|
|
1356
1359
|
if (!typeInfo) {
|
|
@@ -1484,7 +1487,7 @@ const tryBitmapFieldAccess = (
|
|
|
1484
1487
|
if (!ctx.rootIdentifier) {
|
|
1485
1488
|
return null;
|
|
1486
1489
|
}
|
|
1487
|
-
const typeInfo =
|
|
1490
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(ctx.rootIdentifier);
|
|
1488
1491
|
if (!typeInfo?.isBitmap || !typeInfo.bitmapTypeName) {
|
|
1489
1492
|
return null;
|
|
1490
1493
|
}
|
|
@@ -1525,7 +1528,7 @@ const tryScopeMemberAccess = (
|
|
|
1525
1528
|
output.result = fullName;
|
|
1526
1529
|
output.resolvedIdentifier = fullName;
|
|
1527
1530
|
if (!input.symbols!.knownEnums.has(fullName)) {
|
|
1528
|
-
const resolvedTypeInfo =
|
|
1531
|
+
const resolvedTypeInfo = CodeGenState.getVariableTypeInfo(fullName);
|
|
1529
1532
|
if (
|
|
1530
1533
|
resolvedTypeInfo &&
|
|
1531
1534
|
orchestrator.isKnownStruct(resolvedTypeInfo.baseType)
|
|
@@ -1569,7 +1572,7 @@ const tryKnownScopeAccess = (
|
|
|
1569
1572
|
const output = initializeMemberOutput(ctx);
|
|
1570
1573
|
output.result = `${ctx.result}${orchestrator.getScopeSeparator(ctx.isCppAccessChain)}${ctx.memberName}`;
|
|
1571
1574
|
output.resolvedIdentifier = output.result;
|
|
1572
|
-
const resolvedTypeInfo =
|
|
1575
|
+
const resolvedTypeInfo = CodeGenState.getVariableTypeInfo(output.result);
|
|
1573
1576
|
if (
|
|
1574
1577
|
resolvedTypeInfo &&
|
|
1575
1578
|
orchestrator.isKnownStruct(resolvedTypeInfo.baseType)
|
|
@@ -1920,11 +1923,11 @@ const checkRegisterAccess = (
|
|
|
1920
1923
|
*/
|
|
1921
1924
|
const getIdentifierTypeInfo = (
|
|
1922
1925
|
ctx: ISubscriptAccessContext,
|
|
1923
|
-
|
|
1926
|
+
_input: IGeneratorInput,
|
|
1924
1927
|
): TTypeInfo | undefined => {
|
|
1925
1928
|
const identifierToCheck = ctx.resolvedIdentifier || ctx.rootIdentifier;
|
|
1926
1929
|
return identifierToCheck
|
|
1927
|
-
?
|
|
1930
|
+
? CodeGenState.getVariableTypeInfo(identifierToCheck)
|
|
1928
1931
|
: undefined;
|
|
1929
1932
|
};
|
|
1930
1933
|
|
package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import generateFunctionCall from "../CallExprGenerator";
|
|
3
3
|
import IGeneratorInput from "../../IGeneratorInput";
|
|
4
4
|
import IGeneratorState from "../../IGeneratorState";
|
|
5
5
|
import IOrchestrator from "../../IOrchestrator";
|
|
6
6
|
import * as Parser from "../../../../../logic/parser/grammar/CNextParser";
|
|
7
7
|
import ESymbolKind from "../../../../../../utils/types/ESymbolKind";
|
|
8
|
+
import CodeGenState from "../../../../../state/CodeGenState";
|
|
9
|
+
import TTypeInfo from "../../../types/TTypeInfo";
|
|
8
10
|
|
|
9
11
|
// ========================================================================
|
|
10
12
|
// Test Helpers
|
|
@@ -27,10 +29,18 @@ function createMockArgListContext(
|
|
|
27
29
|
function createMockInput(
|
|
28
30
|
overrides: Partial<IGeneratorInput> = {},
|
|
29
31
|
): IGeneratorInput {
|
|
32
|
+
// Also populate CodeGenState with the type registry entries
|
|
33
|
+
// This is needed because CallExprGenerator now uses CodeGenState directly
|
|
34
|
+
const typeRegistry =
|
|
35
|
+
(overrides.typeRegistry as Map<string, TTypeInfo>) ?? new Map();
|
|
36
|
+
for (const [name, info] of typeRegistry) {
|
|
37
|
+
CodeGenState.setVariableTypeInfo(name, info);
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
return {
|
|
31
41
|
symbols: null,
|
|
32
42
|
symbolTable: null,
|
|
33
|
-
typeRegistry
|
|
43
|
+
typeRegistry,
|
|
34
44
|
functionSignatures: new Map(),
|
|
35
45
|
knownFunctions: new Set(),
|
|
36
46
|
knownStructs: new Set(),
|
|
@@ -94,6 +104,11 @@ function createMockOrchestrator(
|
|
|
94
104
|
// ========================================================================
|
|
95
105
|
|
|
96
106
|
describe("CallExprGenerator", () => {
|
|
107
|
+
// Reset CodeGenState before each test to avoid state pollution
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
CodeGenState.reset();
|
|
110
|
+
});
|
|
111
|
+
|
|
97
112
|
describe("empty function call", () => {
|
|
98
113
|
it("generates call with no arguments when argCtx is null", () => {
|
|
99
114
|
const input = createMockInput();
|
|
@@ -756,7 +771,7 @@ describe("CallExprGenerator", () => {
|
|
|
756
771
|
});
|
|
757
772
|
|
|
758
773
|
describe("cross-file function calls (Issue #315)", () => {
|
|
759
|
-
it("looks up parameter info from SymbolTable
|
|
774
|
+
it("looks up parameter info from SymbolTable and passes primitives by value (Issue #786)", () => {
|
|
760
775
|
const argExprs = [createMockExpressionContext("myVal")];
|
|
761
776
|
const argCtx = createMockArgListContext(argExprs);
|
|
762
777
|
const symbolTable = {
|
|
@@ -777,6 +792,7 @@ describe("CallExprGenerator", () => {
|
|
|
777
792
|
isCNextFunction: vi.fn(() => true),
|
|
778
793
|
isFloatType: vi.fn(() => false),
|
|
779
794
|
isParameterPassByValue: vi.fn(() => false),
|
|
795
|
+
isStructType: vi.fn(() => false),
|
|
780
796
|
});
|
|
781
797
|
|
|
782
798
|
const result = generateFunctionCall(
|
|
@@ -788,7 +804,8 @@ describe("CallExprGenerator", () => {
|
|
|
788
804
|
);
|
|
789
805
|
|
|
790
806
|
expect(symbolTable.getOverloads).toHaveBeenCalledWith("crossFileFunc");
|
|
791
|
-
|
|
807
|
+
// Issue #786: Primitive types like u32 are now passed by value for cross-file calls
|
|
808
|
+
expect(result.code).toBe("crossFileFunc(myVal)");
|
|
792
809
|
});
|
|
793
810
|
|
|
794
811
|
it("passes small primitive by value for cross-file functions", () => {
|