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
|
@@ -11,6 +11,52 @@ import CppNamespaceUtils from "../../../../utils/CppNamespaceUtils";
|
|
|
11
11
|
|
|
12
12
|
const { mapType } = typeUtils;
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the C type for a struct field, checking for callback types first.
|
|
16
|
+
*/
|
|
17
|
+
function resolveFieldCType(fieldType: string, input: IHeaderTypeInput): string {
|
|
18
|
+
// ADR-029: Check if field type is a callback type and use typedef name
|
|
19
|
+
const callbackInfo = input.callbackTypes?.get(fieldType);
|
|
20
|
+
if (callbackInfo) {
|
|
21
|
+
return callbackInfo.typedefName;
|
|
22
|
+
}
|
|
23
|
+
// Issue #502/#522: Convert C++ namespace types from _ to :: format
|
|
24
|
+
const convertedType = CppNamespaceUtils.convertToCppNamespace(
|
|
25
|
+
fieldType,
|
|
26
|
+
input.symbolTable,
|
|
27
|
+
);
|
|
28
|
+
return mapType(convertedType);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate the field line for a struct member, handling embedded dimensions.
|
|
33
|
+
*
|
|
34
|
+
* Issue #461: Handle string<N> types which map to char[N+1]
|
|
35
|
+
* The embedded dimension must come after array dimensions in C syntax.
|
|
36
|
+
* Example: string<64> arr[4] -> char arr[4][65], not char[65] arr[4]
|
|
37
|
+
*/
|
|
38
|
+
function generateFieldLine(
|
|
39
|
+
fieldName: string,
|
|
40
|
+
cType: string,
|
|
41
|
+
dims: readonly number[] | undefined,
|
|
42
|
+
): string {
|
|
43
|
+
const dimSuffix =
|
|
44
|
+
dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
|
|
45
|
+
const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
|
|
46
|
+
|
|
47
|
+
if (!embeddedMatch) {
|
|
48
|
+
return ` ${cType} ${fieldName}${dimSuffix};`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const baseType = embeddedMatch[1];
|
|
52
|
+
const embeddedDim = embeddedMatch[2];
|
|
53
|
+
// When dims are provided, they already include string capacity from StructCollector
|
|
54
|
+
if (dims && dims.length > 0) {
|
|
55
|
+
return ` ${baseType} ${fieldName}${dimSuffix};`;
|
|
56
|
+
}
|
|
57
|
+
return ` ${baseType} ${fieldName}[${embeddedDim}];`;
|
|
58
|
+
}
|
|
59
|
+
|
|
14
60
|
/**
|
|
15
61
|
* Generate a C typedef struct declaration for the given struct name.
|
|
16
62
|
*
|
|
@@ -41,35 +87,9 @@ function generateStructHeader(name: string, input: IHeaderTypeInput): string {
|
|
|
41
87
|
|
|
42
88
|
// Iterate fields in insertion order (Map preserves order)
|
|
43
89
|
for (const [fieldName, fieldType] of fields) {
|
|
44
|
-
|
|
45
|
-
const convertedType = CppNamespaceUtils.convertToCppNamespace(
|
|
46
|
-
fieldType,
|
|
47
|
-
input.symbolTable,
|
|
48
|
-
);
|
|
49
|
-
const cType = mapType(convertedType);
|
|
90
|
+
const cType = resolveFieldCType(fieldType, input);
|
|
50
91
|
const dims = dimensions?.get(fieldName);
|
|
51
|
-
|
|
52
|
-
dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
|
|
53
|
-
|
|
54
|
-
// Issue #461: Handle string<N> types which map to char[N+1]
|
|
55
|
-
// The embedded dimension must come after array dimensions in C syntax
|
|
56
|
-
// Example: string<64> arr[4] -> char arr[4][65], not char[65] arr[4]
|
|
57
|
-
//
|
|
58
|
-
// Fix: When dims already include the string capacity (from StructCollector),
|
|
59
|
-
// use dimSuffix directly. Only extract embedded dim when no dims provided.
|
|
60
|
-
const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
|
|
61
|
-
if (embeddedMatch) {
|
|
62
|
-
const baseType = embeddedMatch[1];
|
|
63
|
-
const embeddedDim = embeddedMatch[2];
|
|
64
|
-
// dims already includes string capacity, so just use dimSuffix
|
|
65
|
-
if (dims && dims.length > 0) {
|
|
66
|
-
lines.push(` ${baseType} ${fieldName}${dimSuffix};`);
|
|
67
|
-
} else {
|
|
68
|
-
lines.push(` ${baseType} ${fieldName}[${embeddedDim}];`);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
lines.push(` ${cType} ${fieldName}${dimSuffix};`);
|
|
72
|
-
}
|
|
92
|
+
lines.push(generateFieldLine(fieldName, cType, dims));
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
lines.push(`} ${name};`);
|
|
@@ -30,6 +30,7 @@ import ICallbackTypeInfo from "../output/codegen/types/ICallbackTypeInfo";
|
|
|
30
30
|
import ITargetCapabilities from "../output/codegen/types/ITargetCapabilities";
|
|
31
31
|
import TOverflowBehavior from "../output/codegen/types/TOverflowBehavior";
|
|
32
32
|
import TYPE_WIDTH from "../output/codegen/types/TYPE_WIDTH";
|
|
33
|
+
import type ICodeGenApi from "../output/codegen/types/ICodeGenApi";
|
|
33
34
|
import TypeResolver from "../../utils/TypeResolver";
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -73,10 +74,26 @@ export default class CodeGenState {
|
|
|
73
74
|
// ===========================================================================
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Reference to the CodeGenerator instance for handlers to call methods
|
|
77
|
-
*
|
|
77
|
+
* Reference to the CodeGenerator instance for handlers to call its methods,
|
|
78
|
+
* typed to the method subset they use (ICodeGenApi). `import type` keeps this a
|
|
79
|
+
* compile-time-only edge, and CodeGenState already imports sibling types from
|
|
80
|
+
* output/codegen/types, so this introduces no runtime/circular dependency.
|
|
78
81
|
*/
|
|
79
|
-
static generator:
|
|
82
|
+
static generator: ICodeGenApi | null = null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The CodeGenerator instance, asserted present. Handlers call this instead of
|
|
86
|
+
* each casting `generator` themselves — one source of truth for the access and
|
|
87
|
+
* null-check (the generator is always set before any handler runs).
|
|
88
|
+
*/
|
|
89
|
+
static requireGenerator(): ICodeGenApi {
|
|
90
|
+
if (CodeGenState.generator === null) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"CodeGenState.generator is not set; codegen accessed before initialization.",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return CodeGenState.generator;
|
|
96
|
+
}
|
|
80
97
|
|
|
81
98
|
// ===========================================================================
|
|
82
99
|
// SYMBOL DATA (read-only after initialization)
|
|
@@ -142,9 +159,6 @@ export default class CodeGenState {
|
|
|
142
159
|
/** Tracks which parameters are modified (directly or transitively) */
|
|
143
160
|
static modifiedParameters: Map<string, Set<string>> = new Map();
|
|
144
161
|
|
|
145
|
-
/** Issue #579: Parameters with subscript access (must become pointers) */
|
|
146
|
-
static subscriptAccessedParameters: Map<string, Set<string>> = new Map();
|
|
147
|
-
|
|
148
162
|
/** Parameters that should pass by value (small, unmodified primitives) */
|
|
149
163
|
static passByValueParams: Map<string, Set<string>> = new Map();
|
|
150
164
|
|
|
@@ -217,6 +231,12 @@ export default class CodeGenState {
|
|
|
217
231
|
/** Whether we're inside a function body */
|
|
218
232
|
static inFunctionBody: boolean = false;
|
|
219
233
|
|
|
234
|
+
/** Whether we're generating the RHS of a variable declaration initializer.
|
|
235
|
+
* When true, struct literals use { .field = value } instead of (Type){ .field = value }
|
|
236
|
+
* because plain designated initializers are valid C99 at any scope, while compound
|
|
237
|
+
* literals are not constant expressions and fail at file scope on GCC < 13. */
|
|
238
|
+
static inDeclarationInit: boolean = false;
|
|
239
|
+
|
|
220
240
|
/** Expected type for struct initializers and enum inference */
|
|
221
241
|
static expectedType: string | null = null;
|
|
222
242
|
|
|
@@ -357,7 +377,6 @@ export default class CodeGenState {
|
|
|
357
377
|
|
|
358
378
|
// Pass-by-value analysis
|
|
359
379
|
this.modifiedParameters = new Map();
|
|
360
|
-
this.subscriptAccessedParameters = new Map();
|
|
361
380
|
this.passByValueParams = new Map();
|
|
362
381
|
this.functionCallGraph = new Map();
|
|
363
382
|
this.functionParamLists = new Map();
|
|
@@ -381,6 +400,7 @@ export default class CodeGenState {
|
|
|
381
400
|
// Generation state
|
|
382
401
|
this.indentLevel = 0;
|
|
383
402
|
this.inFunctionBody = false;
|
|
403
|
+
this.inDeclarationInit = false;
|
|
384
404
|
this.expectedType = null;
|
|
385
405
|
this.suppressBareEnumResolution = false;
|
|
386
406
|
this.mainArgsName = null;
|
|
@@ -476,6 +496,49 @@ export default class CodeGenState {
|
|
|
476
496
|
}
|
|
477
497
|
}
|
|
478
498
|
|
|
499
|
+
/** Execute fn with inDeclarationInit=true, restoring prior value on exit. */
|
|
500
|
+
static withDeclarationInit<T>(fn: () => T): T {
|
|
501
|
+
const saved = this.inDeclarationInit;
|
|
502
|
+
this.inDeclarationInit = true;
|
|
503
|
+
try {
|
|
504
|
+
return fn();
|
|
505
|
+
} finally {
|
|
506
|
+
this.inDeclarationInit = saved;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/** Execute fn with inDeclarationInit=false, restoring prior value on exit.
|
|
511
|
+
* Used in sub-expression contexts (function args, ternary arms) where
|
|
512
|
+
* plain designated initializers are not valid C. */
|
|
513
|
+
static withoutDeclarationInit<T>(fn: () => T): T {
|
|
514
|
+
const saved = this.inDeclarationInit;
|
|
515
|
+
this.inDeclarationInit = false;
|
|
516
|
+
try {
|
|
517
|
+
return fn();
|
|
518
|
+
} finally {
|
|
519
|
+
this.inDeclarationInit = saved;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Execute fn with expectedType=null, restoring prior value on exit.
|
|
525
|
+
* Issue #1032: Used in comparison contexts (relational/equality expressions)
|
|
526
|
+
* where MISRA 7.2 U suffix should NOT be applied - comparing `i32 < 0`
|
|
527
|
+
* should not generate `signedIdx < 0U` which changes comparison semantics.
|
|
528
|
+
*/
|
|
529
|
+
static withoutExpectedType<T>(fn: () => T): T {
|
|
530
|
+
const savedType = this.expectedType;
|
|
531
|
+
const savedSuppress = this.suppressBareEnumResolution;
|
|
532
|
+
this.expectedType = null;
|
|
533
|
+
this.suppressBareEnumResolution = false;
|
|
534
|
+
try {
|
|
535
|
+
return fn();
|
|
536
|
+
} finally {
|
|
537
|
+
this.expectedType = savedType;
|
|
538
|
+
this.suppressBareEnumResolution = savedSuppress;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
479
542
|
// ===========================================================================
|
|
480
543
|
// CONVENIENCE LOOKUP METHODS
|
|
481
544
|
// ===========================================================================
|
|
@@ -720,15 +783,6 @@ export default class CodeGenState {
|
|
|
720
783
|
return this.passByValueParams.get(funcName)?.has(paramName) ?? false;
|
|
721
784
|
}
|
|
722
785
|
|
|
723
|
-
/**
|
|
724
|
-
* Check if a parameter has subscript access.
|
|
725
|
-
*/
|
|
726
|
-
static hasSubscriptAccess(funcName: string, paramName: string): boolean {
|
|
727
|
-
return (
|
|
728
|
-
this.subscriptAccessedParameters.get(funcName)?.has(paramName) ?? false
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
786
|
/**
|
|
733
787
|
* Get the function signature for a function.
|
|
734
788
|
*/
|
|
@@ -1136,14 +1190,29 @@ export default class CodeGenState {
|
|
|
1136
1190
|
}
|
|
1137
1191
|
|
|
1138
1192
|
/**
|
|
1139
|
-
* Check if
|
|
1140
|
-
* Used during
|
|
1193
|
+
* Check if generated code accesses an opaque scope variable (and is thus
|
|
1194
|
+
* already a pointer). Used during argument generation to decide whether an
|
|
1195
|
+
* address-of (&) prefix is needed.
|
|
1141
1196
|
*
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
1197
|
+
* Handles two forms:
|
|
1198
|
+
* - Direct access: "MyScope_widget" → the handle itself (pointer)
|
|
1199
|
+
* - Array-element access: "MyScope_widgets[i]" → an element of an opaque
|
|
1200
|
+
* handle array, which is itself a pointer (Issue #996)
|
|
1201
|
+
*
|
|
1202
|
+
* @param generatedCode - The generated access expression (e.g. "UI_widgets[i]")
|
|
1203
|
+
* @returns true if this resolves to an opaque scope variable (already a pointer)
|
|
1144
1204
|
*/
|
|
1145
|
-
static
|
|
1146
|
-
|
|
1205
|
+
static isOpaqueScopeVariableAccess(generatedCode: string): boolean {
|
|
1206
|
+
if (this.opaqueScopeVariables.has(generatedCode)) {
|
|
1207
|
+
return true;
|
|
1208
|
+
}
|
|
1209
|
+
// Issue #996: An element of an opaque-handle array is already a pointer.
|
|
1210
|
+
// Match on the base array name that precedes the subscript.
|
|
1211
|
+
const bracketIndex = generatedCode.indexOf("[");
|
|
1212
|
+
if (bracketIndex === -1) {
|
|
1213
|
+
return false;
|
|
1214
|
+
}
|
|
1215
|
+
return this.opaqueScopeVariables.has(generatedCode.slice(0, bracketIndex));
|
|
1147
1216
|
}
|
|
1148
1217
|
|
|
1149
1218
|
// ===========================================================================
|
|
@@ -376,6 +376,7 @@ describe("CodeGenState", () => {
|
|
|
376
376
|
isArray: false,
|
|
377
377
|
isConst: false,
|
|
378
378
|
isPointer: false,
|
|
379
|
+
isStruct: false,
|
|
379
380
|
arrayDims: "",
|
|
380
381
|
},
|
|
381
382
|
],
|
|
@@ -1011,25 +1012,35 @@ describe("CodeGenState", () => {
|
|
|
1011
1012
|
describe("Opaque Scope Variable Helpers (Issue #948)", () => {
|
|
1012
1013
|
it("markOpaqueScopeVariable adds to opaqueScopeVariables", () => {
|
|
1013
1014
|
CodeGenState.markOpaqueScopeVariable("MyScope_widget");
|
|
1014
|
-
expect(CodeGenState.
|
|
1015
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("MyScope_widget")).toBe(
|
|
1016
|
+
true,
|
|
1017
|
+
);
|
|
1015
1018
|
});
|
|
1016
1019
|
|
|
1017
|
-
it("
|
|
1018
|
-
expect(CodeGenState.
|
|
1020
|
+
it("isOpaqueScopeVariableAccess returns false for unknown variable", () => {
|
|
1021
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Unknown_var")).toBe(
|
|
1022
|
+
false,
|
|
1023
|
+
);
|
|
1019
1024
|
});
|
|
1020
1025
|
|
|
1021
|
-
it("
|
|
1026
|
+
it("isOpaqueScopeVariableAccess returns true for marked variable", () => {
|
|
1022
1027
|
CodeGenState.markOpaqueScopeVariable("Gui_display");
|
|
1023
|
-
expect(CodeGenState.
|
|
1028
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Gui_display")).toBe(
|
|
1029
|
+
true,
|
|
1030
|
+
);
|
|
1024
1031
|
});
|
|
1025
1032
|
|
|
1026
1033
|
it("reset clears opaqueScopeVariables", () => {
|
|
1027
1034
|
CodeGenState.markOpaqueScopeVariable("Test_opaque");
|
|
1028
|
-
expect(CodeGenState.
|
|
1035
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Test_opaque")).toBe(
|
|
1036
|
+
true,
|
|
1037
|
+
);
|
|
1029
1038
|
|
|
1030
1039
|
CodeGenState.reset();
|
|
1031
1040
|
|
|
1032
|
-
expect(CodeGenState.
|
|
1041
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Test_opaque")).toBe(
|
|
1042
|
+
false,
|
|
1043
|
+
);
|
|
1033
1044
|
});
|
|
1034
1045
|
|
|
1035
1046
|
it("handles multiple opaque scope variables", () => {
|
|
@@ -1037,10 +1048,241 @@ describe("CodeGenState", () => {
|
|
|
1037
1048
|
CodeGenState.markOpaqueScopeVariable("Scope1_display");
|
|
1038
1049
|
CodeGenState.markOpaqueScopeVariable("Scope2_handle");
|
|
1039
1050
|
|
|
1040
|
-
expect(CodeGenState.
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
expect(CodeGenState.
|
|
1051
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Scope1_widget")).toBe(
|
|
1052
|
+
true,
|
|
1053
|
+
);
|
|
1054
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Scope1_display")).toBe(
|
|
1055
|
+
true,
|
|
1056
|
+
);
|
|
1057
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Scope2_handle")).toBe(
|
|
1058
|
+
true,
|
|
1059
|
+
);
|
|
1060
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("Scope1_other")).toBe(
|
|
1061
|
+
false,
|
|
1062
|
+
);
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
// Issue #996: An element of an opaque-handle array is itself a pointer.
|
|
1066
|
+
it("isOpaqueScopeVariableAccess matches array-element access of an opaque array", () => {
|
|
1067
|
+
CodeGenState.markOpaqueScopeVariable("UI_widgets");
|
|
1068
|
+
|
|
1069
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("UI_widgets[i]")).toBe(
|
|
1070
|
+
true,
|
|
1071
|
+
);
|
|
1072
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("UI_widgets[0]")).toBe(
|
|
1073
|
+
true,
|
|
1074
|
+
);
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
it("isOpaqueScopeVariableAccess does not match subscript of a non-opaque array", () => {
|
|
1078
|
+
// Base array name was never marked opaque.
|
|
1079
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("UI_counts[i]")).toBe(
|
|
1080
|
+
false,
|
|
1081
|
+
);
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
it("isOpaqueScopeVariableAccess does not match a different array that shares a prefix", () => {
|
|
1085
|
+
CodeGenState.markOpaqueScopeVariable("UI_widgets");
|
|
1086
|
+
|
|
1087
|
+
// "UI_widgetsExtra" is a distinct variable, not a subscript of UI_widgets.
|
|
1088
|
+
expect(CodeGenState.isOpaqueScopeVariableAccess("UI_widgetsExtra")).toBe(
|
|
1089
|
+
false,
|
|
1090
|
+
);
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
describe("withDeclarationInit()", () => {
|
|
1095
|
+
it("sets inDeclarationInit to true during callback", () => {
|
|
1096
|
+
CodeGenState.inDeclarationInit = false;
|
|
1097
|
+
let valueInside = false;
|
|
1098
|
+
|
|
1099
|
+
CodeGenState.withDeclarationInit(() => {
|
|
1100
|
+
valueInside = CodeGenState.inDeclarationInit;
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
expect(valueInside).toBe(true);
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it("restores prior value after callback", () => {
|
|
1107
|
+
CodeGenState.inDeclarationInit = false;
|
|
1108
|
+
|
|
1109
|
+
CodeGenState.withDeclarationInit(() => {
|
|
1110
|
+
// inside: true
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
expect(CodeGenState.inDeclarationInit).toBe(false);
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
it("restores prior value even when already true", () => {
|
|
1117
|
+
CodeGenState.inDeclarationInit = true;
|
|
1118
|
+
|
|
1119
|
+
CodeGenState.withDeclarationInit(() => {
|
|
1120
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it("returns the callback result", () => {
|
|
1127
|
+
const result = CodeGenState.withDeclarationInit(() => "hello");
|
|
1128
|
+
expect(result).toBe("hello");
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
it("restores prior value on exception", () => {
|
|
1132
|
+
CodeGenState.inDeclarationInit = false;
|
|
1133
|
+
|
|
1134
|
+
expect(() =>
|
|
1135
|
+
CodeGenState.withDeclarationInit(() => {
|
|
1136
|
+
throw new Error("test error");
|
|
1137
|
+
}),
|
|
1138
|
+
).toThrow("test error");
|
|
1139
|
+
|
|
1140
|
+
expect(CodeGenState.inDeclarationInit).toBe(false);
|
|
1141
|
+
});
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
describe("withoutDeclarationInit()", () => {
|
|
1145
|
+
it("sets inDeclarationInit to false during callback", () => {
|
|
1146
|
+
CodeGenState.inDeclarationInit = true;
|
|
1147
|
+
let valueInside = true;
|
|
1148
|
+
|
|
1149
|
+
CodeGenState.withoutDeclarationInit(() => {
|
|
1150
|
+
valueInside = CodeGenState.inDeclarationInit;
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
expect(valueInside).toBe(false);
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it("restores prior value after callback", () => {
|
|
1157
|
+
CodeGenState.inDeclarationInit = true;
|
|
1158
|
+
|
|
1159
|
+
CodeGenState.withoutDeclarationInit(() => {
|
|
1160
|
+
// inside: false
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
it("restores prior value even when already false", () => {
|
|
1167
|
+
CodeGenState.inDeclarationInit = false;
|
|
1168
|
+
|
|
1169
|
+
CodeGenState.withoutDeclarationInit(() => {
|
|
1170
|
+
expect(CodeGenState.inDeclarationInit).toBe(false);
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
expect(CodeGenState.inDeclarationInit).toBe(false);
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
it("returns the callback result", () => {
|
|
1177
|
+
const result = CodeGenState.withoutDeclarationInit(() => 42);
|
|
1178
|
+
expect(result).toBe(42);
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
it("restores prior value on exception", () => {
|
|
1182
|
+
CodeGenState.inDeclarationInit = true;
|
|
1183
|
+
|
|
1184
|
+
expect(() =>
|
|
1185
|
+
CodeGenState.withoutDeclarationInit(() => {
|
|
1186
|
+
throw new Error("test error");
|
|
1187
|
+
}),
|
|
1188
|
+
).toThrow("test error");
|
|
1189
|
+
|
|
1190
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
it("nests correctly with withDeclarationInit", () => {
|
|
1194
|
+
CodeGenState.inDeclarationInit = false;
|
|
1195
|
+
|
|
1196
|
+
CodeGenState.withDeclarationInit(() => {
|
|
1197
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1198
|
+
|
|
1199
|
+
CodeGenState.withoutDeclarationInit(() => {
|
|
1200
|
+
expect(CodeGenState.inDeclarationInit).toBe(false);
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
expect(CodeGenState.inDeclarationInit).toBe(true);
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
expect(CodeGenState.inDeclarationInit).toBe(false);
|
|
1207
|
+
});
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
describe("withoutExpectedType()", () => {
|
|
1211
|
+
it("clears expectedType during callback", () => {
|
|
1212
|
+
CodeGenState.expectedType = "u32";
|
|
1213
|
+
let typeInside: string | null = "notCleared";
|
|
1214
|
+
|
|
1215
|
+
CodeGenState.withoutExpectedType(() => {
|
|
1216
|
+
typeInside = CodeGenState.expectedType;
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
expect(typeInside).toBeNull();
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
it("clears suppressBareEnumResolution during callback", () => {
|
|
1223
|
+
CodeGenState.suppressBareEnumResolution = true;
|
|
1224
|
+
let suppressInside = true;
|
|
1225
|
+
|
|
1226
|
+
CodeGenState.withoutExpectedType(() => {
|
|
1227
|
+
suppressInside = CodeGenState.suppressBareEnumResolution;
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
expect(suppressInside).toBe(false);
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
it("restores expectedType after callback", () => {
|
|
1234
|
+
CodeGenState.expectedType = "i32";
|
|
1235
|
+
|
|
1236
|
+
CodeGenState.withoutExpectedType(() => {
|
|
1237
|
+
// inside: null
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
expect(CodeGenState.expectedType).toBe("i32");
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
it("restores suppressBareEnumResolution after callback", () => {
|
|
1244
|
+
CodeGenState.suppressBareEnumResolution = true;
|
|
1245
|
+
|
|
1246
|
+
CodeGenState.withoutExpectedType(() => {
|
|
1247
|
+
// inside: false
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
expect(CodeGenState.suppressBareEnumResolution).toBe(true);
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
it("returns the callback result", () => {
|
|
1254
|
+
const result = CodeGenState.withoutExpectedType(() => 123);
|
|
1255
|
+
expect(result).toBe(123);
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
it("restores values on exception", () => {
|
|
1259
|
+
CodeGenState.expectedType = "bool";
|
|
1260
|
+
CodeGenState.suppressBareEnumResolution = true;
|
|
1261
|
+
|
|
1262
|
+
expect(() =>
|
|
1263
|
+
CodeGenState.withoutExpectedType(() => {
|
|
1264
|
+
throw new Error("test error");
|
|
1265
|
+
}),
|
|
1266
|
+
).toThrow("test error");
|
|
1267
|
+
|
|
1268
|
+
expect(CodeGenState.expectedType).toBe("bool");
|
|
1269
|
+
expect(CodeGenState.suppressBareEnumResolution).toBe(true);
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
it("handles null expectedType correctly", () => {
|
|
1273
|
+
CodeGenState.expectedType = null;
|
|
1274
|
+
CodeGenState.suppressBareEnumResolution = false;
|
|
1275
|
+
|
|
1276
|
+
let executed = false;
|
|
1277
|
+
CodeGenState.withoutExpectedType(() => {
|
|
1278
|
+
executed = true;
|
|
1279
|
+
expect(CodeGenState.expectedType).toBeNull();
|
|
1280
|
+
expect(CodeGenState.suppressBareEnumResolution).toBe(false);
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
expect(executed).toBe(true);
|
|
1284
|
+
expect(CodeGenState.expectedType).toBeNull();
|
|
1285
|
+
expect(CodeGenState.suppressBareEnumResolution).toBe(false);
|
|
1044
1286
|
});
|
|
1045
1287
|
});
|
|
1046
1288
|
});
|
|
@@ -26,6 +26,13 @@ interface ICachedFileEntry {
|
|
|
26
26
|
structTagAliases?: Array<[string, string]>;
|
|
27
27
|
/** Issue #958: Struct tags that have full definitions (bodies) */
|
|
28
28
|
structTagsWithBodies?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Issue #985: This header could not be preprocessed standalone and fell back
|
|
31
|
+
* to raw content, so its cached symbols are degraded (phantom struct bodies,
|
|
32
|
+
* missing/by-value framework signatures). Persisted so a warm-cache build
|
|
33
|
+
* still triggers the external-declaration recovery pass and re-applies the fix.
|
|
34
|
+
*/
|
|
35
|
+
preprocessFailed?: boolean;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
export default ICachedFileEntry;
|
|
@@ -68,9 +68,32 @@ class LiteralUtils {
|
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Issue #1010: Float literals (0.0, 0.0f, .0, etc.)
|
|
72
|
+
if (ctx.FLOAT_LITERAL()) {
|
|
73
|
+
return LiteralUtils.isFloatZero(text);
|
|
74
|
+
}
|
|
75
|
+
|
|
71
76
|
return false;
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Check if a float literal string represents zero.
|
|
81
|
+
* Issue #1010: Detect float zero for division-by-zero checking.
|
|
82
|
+
*
|
|
83
|
+
* Handles: 0.0, .0, 0., 0.0f, 0.0F, 0.0e0, 0.0E0, etc.
|
|
84
|
+
*
|
|
85
|
+
* @param text - The float literal text
|
|
86
|
+
* @returns true if the float is zero
|
|
87
|
+
*/
|
|
88
|
+
static isFloatZero(text: string): boolean {
|
|
89
|
+
// Remove optional float suffix (f, F)
|
|
90
|
+
const withoutSuffix = text.replace(/[fF]$/, "");
|
|
91
|
+
|
|
92
|
+
// Parse to number and check if zero
|
|
93
|
+
const value = Number.parseFloat(withoutSuffix);
|
|
94
|
+
return value === 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
74
97
|
/**
|
|
75
98
|
* Check if a literal is a floating-point number.
|
|
76
99
|
*
|