c-next 0.2.16 → 0.2.18
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/README.md +18 -2
- package/dist/index.js +8897 -6260
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +12 -0
- package/package.json +4 -2
- package/src/transpiler/Transpiler.ts +376 -48
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
- package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
- package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
- package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
- package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
- package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
- package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
- package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
- package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
- package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
- package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
- package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
- package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
- package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
- package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
- package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
- package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
- package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
- package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
- package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
- package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +91 -22
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/ICachedFileEntry.ts +7 -0
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/cache/CacheManager.ts +13 -2
- package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
- package/src/utils/constants/TypeConstants.ts +13 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -74,8 +74,13 @@ describe("SubscriptClassifier", () => {
|
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
describe("parameter types (Issue #
|
|
78
|
-
|
|
77
|
+
describe("parameter types (Issue #1100)", () => {
|
|
78
|
+
// A parameter's array-ness is determined solely by its declared type
|
|
79
|
+
// (isArray, from explicit `T[N]` syntax, ADR-006) — identical to local
|
|
80
|
+
// variables. `isParameter` alone must NOT force array classification;
|
|
81
|
+
// that was the Issue #579 heuristic reverted by Issue #1100 because it
|
|
82
|
+
// broke ADR-007 bit-indexing for scalar parameters.
|
|
83
|
+
it("returns bit_single for non-array parameter with single index", () => {
|
|
79
84
|
const typeInfo: TTypeInfo = {
|
|
80
85
|
baseType: "u8",
|
|
81
86
|
bitWidth: 8,
|
|
@@ -88,10 +93,10 @@ describe("SubscriptClassifier", () => {
|
|
|
88
93
|
subscriptCount: 1,
|
|
89
94
|
isRegisterAccess: false,
|
|
90
95
|
});
|
|
91
|
-
expect(result).toBe("
|
|
96
|
+
expect(result).toBe("bit_single");
|
|
92
97
|
});
|
|
93
98
|
|
|
94
|
-
it("returns
|
|
99
|
+
it("returns bit_range for non-array parameter with two indices", () => {
|
|
95
100
|
const typeInfo: TTypeInfo = {
|
|
96
101
|
baseType: "u8",
|
|
97
102
|
bitWidth: 8,
|
|
@@ -104,7 +109,23 @@ describe("SubscriptClassifier", () => {
|
|
|
104
109
|
subscriptCount: 2,
|
|
105
110
|
isRegisterAccess: false,
|
|
106
111
|
});
|
|
107
|
-
expect(result).toBe("
|
|
112
|
+
expect(result).toBe("bit_range");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("returns array_element for array parameter with single index", () => {
|
|
116
|
+
const typeInfo: TTypeInfo = {
|
|
117
|
+
baseType: "u8",
|
|
118
|
+
bitWidth: 8,
|
|
119
|
+
isArray: true,
|
|
120
|
+
isConst: false,
|
|
121
|
+
isParameter: true,
|
|
122
|
+
};
|
|
123
|
+
const result = SubscriptClassifier.classify({
|
|
124
|
+
typeInfo,
|
|
125
|
+
subscriptCount: 1,
|
|
126
|
+
isRegisterAccess: false,
|
|
127
|
+
});
|
|
128
|
+
expect(result).toBe("array_element");
|
|
108
129
|
});
|
|
109
130
|
});
|
|
110
131
|
|
|
@@ -187,7 +208,7 @@ describe("SubscriptClassifier", () => {
|
|
|
187
208
|
expect(SubscriptClassifier.isArrayAccess(typeInfo)).toBe(true);
|
|
188
209
|
});
|
|
189
210
|
|
|
190
|
-
it("returns
|
|
211
|
+
it("returns false for non-array parameter type (Issue #1100)", () => {
|
|
191
212
|
const typeInfo: TTypeInfo = {
|
|
192
213
|
baseType: "u8",
|
|
193
214
|
bitWidth: 8,
|
|
@@ -195,6 +216,17 @@ describe("SubscriptClassifier", () => {
|
|
|
195
216
|
isConst: false,
|
|
196
217
|
isParameter: true,
|
|
197
218
|
};
|
|
219
|
+
expect(SubscriptClassifier.isArrayAccess(typeInfo)).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("returns true for array parameter type", () => {
|
|
223
|
+
const typeInfo: TTypeInfo = {
|
|
224
|
+
baseType: "u8",
|
|
225
|
+
bitWidth: 8,
|
|
226
|
+
isArray: true,
|
|
227
|
+
isConst: false,
|
|
228
|
+
isParameter: true,
|
|
229
|
+
};
|
|
198
230
|
expect(SubscriptClassifier.isArrayAccess(typeInfo)).toBe(true);
|
|
199
231
|
});
|
|
200
232
|
|
|
@@ -10,7 +10,8 @@ interface ICallbackTypeInfo {
|
|
|
10
10
|
name: string;
|
|
11
11
|
type: string; // C type
|
|
12
12
|
isConst: boolean;
|
|
13
|
-
isPointer: boolean; // Non-array params become pointers
|
|
13
|
+
isPointer: boolean; // Non-array params become pointers (C mode only for structs)
|
|
14
|
+
isStruct: boolean; // True if parameter type is a struct (ADR-006 reference semantics)
|
|
14
15
|
isArray: boolean; // Array parameters pass naturally as pointers
|
|
15
16
|
arrayDims: string; // Array dimensions if applicable
|
|
16
17
|
}>;
|
|
@@ -66,6 +66,13 @@ interface IParameterInput {
|
|
|
66
66
|
* When the C typedef has `const T*`, this preserves const on the generated param.
|
|
67
67
|
*/
|
|
68
68
|
forceConst?: boolean;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Issue #995: Whether the parameter type is an opaque handle (incomplete struct typedef).
|
|
72
|
+
* This is a pass-through flag; the rule (suppress auto-const, force pointer) is applied
|
|
73
|
+
* in ParameterSignatureBuilder to avoid dual code paths.
|
|
74
|
+
*/
|
|
75
|
+
isOpaqueHandle?: boolean;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
export default IParameterInput;
|
|
@@ -116,6 +116,14 @@ abstract class BaseHeaderGenerator {
|
|
|
116
116
|
...HeaderGeneratorUtils.generateEnumSection(groups.enums, typeInput),
|
|
117
117
|
...HeaderGeneratorUtils.generateBitmapSection(groups.bitmaps, typeInput),
|
|
118
118
|
...HeaderGeneratorUtils.generateTypeAliasSection(groups.types),
|
|
119
|
+
...HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
120
|
+
groups.structs,
|
|
121
|
+
typeInput,
|
|
122
|
+
),
|
|
123
|
+
...HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
124
|
+
typeInput,
|
|
125
|
+
options.cppMode,
|
|
126
|
+
),
|
|
119
127
|
...HeaderGeneratorUtils.generateStructSection(
|
|
120
128
|
groups.structs,
|
|
121
129
|
groups.classes,
|
|
@@ -17,6 +17,7 @@ import generateStructHeader from "./generators/generateStructHeader";
|
|
|
17
17
|
import generateBitmapHeader from "./generators/generateBitmapHeader";
|
|
18
18
|
import VariableDeclarationFormatter from "../codegen/helpers/VariableDeclarationFormatter";
|
|
19
19
|
import type IVariableFormatInput from "../codegen/types/IVariableFormatInput";
|
|
20
|
+
import MisraSuppressionUtils from "../MisraSuppressionUtils";
|
|
20
21
|
|
|
21
22
|
const { mapType, isBuiltInType } = typeUtils;
|
|
22
23
|
|
|
@@ -267,6 +268,65 @@ class HeaderGeneratorUtils {
|
|
|
267
268
|
];
|
|
268
269
|
}
|
|
269
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Extract header file stem from include directive for deduplication.
|
|
273
|
+
* E.g., '#include <foo/bar.hpp>' -> 'bar'
|
|
274
|
+
* SonarCloud S8786: Avoid backtracking by using separate patterns.
|
|
275
|
+
*/
|
|
276
|
+
private static extractIncludeStem(include: string): string {
|
|
277
|
+
// Try angle brackets first, then quotes - avoids backtracking regex
|
|
278
|
+
const angleMatch = /<([^<>]+)>/.exec(include);
|
|
279
|
+
if (angleMatch) {
|
|
280
|
+
return angleMatch[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
|
|
281
|
+
}
|
|
282
|
+
const quoteMatch = /"([^"]+)"/.exec(include);
|
|
283
|
+
if (quoteMatch) {
|
|
284
|
+
return quoteMatch[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
|
|
285
|
+
}
|
|
286
|
+
return include;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Add user includes with MISRA suppression comments.
|
|
291
|
+
* SonarCloud S3776: Extracted from generateIncludes.
|
|
292
|
+
*/
|
|
293
|
+
private static addUserIncludes(
|
|
294
|
+
lines: string[],
|
|
295
|
+
userIncludes: string[] | undefined,
|
|
296
|
+
): void {
|
|
297
|
+
if (!userIncludes || userIncludes.length === 0) return;
|
|
298
|
+
for (const include of userIncludes) {
|
|
299
|
+
// Issue #850: Add MISRA suppression for banned headers
|
|
300
|
+
const suppression =
|
|
301
|
+
MisraSuppressionUtils.getMisraSuppressionComment(include);
|
|
302
|
+
if (suppression) {
|
|
303
|
+
lines.push(suppression);
|
|
304
|
+
}
|
|
305
|
+
lines.push(include);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Add external type headers, deduplicating against user includes.
|
|
311
|
+
* SonarCloud S3776: Extracted from generateIncludes.
|
|
312
|
+
*/
|
|
313
|
+
private static addExternalTypeHeaders(
|
|
314
|
+
lines: string[],
|
|
315
|
+
headersToInclude: Set<string>,
|
|
316
|
+
userIncludes: string[] | undefined,
|
|
317
|
+
): void {
|
|
318
|
+
const userIncludeSet = new Set(userIncludes ?? []);
|
|
319
|
+
const userIncludeStems = new Set(
|
|
320
|
+
(userIncludes ?? []).map(HeaderGeneratorUtils.extractIncludeStem),
|
|
321
|
+
);
|
|
322
|
+
for (const directive of headersToInclude) {
|
|
323
|
+
if (userIncludeSet.has(directive)) continue;
|
|
324
|
+
const stem = HeaderGeneratorUtils.extractIncludeStem(directive);
|
|
325
|
+
if (stem && userIncludeStems.has(stem)) continue;
|
|
326
|
+
lines.push(directive);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
270
330
|
/**
|
|
271
331
|
* Generate all include directives (system, user, and external type headers)
|
|
272
332
|
*/
|
|
@@ -282,37 +342,18 @@ class HeaderGeneratorUtils {
|
|
|
282
342
|
}
|
|
283
343
|
|
|
284
344
|
// User includes (already have correct extension from IncludeExtractor)
|
|
285
|
-
|
|
286
|
-
for (const include of options.userIncludes) {
|
|
287
|
-
lines.push(include);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
345
|
+
HeaderGeneratorUtils.addUserIncludes(lines, options.userIncludes);
|
|
290
346
|
|
|
291
347
|
// External type header includes (skip duplicates of user includes)
|
|
292
348
|
// Dedup by basename stem to handle:
|
|
293
349
|
// - Different path styles (e.g., <AppConfig.hpp> vs "../AppConfig.hpp")
|
|
294
350
|
// - Extension mismatch from timing (.h from IncludeResolver before cppDetected,
|
|
295
351
|
// .hpp from IncludeExtractor after cppDetected)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return match[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
|
|
301
|
-
};
|
|
302
|
-
const userIncludeStems = new Set(
|
|
303
|
-
(options.userIncludes ?? []).map(extractStem),
|
|
352
|
+
HeaderGeneratorUtils.addExternalTypeHeaders(
|
|
353
|
+
lines,
|
|
354
|
+
headersToInclude,
|
|
355
|
+
options.userIncludes,
|
|
304
356
|
);
|
|
305
|
-
for (const directive of headersToInclude) {
|
|
306
|
-
if (userIncludeSet.has(directive)) {
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
// Check if a user include already covers the same file
|
|
310
|
-
const stem = extractStem(directive);
|
|
311
|
-
if (stem && userIncludeStems.has(stem)) {
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
lines.push(directive);
|
|
315
|
-
}
|
|
316
357
|
|
|
317
358
|
// Add blank line if any includes were added
|
|
318
359
|
const hasIncludes =
|
|
@@ -416,6 +457,81 @@ class HeaderGeneratorUtils {
|
|
|
416
457
|
return lines;
|
|
417
458
|
}
|
|
418
459
|
|
|
460
|
+
/**
|
|
461
|
+
* ADR-029: Generate forward declarations for structs used in callback typedefs.
|
|
462
|
+
* This ensures struct types can be used in callback parameter types before
|
|
463
|
+
* the full struct definition.
|
|
464
|
+
*/
|
|
465
|
+
static generateCallbackStructForwardDecls(
|
|
466
|
+
structs: IHeaderSymbol[],
|
|
467
|
+
typeInput?: IHeaderTypeInput,
|
|
468
|
+
): string[] {
|
|
469
|
+
if (!typeInput?.callbackTypes || typeInput.callbackTypes.size === 0) {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Collect struct types that are used in callback parameters
|
|
474
|
+
const usedStructTypes = new Set<string>();
|
|
475
|
+
for (const [, cbInfo] of typeInput.callbackTypes) {
|
|
476
|
+
for (const p of cbInfo.parameters) {
|
|
477
|
+
if (p.isStruct) {
|
|
478
|
+
usedStructTypes.add(p.type);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (usedStructTypes.size === 0) {
|
|
484
|
+
return [];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Get local struct names to only forward-declare those
|
|
488
|
+
const localStructNames = new Set(structs.map((s) => s.name));
|
|
489
|
+
|
|
490
|
+
const lines: string[] = [];
|
|
491
|
+
for (const structType of usedStructTypes) {
|
|
492
|
+
if (localStructNames.has(structType)) {
|
|
493
|
+
lines.push(`typedef struct ${structType} ${structType};`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return lines.length > 0 ? [...lines, ""] : [];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* ADR-029: Generate callback typedef section
|
|
502
|
+
* Generates function pointer typedefs for callbacks used as struct field types
|
|
503
|
+
*/
|
|
504
|
+
static generateCallbackTypedefSection(
|
|
505
|
+
typeInput?: IHeaderTypeInput,
|
|
506
|
+
isCppMode?: boolean,
|
|
507
|
+
): string[] {
|
|
508
|
+
if (!typeInput?.callbackTypes || typeInput.callbackTypes.size === 0) {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const lines: string[] = ["/* Callback typedefs */"];
|
|
513
|
+
for (const [, cbInfo] of typeInput.callbackTypes) {
|
|
514
|
+
const params =
|
|
515
|
+
cbInfo.parameters.length > 0
|
|
516
|
+
? cbInfo.parameters
|
|
517
|
+
.map((p) => {
|
|
518
|
+
// ADR-006: Struct parameters become pointers (C) or references (C++)
|
|
519
|
+
if (p.isStruct) {
|
|
520
|
+
const ptrOrRef = isCppMode ? "&" : "*";
|
|
521
|
+
return `${p.type}${ptrOrRef}`;
|
|
522
|
+
}
|
|
523
|
+
return p.type;
|
|
524
|
+
})
|
|
525
|
+
.join(", ")
|
|
526
|
+
: "void";
|
|
527
|
+
lines.push(
|
|
528
|
+
`typedef ${cbInfo.returnType} (*${cbInfo.typedefName})(${params});`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
lines.push("");
|
|
532
|
+
return lines;
|
|
533
|
+
}
|
|
534
|
+
|
|
419
535
|
/**
|
|
420
536
|
* Generate struct and class definitions section
|
|
421
537
|
*/
|
|
@@ -804,4 +804,284 @@ describe("HeaderGeneratorUtils", () => {
|
|
|
804
804
|
expect(lines).toContain("#endif /* MY_FILE_H */");
|
|
805
805
|
});
|
|
806
806
|
});
|
|
807
|
+
|
|
808
|
+
describe("generateCallbackStructForwardDecls", () => {
|
|
809
|
+
it("returns empty array when no callback types provided", () => {
|
|
810
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
811
|
+
[],
|
|
812
|
+
);
|
|
813
|
+
expect(result).toEqual([]);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
it("returns empty array when callback types is undefined", () => {
|
|
817
|
+
const input = {
|
|
818
|
+
structFields: new Map(),
|
|
819
|
+
structFieldDimensions: new Map(),
|
|
820
|
+
enumMembers: new Map(),
|
|
821
|
+
bitmapBackingType: new Map(),
|
|
822
|
+
bitmapFields: new Map(),
|
|
823
|
+
callbackTypes: undefined,
|
|
824
|
+
};
|
|
825
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
826
|
+
[],
|
|
827
|
+
input,
|
|
828
|
+
);
|
|
829
|
+
expect(result).toEqual([]);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it("generates forward declarations for struct parameters", () => {
|
|
833
|
+
const structs = [makeSymbol({ name: "DataStruct", kind: "struct" })];
|
|
834
|
+
const input = {
|
|
835
|
+
structFields: new Map(),
|
|
836
|
+
structFieldDimensions: new Map(),
|
|
837
|
+
enumMembers: new Map(),
|
|
838
|
+
bitmapBackingType: new Map(),
|
|
839
|
+
bitmapFields: new Map(),
|
|
840
|
+
callbackTypes: new Map([
|
|
841
|
+
[
|
|
842
|
+
"processData",
|
|
843
|
+
{
|
|
844
|
+
typedefName: "processData_t",
|
|
845
|
+
returnType: "void",
|
|
846
|
+
parameters: [
|
|
847
|
+
{ type: "DataStruct", isStruct: true },
|
|
848
|
+
{ type: "uint32_t", isStruct: false },
|
|
849
|
+
],
|
|
850
|
+
},
|
|
851
|
+
],
|
|
852
|
+
]),
|
|
853
|
+
};
|
|
854
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
855
|
+
structs,
|
|
856
|
+
input,
|
|
857
|
+
);
|
|
858
|
+
expect(result).toContain("typedef struct DataStruct DataStruct;");
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
it("deduplicates struct forward declarations", () => {
|
|
862
|
+
const structs = [makeSymbol({ name: "MyStruct", kind: "struct" })];
|
|
863
|
+
const input = {
|
|
864
|
+
structFields: new Map(),
|
|
865
|
+
structFieldDimensions: new Map(),
|
|
866
|
+
enumMembers: new Map(),
|
|
867
|
+
bitmapBackingType: new Map(),
|
|
868
|
+
bitmapFields: new Map(),
|
|
869
|
+
callbackTypes: new Map([
|
|
870
|
+
[
|
|
871
|
+
"handler1",
|
|
872
|
+
{
|
|
873
|
+
typedefName: "handler1_t",
|
|
874
|
+
returnType: "void",
|
|
875
|
+
parameters: [{ type: "MyStruct", isStruct: true }],
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
[
|
|
879
|
+
"handler2",
|
|
880
|
+
{
|
|
881
|
+
typedefName: "handler2_t",
|
|
882
|
+
returnType: "void",
|
|
883
|
+
parameters: [{ type: "MyStruct", isStruct: true }],
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
]),
|
|
887
|
+
};
|
|
888
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
889
|
+
structs,
|
|
890
|
+
input,
|
|
891
|
+
);
|
|
892
|
+
const myStructDecls = result.filter((l) => l.includes("MyStruct"));
|
|
893
|
+
expect(myStructDecls.length).toBe(1);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it("ignores non-struct parameters", () => {
|
|
897
|
+
const input = {
|
|
898
|
+
structFields: new Map(),
|
|
899
|
+
structFieldDimensions: new Map(),
|
|
900
|
+
enumMembers: new Map(),
|
|
901
|
+
bitmapBackingType: new Map(),
|
|
902
|
+
bitmapFields: new Map(),
|
|
903
|
+
callbackTypes: new Map([
|
|
904
|
+
[
|
|
905
|
+
"handler",
|
|
906
|
+
{
|
|
907
|
+
typedefName: "handler_t",
|
|
908
|
+
returnType: "uint32_t",
|
|
909
|
+
parameters: [
|
|
910
|
+
{ type: "uint32_t", isStruct: false },
|
|
911
|
+
{ type: "bool", isStruct: false },
|
|
912
|
+
],
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
]),
|
|
916
|
+
};
|
|
917
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
918
|
+
[],
|
|
919
|
+
input,
|
|
920
|
+
);
|
|
921
|
+
expect(result).toEqual([]);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it("only includes local structs in forward declarations", () => {
|
|
925
|
+
// ExternalStruct is not in the local structs list
|
|
926
|
+
const structs = [makeSymbol({ name: "LocalStruct", kind: "struct" })];
|
|
927
|
+
const input = {
|
|
928
|
+
structFields: new Map(),
|
|
929
|
+
structFieldDimensions: new Map(),
|
|
930
|
+
enumMembers: new Map(),
|
|
931
|
+
bitmapBackingType: new Map(),
|
|
932
|
+
bitmapFields: new Map(),
|
|
933
|
+
callbackTypes: new Map([
|
|
934
|
+
[
|
|
935
|
+
"handler",
|
|
936
|
+
{
|
|
937
|
+
typedefName: "handler_t",
|
|
938
|
+
returnType: "void",
|
|
939
|
+
parameters: [
|
|
940
|
+
{ type: "LocalStruct", isStruct: true },
|
|
941
|
+
{ type: "ExternalStruct", isStruct: true },
|
|
942
|
+
],
|
|
943
|
+
},
|
|
944
|
+
],
|
|
945
|
+
]),
|
|
946
|
+
};
|
|
947
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
948
|
+
structs,
|
|
949
|
+
input,
|
|
950
|
+
);
|
|
951
|
+
expect(result).toContain("typedef struct LocalStruct LocalStruct;");
|
|
952
|
+
expect(result).not.toContain("ExternalStruct");
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
describe("generateCallbackTypedefSection", () => {
|
|
957
|
+
it("returns empty array when no callback types provided", () => {
|
|
958
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection();
|
|
959
|
+
expect(result).toEqual([]);
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
it("returns empty array when callback types is undefined", () => {
|
|
963
|
+
const input = {
|
|
964
|
+
structFields: new Map(),
|
|
965
|
+
structFieldDimensions: new Map(),
|
|
966
|
+
enumMembers: new Map(),
|
|
967
|
+
bitmapBackingType: new Map(),
|
|
968
|
+
bitmapFields: new Map(),
|
|
969
|
+
callbackTypes: undefined,
|
|
970
|
+
};
|
|
971
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
972
|
+
input,
|
|
973
|
+
false,
|
|
974
|
+
);
|
|
975
|
+
expect(result).toEqual([]);
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
it("generates C-style function pointer typedef", () => {
|
|
979
|
+
const input = {
|
|
980
|
+
structFields: new Map(),
|
|
981
|
+
structFieldDimensions: new Map(),
|
|
982
|
+
enumMembers: new Map(),
|
|
983
|
+
bitmapBackingType: new Map(),
|
|
984
|
+
bitmapFields: new Map(),
|
|
985
|
+
callbackTypes: new Map([
|
|
986
|
+
[
|
|
987
|
+
"processU32",
|
|
988
|
+
{
|
|
989
|
+
typedefName: "processU32_t",
|
|
990
|
+
returnType: "uint32_t",
|
|
991
|
+
parameters: [{ type: "uint32_t", isStruct: false }],
|
|
992
|
+
},
|
|
993
|
+
],
|
|
994
|
+
]),
|
|
995
|
+
};
|
|
996
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
997
|
+
input,
|
|
998
|
+
false,
|
|
999
|
+
);
|
|
1000
|
+
expect(result.join("\n")).toContain(
|
|
1001
|
+
"typedef uint32_t (*processU32_t)(uint32_t);",
|
|
1002
|
+
);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
it("generates C++ reference for struct parameters", () => {
|
|
1006
|
+
const input = {
|
|
1007
|
+
structFields: new Map(),
|
|
1008
|
+
structFieldDimensions: new Map(),
|
|
1009
|
+
enumMembers: new Map(),
|
|
1010
|
+
bitmapBackingType: new Map(),
|
|
1011
|
+
bitmapFields: new Map(),
|
|
1012
|
+
callbackTypes: new Map([
|
|
1013
|
+
[
|
|
1014
|
+
"processPoint",
|
|
1015
|
+
{
|
|
1016
|
+
typedefName: "processPoint_t",
|
|
1017
|
+
returnType: "uint32_t",
|
|
1018
|
+
parameters: [{ type: "Point", isStruct: true }],
|
|
1019
|
+
},
|
|
1020
|
+
],
|
|
1021
|
+
]),
|
|
1022
|
+
};
|
|
1023
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
1024
|
+
input,
|
|
1025
|
+
true,
|
|
1026
|
+
);
|
|
1027
|
+
expect(result.join("\n")).toContain("Point&");
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it("generates C pointer for struct parameters", () => {
|
|
1031
|
+
const input = {
|
|
1032
|
+
structFields: new Map(),
|
|
1033
|
+
structFieldDimensions: new Map(),
|
|
1034
|
+
enumMembers: new Map(),
|
|
1035
|
+
bitmapBackingType: new Map(),
|
|
1036
|
+
bitmapFields: new Map(),
|
|
1037
|
+
callbackTypes: new Map([
|
|
1038
|
+
[
|
|
1039
|
+
"processPoint",
|
|
1040
|
+
{
|
|
1041
|
+
typedefName: "processPoint_t",
|
|
1042
|
+
returnType: "uint32_t",
|
|
1043
|
+
parameters: [{ type: "Point", isStruct: true }],
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
]),
|
|
1047
|
+
};
|
|
1048
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
1049
|
+
input,
|
|
1050
|
+
false,
|
|
1051
|
+
);
|
|
1052
|
+
expect(result.join("\n")).toContain("Point*");
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it("handles multiple parameters", () => {
|
|
1056
|
+
const input = {
|
|
1057
|
+
structFields: new Map(),
|
|
1058
|
+
structFieldDimensions: new Map(),
|
|
1059
|
+
enumMembers: new Map(),
|
|
1060
|
+
bitmapBackingType: new Map(),
|
|
1061
|
+
bitmapFields: new Map(),
|
|
1062
|
+
callbackTypes: new Map([
|
|
1063
|
+
[
|
|
1064
|
+
"multiParam",
|
|
1065
|
+
{
|
|
1066
|
+
typedefName: "multiParam_t",
|
|
1067
|
+
returnType: "void",
|
|
1068
|
+
parameters: [
|
|
1069
|
+
{ type: "uint32_t", isStruct: false },
|
|
1070
|
+
{ type: "Point", isStruct: true },
|
|
1071
|
+
{ type: "bool", isStruct: false },
|
|
1072
|
+
],
|
|
1073
|
+
},
|
|
1074
|
+
],
|
|
1075
|
+
]),
|
|
1076
|
+
};
|
|
1077
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
1078
|
+
input,
|
|
1079
|
+
false,
|
|
1080
|
+
);
|
|
1081
|
+
const typedef = result.find((l) => l.includes("multiParam_t"));
|
|
1082
|
+
expect(typedef).toContain("uint32_t");
|
|
1083
|
+
expect(typedef).toContain("Point*");
|
|
1084
|
+
expect(typedef).toContain("bool");
|
|
1085
|
+
});
|
|
1086
|
+
});
|
|
807
1087
|
});
|
|
@@ -31,6 +31,19 @@ interface IHeaderTypeInput {
|
|
|
31
31
|
string,
|
|
32
32
|
ReadonlyMap<string, { readonly offset: number; readonly width: number }>
|
|
33
33
|
>;
|
|
34
|
+
|
|
35
|
+
/** Callback types: functionName -> typedef info for header generation */
|
|
36
|
+
readonly callbackTypes?: ReadonlyMap<
|
|
37
|
+
string,
|
|
38
|
+
{
|
|
39
|
+
readonly typedefName: string;
|
|
40
|
+
readonly returnType: string;
|
|
41
|
+
readonly parameters: ReadonlyArray<{
|
|
42
|
+
readonly type: string;
|
|
43
|
+
readonly isStruct: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
>;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
export default IHeaderTypeInput;
|