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
|
@@ -120,20 +120,16 @@ import CodegenParserUtils from "./utils/CodegenParserUtils";
|
|
|
120
120
|
import IMemberSeparatorDeps from "./types/IMemberSeparatorDeps";
|
|
121
121
|
import IParameterDereferenceDeps from "./types/IParameterDereferenceDeps";
|
|
122
122
|
import ISeparatorContext from "./types/ISeparatorContext";
|
|
123
|
-
// Issue #
|
|
124
|
-
import
|
|
125
|
-
// Issue #269: Transitive modification propagation for const inference
|
|
126
|
-
import TransitiveModificationPropagator from "./helpers/TransitiveModificationPropagator";
|
|
127
|
-
// Issue #566: Child statement/block collection for const inference
|
|
128
|
-
import ChildStatementCollector from "./helpers/ChildStatementCollector";
|
|
129
|
-
// SonarCloud S3776: Assignment target extraction for walkStatementForModifications
|
|
130
|
-
import AssignmentTargetExtractor from "./helpers/AssignmentTargetExtractor";
|
|
123
|
+
// Issue #269: Transitive modification propagation for const inference (used by analyzeModificationsOnly)
|
|
124
|
+
import TransitiveModificationPropagator from "../../logic/analysis/helpers/TransitiveModificationPropagator";
|
|
131
125
|
// Phase 3: Type generation helper for improved testability
|
|
132
126
|
import TypeGenerationHelper from "./helpers/TypeGenerationHelper";
|
|
133
127
|
// Phase 5: Cast validation helper for improved testability
|
|
134
128
|
import CastValidator from "./helpers/CastValidator";
|
|
135
129
|
// Global state for code generation (simplifies debugging, eliminates DI complexity)
|
|
136
130
|
import CodeGenState from "../../state/CodeGenState";
|
|
131
|
+
// Issue #269: Pass-by-value analysis extracted from CodeGenerator
|
|
132
|
+
import PassByValueAnalyzer from "../../logic/analysis/PassByValueAnalyzer";
|
|
137
133
|
// Unified parameter generation (Phase 1)
|
|
138
134
|
import ParameterInputAdapter from "./helpers/ParameterInputAdapter";
|
|
139
135
|
import ParameterSignatureBuilder from "./helpers/ParameterSignatureBuilder";
|
|
@@ -220,41 +216,6 @@ const DEFAULT_TARGET: TargetCapabilities = {
|
|
|
220
216
|
hasBasepri: false,
|
|
221
217
|
};
|
|
222
218
|
|
|
223
|
-
/**
|
|
224
|
-
* ADR-044: Assignment context for overflow behavior tracking
|
|
225
|
-
*/
|
|
226
|
-
interface AssignmentContext {
|
|
227
|
-
targetName: string | null;
|
|
228
|
-
targetType: string | null;
|
|
229
|
-
overflowBehavior: TOverflowBehavior;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Context for tracking current scope during code generation
|
|
234
|
-
*/
|
|
235
|
-
interface GeneratorContext {
|
|
236
|
-
currentScope: string | null; // ADR-016: renamed from currentNamespace
|
|
237
|
-
currentFunctionName: string | null; // Issue #269: track current function for pass-by-value lookup
|
|
238
|
-
currentFunctionReturnType: string | null; // Issue #477: track return type for enum inference
|
|
239
|
-
indentLevel: number;
|
|
240
|
-
scopeMembers: Map<string, Set<string>>; // scope -> member names (ADR-016)
|
|
241
|
-
currentParameters: Map<string, TParameterInfo>; // ADR-006: track params for pointer semantics
|
|
242
|
-
// Issue #558: modifiedParameters removed - now uses analysis-phase results from CodeGenState.modifiedParameters
|
|
243
|
-
localArrays: Set<string>; // ADR-006: track local array variables (no & needed)
|
|
244
|
-
localVariables: Set<string>; // ADR-016: track local variables (allowed as bare identifiers)
|
|
245
|
-
floatBitShadows: Set<string>; // Track declared shadow variables for float bit indexing
|
|
246
|
-
floatShadowCurrent: Set<string>; // Track which shadows have current value (skip redundant memcpy reads)
|
|
247
|
-
inFunctionBody: boolean; // ADR-016: track if we're inside a function body
|
|
248
|
-
typeRegistry: Map<string, TTypeInfo>; // Track variable types for bit access and .length
|
|
249
|
-
expectedType: string | null; // For inferred struct initializers
|
|
250
|
-
mainArgsName: string | null; // Track the args parameter name for main() translation
|
|
251
|
-
assignmentContext: AssignmentContext; // ADR-044: Track current assignment for overflow
|
|
252
|
-
lastArrayInitCount: number; // ADR-035: Track element count for size inference
|
|
253
|
-
lastArrayFillValue: string | undefined; // ADR-035: Track fill-all value
|
|
254
|
-
lengthCache: Map<string, string> | null; // Cache: variable name -> temp variable name for strlen optimization
|
|
255
|
-
targetCapabilities: TargetCapabilities; // ADR-049: Target platform for atomic code generation
|
|
256
|
-
}
|
|
257
|
-
|
|
258
219
|
/**
|
|
259
220
|
* Code Generator - Transpiles C-Next to C
|
|
260
221
|
*
|
|
@@ -269,42 +230,6 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
269
230
|
["f64", "0.0"],
|
|
270
231
|
]);
|
|
271
232
|
|
|
272
|
-
private context: GeneratorContext =
|
|
273
|
-
CodeGenerator.createDefaultContext(DEFAULT_TARGET);
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Create a fresh GeneratorContext with default values.
|
|
277
|
-
*/
|
|
278
|
-
private static createDefaultContext(
|
|
279
|
-
targetCapabilities: TargetCapabilities,
|
|
280
|
-
): GeneratorContext {
|
|
281
|
-
return {
|
|
282
|
-
currentScope: null,
|
|
283
|
-
currentFunctionName: null,
|
|
284
|
-
currentFunctionReturnType: null,
|
|
285
|
-
indentLevel: 0,
|
|
286
|
-
scopeMembers: new Map(),
|
|
287
|
-
currentParameters: new Map(),
|
|
288
|
-
localArrays: new Set(),
|
|
289
|
-
localVariables: new Set(),
|
|
290
|
-
floatBitShadows: new Set(),
|
|
291
|
-
floatShadowCurrent: new Set(),
|
|
292
|
-
inFunctionBody: false,
|
|
293
|
-
typeRegistry: new Map(),
|
|
294
|
-
expectedType: null,
|
|
295
|
-
mainArgsName: null,
|
|
296
|
-
assignmentContext: {
|
|
297
|
-
targetName: null,
|
|
298
|
-
targetType: null,
|
|
299
|
-
overflowBehavior: "clamp",
|
|
300
|
-
},
|
|
301
|
-
lastArrayInitCount: 0,
|
|
302
|
-
lastArrayFillValue: undefined,
|
|
303
|
-
lengthCache: null,
|
|
304
|
-
targetCapabilities,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
233
|
/** Token stream for comment extraction (ADR-043) */
|
|
309
234
|
private tokenStream: CommonTokenStream | null = null;
|
|
310
235
|
|
|
@@ -458,7 +383,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
458
383
|
return {
|
|
459
384
|
symbolTable: CodeGenState.symbolTable,
|
|
460
385
|
symbols: CodeGenState.symbols,
|
|
461
|
-
typeRegistry: CodeGenState.
|
|
386
|
+
typeRegistry: CodeGenState.getTypeRegistryView(),
|
|
462
387
|
functionSignatures: CodeGenState.functionSignatures,
|
|
463
388
|
knownFunctions: CodeGenState.knownFunctions,
|
|
464
389
|
knownStructs: CodeGenState.symbols?.knownStructs ?? new Set(),
|
|
@@ -485,7 +410,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
485
410
|
expectedType: CodeGenState.expectedType,
|
|
486
411
|
selfIncludeAdded: CodeGenState.selfIncludeAdded, // Issue #369
|
|
487
412
|
// Issue #644: Postfix expression state
|
|
488
|
-
scopeMembers: CodeGenState.
|
|
413
|
+
scopeMembers: CodeGenState.getAllScopeMembers(),
|
|
489
414
|
mainArgsName: CodeGenState.mainArgsName,
|
|
490
415
|
floatBitShadows: CodeGenState.floatBitShadows,
|
|
491
416
|
floatShadowCurrent: CodeGenState.floatShadowCurrent,
|
|
@@ -522,7 +447,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
522
447
|
|
|
523
448
|
// Type registration effects
|
|
524
449
|
case "register-type":
|
|
525
|
-
CodeGenState.
|
|
450
|
+
CodeGenState.setVariableTypeInfo(effect.name, effect.info);
|
|
526
451
|
break;
|
|
527
452
|
case "register-local":
|
|
528
453
|
CodeGenState.localVariables.add(effect.name);
|
|
@@ -628,7 +553,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
628
553
|
resolveIdentifier(identifier: string): string {
|
|
629
554
|
// Check current scope first (inner scope shadows outer)
|
|
630
555
|
if (CodeGenState.currentScope) {
|
|
631
|
-
const members = CodeGenState.
|
|
556
|
+
const members = CodeGenState.getScopeMembers(CodeGenState.currentScope);
|
|
632
557
|
if (members?.has(identifier)) {
|
|
633
558
|
return `${CodeGenState.currentScope}_${identifier}`;
|
|
634
559
|
}
|
|
@@ -798,7 +723,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
798
723
|
|
|
799
724
|
// Check if it's a simple variable of string type
|
|
800
725
|
if (/^[a-zA-Z_]\w*$/.exec(text)) {
|
|
801
|
-
const typeInfo = CodeGenState.
|
|
726
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(text);
|
|
802
727
|
if (typeInfo?.isString) {
|
|
803
728
|
return true;
|
|
804
729
|
}
|
|
@@ -830,7 +755,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
830
755
|
}
|
|
831
756
|
|
|
832
757
|
const arrayName = arrayAccessMatch[1];
|
|
833
|
-
const typeInfo = CodeGenState.
|
|
758
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(arrayName);
|
|
834
759
|
if (!typeInfo) {
|
|
835
760
|
return false;
|
|
836
761
|
}
|
|
@@ -1148,10 +1073,34 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1148
1073
|
);
|
|
1149
1074
|
}
|
|
1150
1075
|
|
|
1076
|
+
// Issue #779: Resolve bare scope member identifiers before postfix chain processing
|
|
1077
|
+
// This ensures scope members get their prefix even with array/member access.
|
|
1078
|
+
// Skip parameters - they don't need scope resolution and shouldn't be dereferenced
|
|
1079
|
+
// when used with array indexing (buf[idx] is valid C for pointer params).
|
|
1080
|
+
// Also skip known registers - they should be handled by the postfix chain builder
|
|
1081
|
+
// to enable proper register validation (requiring global. when shadowed).
|
|
1082
|
+
let resolvedIdentifier = identifier ?? "";
|
|
1083
|
+
if (!hasGlobal && !hasThis && identifier) {
|
|
1084
|
+
const isParameter = CodeGenState.currentParameters.has(identifier);
|
|
1085
|
+
const isLocalVariable = CodeGenState.localVariables.has(identifier);
|
|
1086
|
+
const isKnownRegister =
|
|
1087
|
+
CodeGenState.symbols?.knownRegisters.has(identifier);
|
|
1088
|
+
if (!isParameter && !isLocalVariable && !isKnownRegister) {
|
|
1089
|
+
const resolved = TypeValidator.resolveBareIdentifier(
|
|
1090
|
+
identifier,
|
|
1091
|
+
false, // not local
|
|
1092
|
+
(name: string) => this.isKnownStruct(name),
|
|
1093
|
+
);
|
|
1094
|
+
if (resolved !== null) {
|
|
1095
|
+
resolvedIdentifier = resolved;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1151
1100
|
// SonarCloud S3776: Use BaseIdentifierBuilder for base identifier
|
|
1152
1101
|
const safeIdentifier = identifier ?? "";
|
|
1153
1102
|
const { result: baseResult, firstId } = BaseIdentifierBuilder.build(
|
|
1154
|
-
safeIdentifier,
|
|
1103
|
+
hasGlobal || hasThis ? safeIdentifier : resolvedIdentifier,
|
|
1155
1104
|
hasGlobal,
|
|
1156
1105
|
hasThis,
|
|
1157
1106
|
CodeGenState.currentScope,
|
|
@@ -1415,7 +1364,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1415
1364
|
// Variable - check type registry
|
|
1416
1365
|
const identifierRegex = /^[a-zA-Z_]\w*$/;
|
|
1417
1366
|
if (identifierRegex.test(exprCode)) {
|
|
1418
|
-
const typeInfo = CodeGenState.
|
|
1367
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(exprCode);
|
|
1419
1368
|
if (typeInfo?.isString && typeInfo.stringCapacity !== undefined) {
|
|
1420
1369
|
return typeInfo.stringCapacity;
|
|
1421
1370
|
}
|
|
@@ -1546,36 +1495,24 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1546
1495
|
return `\ntypedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});\n`;
|
|
1547
1496
|
}
|
|
1548
1497
|
|
|
1549
|
-
/**
|
|
1550
|
-
* Issue #268: Store unmodified parameters for a function.
|
|
1551
|
-
* Maps function name -> Set of parameter names that were NOT modified.
|
|
1552
|
-
* Used by Pipeline to update symbol info before header generation.
|
|
1553
|
-
*/
|
|
1554
|
-
private readonly functionUnmodifiedParams: Map<string, Set<string>> =
|
|
1555
|
-
new Map();
|
|
1556
|
-
|
|
1557
1498
|
/**
|
|
1558
1499
|
* Issue #268: Get unmodified parameters info for all functions.
|
|
1559
1500
|
* Returns map of function name -> Set of unmodified parameter names.
|
|
1501
|
+
* Computed on-demand from functionSignatures and modifiedParameters.
|
|
1560
1502
|
*/
|
|
1561
1503
|
getFunctionUnmodifiedParams(): ReadonlyMap<string, Set<string>> {
|
|
1562
|
-
return
|
|
1504
|
+
return CodeGenState.getUnmodifiedParameters();
|
|
1563
1505
|
}
|
|
1564
1506
|
|
|
1565
1507
|
/**
|
|
1566
1508
|
* Issue #268: Update symbol parameters with auto-const info.
|
|
1567
|
-
*
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
if (!modifiedSet?.has(paramName)) {
|
|
1575
|
-
unmodifiedParams.add(paramName);
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
this.functionUnmodifiedParams.set(functionName, unmodifiedParams);
|
|
1509
|
+
* Now a no-op - unmodified params are computed on-demand from CodeGenState.
|
|
1510
|
+
* Kept for IOrchestrator interface compatibility.
|
|
1511
|
+
*/
|
|
1512
|
+
updateFunctionParamsAutoConst(_functionName: string): void {
|
|
1513
|
+
// No-op: Unmodified parameters are now computed on-demand from
|
|
1514
|
+
// CodeGenState.functionSignatures and CodeGenState.modifiedParameters
|
|
1515
|
+
// via CodeGenState.getUnmodifiedParameters().
|
|
1579
1516
|
}
|
|
1580
1517
|
|
|
1581
1518
|
/**
|
|
@@ -1663,7 +1600,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1663
1600
|
const injectedFuncs = new Set(crossFileModifications?.keys() ?? []);
|
|
1664
1601
|
|
|
1665
1602
|
// Run modification analysis on the tree (adds to what was injected)
|
|
1666
|
-
|
|
1603
|
+
PassByValueAnalyzer.collectFunctionParametersAndModifications(tree);
|
|
1667
1604
|
|
|
1668
1605
|
// Issue #565: Run transitive propagation with full context
|
|
1669
1606
|
TransitiveModificationPropagator.propagate(
|
|
@@ -1773,23 +1710,16 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1773
1710
|
* Returns true if the callee modifies that parameter (should not have const).
|
|
1774
1711
|
*/
|
|
1775
1712
|
isCalleeParameterModified(funcName: string, paramIndex: number): boolean {
|
|
1776
|
-
const unmodifiedParams = this.functionUnmodifiedParams.get(funcName);
|
|
1777
|
-
if (!unmodifiedParams) {
|
|
1778
|
-
// Callee not yet processed - conservatively return false (assume unmodified)
|
|
1779
|
-
// This means we won't mark our param as modified, which may cause a C compiler error
|
|
1780
|
-
// if the callee actually modifies the param. The C compiler will catch this.
|
|
1781
|
-
return false;
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
1713
|
// Get the parameter name at the given index from the function signature
|
|
1785
1714
|
const sig = CodeGenState.functionSignatures.get(funcName);
|
|
1786
1715
|
if (!sig || paramIndex >= sig.parameters.length) {
|
|
1716
|
+
// Callee not yet processed - conservatively return false (assume unmodified)
|
|
1787
1717
|
return false;
|
|
1788
1718
|
}
|
|
1789
1719
|
|
|
1790
1720
|
const paramName = sig.parameters[paramIndex].name;
|
|
1791
|
-
//
|
|
1792
|
-
return
|
|
1721
|
+
// Check directly if the parameter is in the modified set
|
|
1722
|
+
return CodeGenState.isParameterModified(funcName, paramName);
|
|
1793
1723
|
}
|
|
1794
1724
|
|
|
1795
1725
|
/**
|
|
@@ -2017,7 +1947,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2017
1947
|
isKnownPrimitive: (typeName: string) => this._isKnownPrimitive(typeName),
|
|
2018
1948
|
knownEnums: CodeGenState.symbols!.knownEnums,
|
|
2019
1949
|
isParameterPassByValue: (funcName: string, paramName: string) =>
|
|
2020
|
-
|
|
1950
|
+
PassByValueAnalyzer.isParameterPassByValueByName(funcName, paramName),
|
|
2021
1951
|
currentFunctionName: CodeGenState.currentFunctionName,
|
|
2022
1952
|
maybeDereference: (id: string) => CppModeHelper.maybeDereference(id),
|
|
2023
1953
|
};
|
|
@@ -2047,7 +1977,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2047
1977
|
}
|
|
2048
1978
|
|
|
2049
1979
|
/**
|
|
2050
|
-
* Validate register access from inside a scope requires global. prefix
|
|
1980
|
+
* Validate register access from inside a scope requires global. prefix.
|
|
1981
|
+
*
|
|
1982
|
+
* Issue #779: Use ambiguity-aware validation - only require global. when
|
|
1983
|
+
* the register name is ACTUALLY shadowed by a local or scope member.
|
|
1984
|
+
*
|
|
1985
|
+
* Exceptions (no global. required):
|
|
1986
|
+
* 1. Scoped registers defined within the current scope
|
|
1987
|
+
* 2. Unambiguous access - no local/scope member with the same name
|
|
2051
1988
|
*/
|
|
2052
1989
|
private _validateRegisterAccess(
|
|
2053
1990
|
registerName: string,
|
|
@@ -2056,6 +1993,27 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2056
1993
|
): void {
|
|
2057
1994
|
// Only validate when inside a scope and accessing without global. prefix
|
|
2058
1995
|
if (CodeGenState.currentScope && !hasGlobal) {
|
|
1996
|
+
// Check if this is a scoped register (defined within the current scope)
|
|
1997
|
+
// The registerName may already be the fully qualified name (e.g., "GPIO_PORTA")
|
|
1998
|
+
// if accessed as PORTA from inside scope GPIO
|
|
1999
|
+
const scopePrefix = `${CodeGenState.currentScope}_`;
|
|
2000
|
+
if (registerName.startsWith(scopePrefix)) {
|
|
2001
|
+
// This is a scoped register - allow bare access
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// Issue #779: Ambiguity-aware validation
|
|
2006
|
+
// Only require global. if the register name is shadowed by:
|
|
2007
|
+
// 1. A local variable in the current function
|
|
2008
|
+
// 2. A member of the current scope
|
|
2009
|
+
const isShadowedByLocal = CodeGenState.localVariables.has(registerName);
|
|
2010
|
+
const isShadowedByScope = CodeGenState.isCurrentScopeMember(registerName);
|
|
2011
|
+
|
|
2012
|
+
if (!isShadowedByLocal && !isShadowedByScope) {
|
|
2013
|
+
// Unambiguous - allow bare access
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2059
2017
|
throw new Error(
|
|
2060
2018
|
`Error: Use 'global.${registerName}.${memberName}' to access register '${registerName}' ` +
|
|
2061
2019
|
`from inside scope '${CodeGenState.currentScope}'`,
|
|
@@ -2190,30 +2148,11 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2190
2148
|
* Reset all generator state for a fresh generation pass.
|
|
2191
2149
|
*/
|
|
2192
2150
|
private resetGeneratorState(targetCapabilities: TargetCapabilities): void {
|
|
2193
|
-
// Reset global state
|
|
2151
|
+
// Reset global state (CodeGenState.reset() handles all field initialization)
|
|
2194
2152
|
CodeGenState.reset(targetCapabilities);
|
|
2195
2153
|
|
|
2196
2154
|
// Set generator reference for handlers to use
|
|
2197
2155
|
CodeGenState.generator = this;
|
|
2198
|
-
|
|
2199
|
-
// Reset local context (will gradually migrate to CodeGenState)
|
|
2200
|
-
this.context = CodeGenerator.createDefaultContext(targetCapabilities);
|
|
2201
|
-
|
|
2202
|
-
CodeGenState.knownFunctions = new Set();
|
|
2203
|
-
CodeGenState.functionSignatures = new Map();
|
|
2204
|
-
CodeGenState.callbackTypes = new Map();
|
|
2205
|
-
CodeGenState.callbackFieldTypes = new Map();
|
|
2206
|
-
CodeGenState.usedClampOps = new Set();
|
|
2207
|
-
CodeGenState.usedSafeDivOps = new Set();
|
|
2208
|
-
CodeGenState.needsStdint = false;
|
|
2209
|
-
CodeGenState.needsStdbool = false;
|
|
2210
|
-
CodeGenState.needsString = false;
|
|
2211
|
-
CodeGenState.needsFloatStaticAssert = false;
|
|
2212
|
-
CodeGenState.needsISR = false;
|
|
2213
|
-
CodeGenState.needsCMSIS = false;
|
|
2214
|
-
CodeGenState.needsLimits = false;
|
|
2215
|
-
CodeGenState.needsIrqWrappers = false;
|
|
2216
|
-
CodeGenState.selfIncludeAdded = false;
|
|
2217
2156
|
}
|
|
2218
2157
|
|
|
2219
2158
|
/**
|
|
@@ -2224,7 +2163,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2224
2163
|
|
|
2225
2164
|
// Copy symbol data to CodeGenState.scopeMembers
|
|
2226
2165
|
for (const [scopeName, members] of symbols.scopeMembers) {
|
|
2227
|
-
CodeGenState.
|
|
2166
|
+
CodeGenState.setScopeMembers(scopeName, new Set(members));
|
|
2228
2167
|
}
|
|
2229
2168
|
|
|
2230
2169
|
// Issue #461: Initialize constValues from symbol table
|
|
@@ -2253,7 +2192,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2253
2192
|
private initializeHelperObjects(tree: Parser.ProgramContext): void {
|
|
2254
2193
|
// Collect function/callback information
|
|
2255
2194
|
this.collectFunctionsAndCallbacks(tree);
|
|
2256
|
-
|
|
2195
|
+
PassByValueAnalyzer.analyze(tree);
|
|
2257
2196
|
}
|
|
2258
2197
|
|
|
2259
2198
|
/**
|
|
@@ -2412,13 +2351,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2412
2351
|
}
|
|
2413
2352
|
|
|
2414
2353
|
if (CodeGenState.needsIrqWrappers) {
|
|
2415
|
-
output.push(
|
|
2416
|
-
"// ADR-050: IRQ wrappers to avoid macro collisions with platform headers",
|
|
2417
|
-
"static inline void __cnx_disable_irq(void) { __disable_irq(); }",
|
|
2418
|
-
"static inline uint32_t __cnx_get_PRIMASK(void) { return __get_PRIMASK(); }",
|
|
2419
|
-
"static inline void __cnx_set_PRIMASK(uint32_t mask) { __set_PRIMASK(mask); }",
|
|
2420
|
-
"",
|
|
2421
|
-
);
|
|
2354
|
+
output.push(...this.generateIrqWrappers());
|
|
2422
2355
|
}
|
|
2423
2356
|
|
|
2424
2357
|
if (CodeGenState.needsISR) {
|
|
@@ -2686,581 +2619,15 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2686
2619
|
// Issue #63: validateBitmapFieldLiteral moved to TypeValidator
|
|
2687
2620
|
// Issue #60: evaluateConstantExpression method removed - now in SymbolCollector
|
|
2688
2621
|
|
|
2689
|
-
//
|
|
2690
|
-
// Issue #269: Pass-by-value analysis for small unmodified parameters
|
|
2691
|
-
// ========================================================================
|
|
2692
|
-
|
|
2693
|
-
/**
|
|
2694
|
-
* Analyze all functions to determine which parameters should pass by value.
|
|
2695
|
-
* This runs before code generation and populates passByValueParams.
|
|
2696
|
-
* SonarCloud S3776: Refactored to use helper methods.
|
|
2697
|
-
*/
|
|
2698
|
-
private analyzePassByValue(tree: Parser.ProgramContext): void {
|
|
2699
|
-
// Reset analysis state
|
|
2700
|
-
CodeGenState.modifiedParameters.clear();
|
|
2701
|
-
CodeGenState.passByValueParams.clear();
|
|
2702
|
-
CodeGenState.functionCallGraph.clear();
|
|
2703
|
-
CodeGenState.functionParamLists.clear();
|
|
2704
|
-
|
|
2705
|
-
// Phase 1: Collect function parameter lists and direct modifications
|
|
2706
|
-
this.collectFunctionParametersAndModifications(tree);
|
|
2707
|
-
|
|
2708
|
-
// Issue #558: Inject cross-file data before transitive propagation
|
|
2709
|
-
this.injectCrossFileModifications();
|
|
2710
|
-
this.injectCrossFileParamLists();
|
|
2711
|
-
|
|
2712
|
-
// Phase 2: Fixed-point iteration for transitive modifications
|
|
2713
|
-
TransitiveModificationPropagator.propagate(
|
|
2714
|
-
CodeGenState.functionCallGraph,
|
|
2715
|
-
CodeGenState.functionParamLists,
|
|
2716
|
-
CodeGenState.modifiedParameters,
|
|
2717
|
-
);
|
|
2718
|
-
|
|
2719
|
-
// Phase 3: Determine which parameters can pass by value
|
|
2720
|
-
this.computePassByValueParams();
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2723
|
-
/**
|
|
2724
|
-
* Inject cross-file modification data into modifiedParameters.
|
|
2725
|
-
* SonarCloud S3776: Extracted from analyzePassByValue().
|
|
2726
|
-
*/
|
|
2727
|
-
private injectCrossFileModifications(): void {
|
|
2728
|
-
if (!CodeGenState.pendingCrossFileModifications) return;
|
|
2729
|
-
|
|
2730
|
-
for (const [
|
|
2731
|
-
funcName,
|
|
2732
|
-
params,
|
|
2733
|
-
] of CodeGenState.pendingCrossFileModifications) {
|
|
2734
|
-
const existing = CodeGenState.modifiedParameters.get(funcName);
|
|
2735
|
-
if (existing) {
|
|
2736
|
-
for (const param of params) {
|
|
2737
|
-
existing.add(param);
|
|
2738
|
-
}
|
|
2739
|
-
} else {
|
|
2740
|
-
CodeGenState.modifiedParameters.set(funcName, new Set(params));
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
CodeGenState.pendingCrossFileModifications = null; // Clear after use
|
|
2744
|
-
}
|
|
2745
|
-
|
|
2746
|
-
/**
|
|
2747
|
-
* Inject cross-file parameter lists into functionParamLists.
|
|
2748
|
-
* SonarCloud S3776: Extracted from analyzePassByValue().
|
|
2749
|
-
*/
|
|
2750
|
-
private injectCrossFileParamLists(): void {
|
|
2751
|
-
if (!CodeGenState.pendingCrossFileParamLists) return;
|
|
2752
|
-
|
|
2753
|
-
for (const [funcName, params] of CodeGenState.pendingCrossFileParamLists) {
|
|
2754
|
-
if (!CodeGenState.functionParamLists.has(funcName)) {
|
|
2755
|
-
CodeGenState.functionParamLists.set(funcName, [...params]);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
CodeGenState.pendingCrossFileParamLists = null; // Clear after use
|
|
2759
|
-
}
|
|
2760
|
-
|
|
2761
|
-
/**
|
|
2762
|
-
* Phase 1: Walk all functions to collect:
|
|
2763
|
-
* - Parameter lists (for call graph resolution)
|
|
2764
|
-
* - Direct modifications (param <- value)
|
|
2765
|
-
* - Function calls where params are passed as arguments
|
|
2766
|
-
*/
|
|
2767
|
-
private collectFunctionParametersAndModifications(
|
|
2768
|
-
tree: Parser.ProgramContext,
|
|
2769
|
-
): void {
|
|
2770
|
-
for (const decl of tree.declaration()) {
|
|
2771
|
-
// Handle scope-level functions
|
|
2772
|
-
if (decl.scopeDeclaration()) {
|
|
2773
|
-
const scopeDecl = decl.scopeDeclaration()!;
|
|
2774
|
-
const scopeName = scopeDecl.IDENTIFIER().getText();
|
|
2775
|
-
|
|
2776
|
-
for (const member of scopeDecl.scopeMember()) {
|
|
2777
|
-
if (member.functionDeclaration()) {
|
|
2778
|
-
const funcDecl = member.functionDeclaration()!;
|
|
2779
|
-
const funcName = funcDecl.IDENTIFIER().getText();
|
|
2780
|
-
const fullName = `${scopeName}_${funcName}`;
|
|
2781
|
-
this.analyzeFunctionForModifications(fullName, funcDecl);
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
|
|
2786
|
-
// Handle top-level functions
|
|
2787
|
-
if (decl.functionDeclaration()) {
|
|
2788
|
-
const funcDecl = decl.functionDeclaration()!;
|
|
2789
|
-
const name = funcDecl.IDENTIFIER().getText();
|
|
2790
|
-
this.analyzeFunctionForModifications(name, funcDecl);
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
|
|
2795
|
-
/**
|
|
2796
|
-
* Analyze a single function for parameter modifications and call graph edges.
|
|
2797
|
-
*/
|
|
2798
|
-
private analyzeFunctionForModifications(
|
|
2799
|
-
funcName: string,
|
|
2800
|
-
funcDecl: Parser.FunctionDeclarationContext,
|
|
2801
|
-
): void {
|
|
2802
|
-
// Collect parameter names
|
|
2803
|
-
const paramNames: string[] = [];
|
|
2804
|
-
const paramList = funcDecl.parameterList();
|
|
2805
|
-
if (paramList) {
|
|
2806
|
-
for (const param of paramList.parameter()) {
|
|
2807
|
-
paramNames.push(param.IDENTIFIER().getText());
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
|
-
CodeGenState.functionParamLists.set(funcName, paramNames);
|
|
2811
|
-
|
|
2812
|
-
// Initialize modified set
|
|
2813
|
-
CodeGenState.modifiedParameters.set(funcName, new Set());
|
|
2814
|
-
// Issue #579: Initialize subscript access tracking
|
|
2815
|
-
CodeGenState.subscriptAccessedParameters.set(funcName, new Set());
|
|
2816
|
-
CodeGenState.functionCallGraph.set(funcName, []);
|
|
2817
|
-
|
|
2818
|
-
// Walk the function body to find modifications and calls
|
|
2819
|
-
const block = funcDecl.block();
|
|
2820
|
-
if (block) {
|
|
2821
|
-
this.walkBlockForModifications(funcName, paramNames, block);
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
|
|
2825
|
-
/**
|
|
2826
|
-
* Walk a block to find parameter modifications and function calls.
|
|
2827
|
-
*/
|
|
2828
|
-
private walkBlockForModifications(
|
|
2829
|
-
funcName: string,
|
|
2830
|
-
paramNames: string[],
|
|
2831
|
-
block: Parser.BlockContext,
|
|
2832
|
-
): void {
|
|
2833
|
-
const paramSet = new Set(paramNames);
|
|
2834
|
-
|
|
2835
|
-
for (const stmt of block.statement()) {
|
|
2836
|
-
this.walkStatementForModifications(funcName, paramSet, stmt);
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
/**
|
|
2841
|
-
* Walk a statement recursively looking for modifications and calls.
|
|
2842
|
-
* Issue #566: Refactored to use helper methods for expression and child collection.
|
|
2843
|
-
*/
|
|
2844
|
-
private walkStatementForModifications(
|
|
2845
|
-
funcName: string,
|
|
2846
|
-
paramSet: Set<string>,
|
|
2847
|
-
stmt: Parser.StatementContext,
|
|
2848
|
-
): void {
|
|
2849
|
-
// 1. Check for parameter modifications via assignment targets
|
|
2850
|
-
if (stmt.assignmentStatement()) {
|
|
2851
|
-
this.trackAssignmentModifications(funcName, paramSet, stmt);
|
|
2852
|
-
}
|
|
2853
|
-
|
|
2854
|
-
// 2. Walk all expressions in this statement for function calls and subscript access
|
|
2855
|
-
for (const expr of StatementExpressionCollector.collectAll(stmt)) {
|
|
2856
|
-
this.walkExpressionForCalls(funcName, paramSet, expr);
|
|
2857
|
-
// Issue #579: Also track subscript read access on parameters
|
|
2858
|
-
this.walkExpressionForSubscriptAccess(funcName, paramSet, expr);
|
|
2859
|
-
}
|
|
2860
|
-
|
|
2861
|
-
// 3. Recurse into child statements and blocks
|
|
2862
|
-
const { statements, blocks } = ChildStatementCollector.collectAll(stmt);
|
|
2863
|
-
for (const childStmt of statements) {
|
|
2864
|
-
this.walkStatementForModifications(funcName, paramSet, childStmt);
|
|
2865
|
-
}
|
|
2866
|
-
for (const block of blocks) {
|
|
2867
|
-
this.walkBlockForModifications(funcName, [...paramSet], block);
|
|
2868
|
-
}
|
|
2869
|
-
}
|
|
2870
|
-
|
|
2871
|
-
/**
|
|
2872
|
-
* Track assignment modifications for parameter const inference.
|
|
2873
|
-
* SonarCloud S3776: Extracted from walkStatementForModifications().
|
|
2874
|
-
*/
|
|
2875
|
-
private trackAssignmentModifications(
|
|
2876
|
-
funcName: string,
|
|
2877
|
-
paramSet: Set<string>,
|
|
2878
|
-
stmt: Parser.StatementContext,
|
|
2879
|
-
): void {
|
|
2880
|
-
const assign = stmt.assignmentStatement()!;
|
|
2881
|
-
const target = assign.assignmentTarget();
|
|
2882
|
-
|
|
2883
|
-
const { baseIdentifier, hasSingleIndexSubscript } =
|
|
2884
|
-
AssignmentTargetExtractor.extract(target);
|
|
2885
|
-
|
|
2886
|
-
// Issue #579: Track subscript access on parameters (for write path)
|
|
2887
|
-
if (
|
|
2888
|
-
hasSingleIndexSubscript &&
|
|
2889
|
-
baseIdentifier &&
|
|
2890
|
-
paramSet.has(baseIdentifier)
|
|
2891
|
-
) {
|
|
2892
|
-
CodeGenState.subscriptAccessedParameters
|
|
2893
|
-
.get(funcName)!
|
|
2894
|
-
.add(baseIdentifier);
|
|
2895
|
-
}
|
|
2896
|
-
|
|
2897
|
-
// Track as modified parameter
|
|
2898
|
-
if (baseIdentifier && paramSet.has(baseIdentifier)) {
|
|
2899
|
-
CodeGenState.modifiedParameters.get(funcName)!.add(baseIdentifier);
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
|
|
2903
|
-
/**
|
|
2904
|
-
* Walk an expression tree to find function calls where parameters are passed.
|
|
2905
|
-
* Uses recursive descent through the expression hierarchy.
|
|
2906
|
-
*/
|
|
2907
|
-
private walkExpressionForCalls(
|
|
2908
|
-
funcName: string,
|
|
2909
|
-
paramSet: Set<string>,
|
|
2910
|
-
expr: Parser.ExpressionContext,
|
|
2911
|
-
): void {
|
|
2912
|
-
// Expression -> TernaryExpression -> OrExpression -> ... -> PostfixExpression
|
|
2913
|
-
const ternary = expr.ternaryExpression();
|
|
2914
|
-
if (ternary) {
|
|
2915
|
-
// Walk all orExpression children
|
|
2916
|
-
for (const orExpr of ternary.orExpression()) {
|
|
2917
|
-
this.walkOrExpressionForCalls(funcName, paramSet, orExpr);
|
|
2918
|
-
}
|
|
2919
|
-
}
|
|
2920
|
-
}
|
|
2921
|
-
|
|
2922
|
-
/**
|
|
2923
|
-
* Issue #579: Walk an expression tree to find subscript access on parameters.
|
|
2924
|
-
* This tracks read access like `buf[i]` where buf is a parameter.
|
|
2925
|
-
* Parameters with subscript access must become pointers.
|
|
2926
|
-
*/
|
|
2927
|
-
private walkExpressionForSubscriptAccess(
|
|
2928
|
-
funcName: string,
|
|
2929
|
-
paramSet: Set<string>,
|
|
2930
|
-
expr: Parser.ExpressionContext,
|
|
2931
|
-
): void {
|
|
2932
|
-
const ternary = expr.ternaryExpression();
|
|
2933
|
-
if (ternary) {
|
|
2934
|
-
for (const orExpr of ternary.orExpression()) {
|
|
2935
|
-
this.walkOrExpression(orExpr, (unaryExpr) => {
|
|
2936
|
-
this.handleSubscriptAccess(funcName, paramSet, unaryExpr);
|
|
2937
|
-
});
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
/**
|
|
2943
|
-
* Issue #579: Handle subscript access on a unary expression.
|
|
2944
|
-
* Only tracks single-index subscript access (which could be array access).
|
|
2945
|
-
* Two-index subscript (e.g., value[start, width]) is always bit extraction,
|
|
2946
|
-
* so it doesn't require the parameter to become a pointer.
|
|
2947
|
-
*/
|
|
2948
|
-
private handleSubscriptAccess(
|
|
2949
|
-
funcName: string,
|
|
2950
|
-
paramSet: Set<string>,
|
|
2951
|
-
unaryExpr: Parser.UnaryExpressionContext,
|
|
2952
|
-
): void {
|
|
2953
|
-
const postfixExpr = unaryExpr.postfixExpression();
|
|
2954
|
-
if (!postfixExpr) return;
|
|
2955
|
-
|
|
2956
|
-
const primary = postfixExpr.primaryExpression();
|
|
2957
|
-
const ops = postfixExpr.postfixOp();
|
|
2958
|
-
|
|
2959
|
-
// Check if primary is a parameter and there's subscript access
|
|
2960
|
-
const primaryId = primary.IDENTIFIER()?.getText();
|
|
2961
|
-
if (!primaryId || !paramSet.has(primaryId)) {
|
|
2962
|
-
return;
|
|
2963
|
-
}
|
|
2964
|
-
|
|
2965
|
-
// Only track SINGLE-index subscript access (potential array access)
|
|
2966
|
-
// Two-index subscript like value[0, 8] is bit extraction, not array access
|
|
2967
|
-
const hasSingleIndexSubscript = ops.some(
|
|
2968
|
-
(op) => op.expression().length === 1,
|
|
2969
|
-
);
|
|
2970
|
-
if (hasSingleIndexSubscript) {
|
|
2971
|
-
CodeGenState.subscriptAccessedParameters.get(funcName)!.add(primaryId);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
|
|
2975
|
-
/**
|
|
2976
|
-
* Generic walker for orExpression trees.
|
|
2977
|
-
* Walks through the expression hierarchy and calls the handler for each unaryExpression.
|
|
2978
|
-
* Used by both function call tracking and subscript access tracking.
|
|
2979
|
-
*/
|
|
2980
|
-
private walkOrExpression(
|
|
2981
|
-
orExpr: Parser.OrExpressionContext,
|
|
2982
|
-
handler: (unaryExpr: Parser.UnaryExpressionContext) => void,
|
|
2983
|
-
): void {
|
|
2984
|
-
orExpr
|
|
2985
|
-
.andExpression()
|
|
2986
|
-
.flatMap((and) => and.equalityExpression())
|
|
2987
|
-
.flatMap((eq) => eq.relationalExpression())
|
|
2988
|
-
.flatMap((rel) => rel.bitwiseOrExpression())
|
|
2989
|
-
.flatMap((bor) => bor.bitwiseXorExpression())
|
|
2990
|
-
.flatMap((bxor) => bxor.bitwiseAndExpression())
|
|
2991
|
-
.flatMap((band) => band.shiftExpression())
|
|
2992
|
-
.flatMap((shift) => shift.additiveExpression())
|
|
2993
|
-
.flatMap((add) => add.multiplicativeExpression())
|
|
2994
|
-
.flatMap((mul) => mul.unaryExpression())
|
|
2995
|
-
.forEach(handler);
|
|
2996
|
-
}
|
|
2997
|
-
|
|
2998
|
-
/**
|
|
2999
|
-
* Walk an orExpression tree for function calls.
|
|
3000
|
-
*/
|
|
3001
|
-
private walkOrExpressionForCalls(
|
|
3002
|
-
funcName: string,
|
|
3003
|
-
paramSet: Set<string>,
|
|
3004
|
-
orExpr: Parser.OrExpressionContext,
|
|
3005
|
-
): void {
|
|
3006
|
-
this.walkOrExpression(orExpr, (unaryExpr) => {
|
|
3007
|
-
this.walkUnaryExpressionForCalls(funcName, paramSet, unaryExpr);
|
|
3008
|
-
});
|
|
3009
|
-
}
|
|
3010
|
-
|
|
3011
|
-
/**
|
|
3012
|
-
* Walk a unaryExpression tree for function calls.
|
|
3013
|
-
*/
|
|
3014
|
-
private walkUnaryExpressionForCalls(
|
|
3015
|
-
funcName: string,
|
|
3016
|
-
paramSet: Set<string>,
|
|
3017
|
-
unaryExpr: Parser.UnaryExpressionContext,
|
|
3018
|
-
): void {
|
|
3019
|
-
// Recurse into nested unary
|
|
3020
|
-
if (unaryExpr.unaryExpression()) {
|
|
3021
|
-
this.walkUnaryExpressionForCalls(
|
|
3022
|
-
funcName,
|
|
3023
|
-
paramSet,
|
|
3024
|
-
unaryExpr.unaryExpression()!,
|
|
3025
|
-
);
|
|
3026
|
-
return;
|
|
3027
|
-
}
|
|
3028
|
-
|
|
3029
|
-
// Check postfix expression
|
|
3030
|
-
const postfix = unaryExpr.postfixExpression();
|
|
3031
|
-
if (postfix) {
|
|
3032
|
-
this.walkPostfixExpressionForCalls(funcName, paramSet, postfix);
|
|
3033
|
-
}
|
|
3034
|
-
}
|
|
3035
|
-
|
|
3036
|
-
/**
|
|
3037
|
-
* Walk a postfixExpression for function calls.
|
|
3038
|
-
* This is where function calls are found: primaryExpr followed by '(' args ')'
|
|
3039
|
-
*/
|
|
3040
|
-
private walkPostfixExpressionForCalls(
|
|
3041
|
-
funcName: string,
|
|
3042
|
-
paramSet: Set<string>,
|
|
3043
|
-
postfix: Parser.PostfixExpressionContext,
|
|
3044
|
-
): void {
|
|
3045
|
-
const primary = postfix.primaryExpression();
|
|
3046
|
-
const postfixOps = postfix.postfixOp();
|
|
3047
|
-
|
|
3048
|
-
// Handle simple function calls: IDENTIFIER followed by '(' ... ')'
|
|
3049
|
-
this.handleSimpleFunctionCall(funcName, paramSet, primary, postfixOps);
|
|
3050
|
-
|
|
3051
|
-
// Issue #365: Handle scope-qualified calls: Scope.method(...) or global.Scope.method(...)
|
|
3052
|
-
this.handleScopeQualifiedCalls(funcName, paramSet, primary, postfixOps);
|
|
3053
|
-
|
|
3054
|
-
// Recurse into primary expression if it's a parenthesized expression
|
|
3055
|
-
if (primary.expression()) {
|
|
3056
|
-
this.walkExpressionForCalls(funcName, paramSet, primary.expression()!);
|
|
3057
|
-
}
|
|
3058
|
-
|
|
3059
|
-
// Walk arguments in any postfix function call ops (for nested calls)
|
|
3060
|
-
this.walkPostfixOpsRecursively(funcName, paramSet, postfixOps);
|
|
3061
|
-
}
|
|
3062
|
-
|
|
3063
|
-
/**
|
|
3064
|
-
* Handle simple function calls: IDENTIFIER followed by '(' ... ')'
|
|
3065
|
-
*/
|
|
3066
|
-
private handleSimpleFunctionCall(
|
|
3067
|
-
funcName: string,
|
|
3068
|
-
paramSet: Set<string>,
|
|
3069
|
-
primary: Parser.PrimaryExpressionContext,
|
|
3070
|
-
postfixOps: Parser.PostfixOpContext[],
|
|
3071
|
-
): void {
|
|
3072
|
-
if (!primary.IDENTIFIER() || postfixOps.length === 0) return;
|
|
3073
|
-
|
|
3074
|
-
const firstOp = postfixOps[0];
|
|
3075
|
-
if (!firstOp.LPAREN()) return;
|
|
3076
|
-
|
|
3077
|
-
const calleeName = primary.IDENTIFIER()!.getText();
|
|
3078
|
-
this.recordCallsFromArgList(funcName, paramSet, calleeName, firstOp);
|
|
3079
|
-
}
|
|
3080
|
-
|
|
3081
|
-
/**
|
|
3082
|
-
* Handle scope-qualified calls: Scope.method(...) or global.Scope.method(...)
|
|
3083
|
-
* Track member accesses to build the mangled callee name (e.g., Storage_load)
|
|
3084
|
-
*/
|
|
3085
|
-
private handleScopeQualifiedCalls(
|
|
3086
|
-
funcName: string,
|
|
3087
|
-
paramSet: Set<string>,
|
|
3088
|
-
primary: Parser.PrimaryExpressionContext,
|
|
3089
|
-
postfixOps: Parser.PostfixOpContext[],
|
|
3090
|
-
): void {
|
|
3091
|
-
if (postfixOps.length === 0) return;
|
|
3092
|
-
|
|
3093
|
-
const memberNames = this.collectInitialMemberNames(funcName, primary);
|
|
3094
|
-
|
|
3095
|
-
for (const op of postfixOps) {
|
|
3096
|
-
if (op.IDENTIFIER()) {
|
|
3097
|
-
memberNames.push(op.IDENTIFIER()!.getText());
|
|
3098
|
-
} else if (op.LPAREN() && memberNames.length >= 1) {
|
|
3099
|
-
const calleeName = memberNames.join("_");
|
|
3100
|
-
this.recordCallsFromArgList(funcName, paramSet, calleeName, op);
|
|
3101
|
-
memberNames.length = 0; // Reset for potential chained calls
|
|
3102
|
-
} else if (op.expression().length > 0) {
|
|
3103
|
-
memberNames.length = 0; // Array subscript breaks scope chain
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
|
|
3108
|
-
/**
|
|
3109
|
-
* Collect initial member names from primary expression for scope resolution.
|
|
3110
|
-
* Issue #561: When 'this' is used, resolve to the current scope name from funcName.
|
|
3111
|
-
*/
|
|
3112
|
-
private collectInitialMemberNames(
|
|
3113
|
-
funcName: string,
|
|
3114
|
-
primary: Parser.PrimaryExpressionContext,
|
|
3115
|
-
): string[] {
|
|
3116
|
-
const memberNames: string[] = [];
|
|
3117
|
-
const primaryId = primary.IDENTIFIER()?.getText();
|
|
3118
|
-
|
|
3119
|
-
if (primaryId && primaryId !== "global") {
|
|
3120
|
-
memberNames.push(primaryId);
|
|
3121
|
-
} else if (primary.THIS()) {
|
|
3122
|
-
const scopeName = funcName.split("_")[0];
|
|
3123
|
-
if (scopeName && scopeName !== funcName) {
|
|
3124
|
-
memberNames.push(scopeName);
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
return memberNames;
|
|
3128
|
-
}
|
|
3129
|
-
|
|
3130
|
-
/**
|
|
3131
|
-
* Record function calls to the call graph from an argument list.
|
|
3132
|
-
* Also recurses into argument expressions.
|
|
3133
|
-
*/
|
|
3134
|
-
private recordCallsFromArgList(
|
|
3135
|
-
funcName: string,
|
|
3136
|
-
paramSet: Set<string>,
|
|
3137
|
-
calleeName: string,
|
|
3138
|
-
op: Parser.PostfixOpContext,
|
|
3139
|
-
): void {
|
|
3140
|
-
const argList = op.argumentList();
|
|
3141
|
-
if (!argList) return;
|
|
3142
|
-
|
|
3143
|
-
const args = argList.expression();
|
|
3144
|
-
for (let i = 0; i < args.length; i++) {
|
|
3145
|
-
const arg = args[i];
|
|
3146
|
-
const argName = ExpressionUtils.extractIdentifier(arg);
|
|
3147
|
-
if (argName && paramSet.has(argName)) {
|
|
3148
|
-
CodeGenState.functionCallGraph.get(funcName)!.push({
|
|
3149
|
-
callee: calleeName,
|
|
3150
|
-
paramIndex: i,
|
|
3151
|
-
argParamName: argName,
|
|
3152
|
-
});
|
|
3153
|
-
}
|
|
3154
|
-
this.walkExpressionForCalls(funcName, paramSet, arg);
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
|
|
3158
|
-
/**
|
|
3159
|
-
* Walk postfix ops recursively for nested calls and array subscripts.
|
|
3160
|
-
*/
|
|
3161
|
-
private walkPostfixOpsRecursively(
|
|
3162
|
-
funcName: string,
|
|
3163
|
-
paramSet: Set<string>,
|
|
3164
|
-
postfixOps: Parser.PostfixOpContext[],
|
|
3165
|
-
): void {
|
|
3166
|
-
for (const op of postfixOps) {
|
|
3167
|
-
if (op.argumentList()) {
|
|
3168
|
-
for (const argExpr of op.argumentList()!.expression()) {
|
|
3169
|
-
this.walkExpressionForCalls(funcName, paramSet, argExpr);
|
|
3170
|
-
}
|
|
3171
|
-
}
|
|
3172
|
-
for (const expr of op.expression()) {
|
|
3173
|
-
this.walkExpressionForCalls(funcName, paramSet, expr);
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
}
|
|
3177
|
-
|
|
3178
|
-
/**
|
|
3179
|
-
* Phase 3: Determine which parameters can pass by value.
|
|
3180
|
-
* A parameter passes by value if:
|
|
3181
|
-
* 1. It's a small primitive type (u8, i8, u16, i16, u32, i32, u64, i64, bool)
|
|
3182
|
-
* 2. It's not modified (directly or transitively)
|
|
3183
|
-
* 3. It's not an array, struct, string, or callback
|
|
3184
|
-
*/
|
|
3185
|
-
private computePassByValueParams(): void {
|
|
3186
|
-
const smallPrimitives = new Set([
|
|
3187
|
-
"u8",
|
|
3188
|
-
"i8",
|
|
3189
|
-
"u16",
|
|
3190
|
-
"i16",
|
|
3191
|
-
"u32",
|
|
3192
|
-
"i32",
|
|
3193
|
-
"u64",
|
|
3194
|
-
"i64",
|
|
3195
|
-
"bool",
|
|
3196
|
-
]);
|
|
3197
|
-
|
|
3198
|
-
for (const [funcName, paramNames] of CodeGenState.functionParamLists) {
|
|
3199
|
-
const passByValue = new Set<string>();
|
|
3200
|
-
const modified =
|
|
3201
|
-
CodeGenState.modifiedParameters.get(funcName) ?? new Set();
|
|
3202
|
-
|
|
3203
|
-
// Get function declaration to check parameter types
|
|
3204
|
-
const funcSig = CodeGenState.functionSignatures.get(funcName);
|
|
3205
|
-
if (funcSig) {
|
|
3206
|
-
for (let i = 0; i < paramNames.length; i++) {
|
|
3207
|
-
const paramName = paramNames[i];
|
|
3208
|
-
const paramSig = funcSig.parameters[i];
|
|
3209
|
-
|
|
3210
|
-
if (!paramSig) continue;
|
|
3211
|
-
|
|
3212
|
-
// Check if eligible for pass-by-value:
|
|
3213
|
-
// - Is a small primitive type
|
|
3214
|
-
// - Not an array
|
|
3215
|
-
// - Not modified
|
|
3216
|
-
// - Not accessed via subscript (Issue #579)
|
|
3217
|
-
const isSmallPrimitive = smallPrimitives.has(paramSig.baseType);
|
|
3218
|
-
const isArray = paramSig.isArray ?? false;
|
|
3219
|
-
const isModified = modified.has(paramName);
|
|
3220
|
-
// Issue #579: Parameters with subscript access must become pointers
|
|
3221
|
-
const hasSubscriptAccess =
|
|
3222
|
-
CodeGenState.subscriptAccessedParameters
|
|
3223
|
-
.get(funcName)
|
|
3224
|
-
?.has(paramName) ?? false;
|
|
3225
|
-
|
|
3226
|
-
if (
|
|
3227
|
-
isSmallPrimitive &&
|
|
3228
|
-
!isArray &&
|
|
3229
|
-
!isModified &&
|
|
3230
|
-
!hasSubscriptAccess
|
|
3231
|
-
) {
|
|
3232
|
-
passByValue.add(paramName);
|
|
3233
|
-
}
|
|
3234
|
-
}
|
|
3235
|
-
}
|
|
3236
|
-
|
|
3237
|
-
CodeGenState.passByValueParams.set(funcName, passByValue);
|
|
3238
|
-
}
|
|
3239
|
-
}
|
|
3240
|
-
|
|
3241
|
-
/**
|
|
3242
|
-
* Check if a parameter should be passed by value (by name).
|
|
3243
|
-
* Used internally during code generation.
|
|
3244
|
-
*/
|
|
3245
|
-
private _isParameterPassByValueByName(
|
|
3246
|
-
funcName: string,
|
|
3247
|
-
paramName: string,
|
|
3248
|
-
): boolean {
|
|
3249
|
-
const passByValue = CodeGenState.passByValueParams.get(funcName);
|
|
3250
|
-
return passByValue?.has(paramName) ?? false;
|
|
3251
|
-
}
|
|
2622
|
+
// Issue #269: Pass-by-value analysis extracted to PassByValueAnalyzer
|
|
3252
2623
|
|
|
3253
2624
|
/**
|
|
3254
2625
|
* Issue #269: Check if a parameter should be passed by value (by index).
|
|
3255
2626
|
* Part of IOrchestrator interface - used by CallExprGenerator.
|
|
2627
|
+
* Delegates to PassByValueAnalyzer.
|
|
3256
2628
|
*/
|
|
3257
2629
|
isParameterPassByValue(funcName: string, paramIndex: number): boolean {
|
|
3258
|
-
|
|
3259
|
-
if (!paramList || paramIndex < 0 || paramIndex >= paramList.length) {
|
|
3260
|
-
return false;
|
|
3261
|
-
}
|
|
3262
|
-
const paramName = paramList[paramIndex];
|
|
3263
|
-
return this._isParameterPassByValueByName(funcName, paramName);
|
|
2630
|
+
return PassByValueAnalyzer.isParameterPassByValue(funcName, paramIndex);
|
|
3264
2631
|
}
|
|
3265
2632
|
|
|
3266
2633
|
/**
|
|
@@ -3319,7 +2686,6 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3319
2686
|
// ADR-017: Check if this is an enum type
|
|
3320
2687
|
if (
|
|
3321
2688
|
TypeRegistrationUtils.tryRegisterEnumType(
|
|
3322
|
-
CodeGenState.typeRegistry,
|
|
3323
2689
|
CodeGenState.symbols!,
|
|
3324
2690
|
registrationOptions,
|
|
3325
2691
|
)
|
|
@@ -3331,7 +2697,6 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3331
2697
|
const bitmapDimensions = this._evaluateArrayDimensions(arrayDim);
|
|
3332
2698
|
if (
|
|
3333
2699
|
TypeRegistrationUtils.tryRegisterBitmapType(
|
|
3334
|
-
CodeGenState.typeRegistry,
|
|
3335
2700
|
CodeGenState.symbols!,
|
|
3336
2701
|
registrationOptions,
|
|
3337
2702
|
bitmapDimensions,
|
|
@@ -3398,7 +2763,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3398
2763
|
const allDims =
|
|
3399
2764
|
additionalDims.length > 0 ? [...additionalDims, stringDim] : [stringDim];
|
|
3400
2765
|
|
|
3401
|
-
CodeGenState.
|
|
2766
|
+
CodeGenState.setVariableTypeInfo(registryName, {
|
|
3402
2767
|
baseType: "char",
|
|
3403
2768
|
bitWidth: 8,
|
|
3404
2769
|
isArray: true,
|
|
@@ -3564,14 +2929,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3564
2929
|
)
|
|
3565
2930
|
) {
|
|
3566
2931
|
// Enum/bitmap was registered, but we need to update with arrayType dimension
|
|
3567
|
-
const existingInfo = CodeGenState.
|
|
2932
|
+
const existingInfo = CodeGenState.getVariableTypeInfo(registryName);
|
|
3568
2933
|
if (existingInfo) {
|
|
3569
2934
|
const arrayTypeDim =
|
|
3570
2935
|
this._parseArrayTypeDimensionFromCtx(arrayTypeCtx);
|
|
3571
2936
|
const allDims = arrayTypeDim
|
|
3572
2937
|
? [arrayTypeDim, ...(existingInfo.arrayDimensions ?? [])]
|
|
3573
2938
|
: existingInfo.arrayDimensions;
|
|
3574
|
-
CodeGenState.
|
|
2939
|
+
CodeGenState.setVariableTypeInfo(registryName, {
|
|
3575
2940
|
...existingInfo,
|
|
3576
2941
|
isArray: true,
|
|
3577
2942
|
arrayDimensions: allDims,
|
|
@@ -3593,7 +2958,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3593
2958
|
arrayDim,
|
|
3594
2959
|
);
|
|
3595
2960
|
|
|
3596
|
-
CodeGenState.
|
|
2961
|
+
CodeGenState.setVariableTypeInfo(registryName, {
|
|
3597
2962
|
baseType,
|
|
3598
2963
|
bitWidth,
|
|
3599
2964
|
isArray: true,
|
|
@@ -3672,7 +3037,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3672
3037
|
? this._evaluateArrayDimensions(arrayDim)
|
|
3673
3038
|
: undefined;
|
|
3674
3039
|
|
|
3675
|
-
CodeGenState.
|
|
3040
|
+
CodeGenState.setVariableTypeInfo(registryName, {
|
|
3676
3041
|
baseType,
|
|
3677
3042
|
bitWidth,
|
|
3678
3043
|
isArray,
|
|
@@ -3925,7 +3290,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3925
3290
|
stringCapacity,
|
|
3926
3291
|
isParameter: true,
|
|
3927
3292
|
};
|
|
3928
|
-
CodeGenState.
|
|
3293
|
+
CodeGenState.setVariableTypeInfo(name, registeredType);
|
|
3929
3294
|
}
|
|
3930
3295
|
|
|
3931
3296
|
/**
|
|
@@ -3962,13 +3327,10 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3962
3327
|
private _clearParameters(): void {
|
|
3963
3328
|
// ADR-025: Remove parameter types from typeRegistry
|
|
3964
3329
|
for (const name of CodeGenState.currentParameters.keys()) {
|
|
3965
|
-
CodeGenState.
|
|
3966
|
-
CodeGenState.typeRegistry.delete(name);
|
|
3330
|
+
CodeGenState.deleteVariableTypeInfo(name);
|
|
3967
3331
|
}
|
|
3968
3332
|
CodeGenState.currentParameters.clear();
|
|
3969
3333
|
CodeGenState.localArrays.clear();
|
|
3970
|
-
CodeGenState.currentParameters.clear();
|
|
3971
|
-
CodeGenState.localArrays.clear();
|
|
3972
3334
|
}
|
|
3973
3335
|
|
|
3974
3336
|
/**
|
|
@@ -4183,7 +3545,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4183
3545
|
const sourceName = sourceId.getText();
|
|
4184
3546
|
|
|
4185
3547
|
// Check if source is a string type
|
|
4186
|
-
const typeInfo = CodeGenState.
|
|
3548
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(sourceName);
|
|
4187
3549
|
if (!typeInfo?.isString || typeInfo.stringCapacity === undefined) {
|
|
4188
3550
|
return null;
|
|
4189
3551
|
}
|
|
@@ -4370,7 +3732,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4370
3732
|
baseId: string,
|
|
4371
3733
|
targetParamBaseType: string,
|
|
4372
3734
|
): boolean {
|
|
4373
|
-
const typeInfo = CodeGenState.
|
|
3735
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(baseId);
|
|
4374
3736
|
return CppMemberHelper.needsComplexMemberConversion(
|
|
4375
3737
|
this._toPostfixOps(ops),
|
|
4376
3738
|
typeInfo,
|
|
@@ -4397,7 +3759,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4397
3759
|
const baseId = primary.IDENTIFIER()?.getText();
|
|
4398
3760
|
if (!baseId) return false;
|
|
4399
3761
|
|
|
4400
|
-
const typeInfo = CodeGenState.
|
|
3762
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(baseId);
|
|
4401
3763
|
const paramInfo = CodeGenState.currentParameters.get(baseId);
|
|
4402
3764
|
|
|
4403
3765
|
return CppMemberHelper.isStringSubscriptPattern(
|
|
@@ -4451,7 +3813,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4451
3813
|
// 2. Parameter: currentParameters.get(baseId).baseType
|
|
4452
3814
|
let structType: string | undefined;
|
|
4453
3815
|
|
|
4454
|
-
const typeInfo = CodeGenState.
|
|
3816
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(baseId);
|
|
4455
3817
|
if (typeInfo) {
|
|
4456
3818
|
structType = typeInfo.baseType;
|
|
4457
3819
|
} else {
|
|
@@ -4518,14 +3880,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4518
3880
|
|
|
4519
3881
|
// Global arrays also decay to pointers (check typeRegistry)
|
|
4520
3882
|
// But NOT strings - strings need & (they're char arrays but passed by reference)
|
|
4521
|
-
const typeInfo = CodeGenState.
|
|
3883
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(id);
|
|
4522
3884
|
if (typeInfo?.isArray && !typeInfo.isString) {
|
|
4523
3885
|
return id;
|
|
4524
3886
|
}
|
|
4525
3887
|
|
|
4526
3888
|
// Scope member - may need prefixing
|
|
4527
3889
|
if (CodeGenState.currentScope) {
|
|
4528
|
-
const members = CodeGenState.
|
|
3890
|
+
const members = CodeGenState.getScopeMembers(CodeGenState.currentScope);
|
|
4529
3891
|
if (members?.has(id)) {
|
|
4530
3892
|
const scopedName = `${CodeGenState.currentScope}_${id}`;
|
|
4531
3893
|
return CppModeHelper.maybeAddressOf(scopedName);
|
|
@@ -5388,7 +4750,10 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5388
4750
|
// Small unmodified primitives
|
|
5389
4751
|
if (
|
|
5390
4752
|
CodeGenState.currentFunctionName &&
|
|
5391
|
-
|
|
4753
|
+
PassByValueAnalyzer.isParameterPassByValueByName(
|
|
4754
|
+
CodeGenState.currentFunctionName,
|
|
4755
|
+
name,
|
|
4756
|
+
)
|
|
5392
4757
|
) {
|
|
5393
4758
|
return true;
|
|
5394
4759
|
}
|
|
@@ -5847,14 +5212,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5847
5212
|
const argName = argNode.getText();
|
|
5848
5213
|
|
|
5849
5214
|
// Check if it exists in type registry
|
|
5850
|
-
const typeInfo = CodeGenState.
|
|
5215
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(argName);
|
|
5851
5216
|
|
|
5852
5217
|
// Also check scoped variables if inside a scope
|
|
5853
5218
|
let scopedArgName = argName;
|
|
5854
5219
|
let scopedTypeInfo = typeInfo;
|
|
5855
5220
|
if (!typeInfo && CodeGenState.currentScope) {
|
|
5856
5221
|
scopedArgName = `${CodeGenState.currentScope}_${argName}`;
|
|
5857
|
-
scopedTypeInfo = CodeGenState.
|
|
5222
|
+
scopedTypeInfo = CodeGenState.getVariableTypeInfo(scopedArgName);
|
|
5858
5223
|
}
|
|
5859
5224
|
|
|
5860
5225
|
if (!typeInfo && !scopedTypeInfo) {
|
|
@@ -5878,7 +5243,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5878
5243
|
}
|
|
5879
5244
|
|
|
5880
5245
|
// Track the variable in type registry (as an external C++ type)
|
|
5881
|
-
CodeGenState.
|
|
5246
|
+
CodeGenState.setVariableTypeInfo(name, {
|
|
5882
5247
|
baseType: type,
|
|
5883
5248
|
bitWidth: 0, // Unknown for C++ types
|
|
5884
5249
|
isArray: false,
|
|
@@ -6118,8 +5483,12 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6118
5483
|
// ADR-109: Dispatch to assignment handlers
|
|
6119
5484
|
// Build context, classify, and dispatch - all patterns handled by handlers
|
|
6120
5485
|
const assignCtx = buildAssignmentContext(ctx, {
|
|
6121
|
-
typeRegistry: CodeGenState.
|
|
5486
|
+
typeRegistry: CodeGenState.getTypeRegistryView(),
|
|
6122
5487
|
generateExpression: () => value,
|
|
5488
|
+
generateAssignmentTarget: (targetCtx) =>
|
|
5489
|
+
this.generateAssignmentTarget(targetCtx),
|
|
5490
|
+
isKnownRegister: (name) => CodeGenState.symbols!.knownRegisters.has(name),
|
|
5491
|
+
currentScope: CodeGenState.currentScope,
|
|
6123
5492
|
});
|
|
6124
5493
|
// ADR-109: Handlers access CodeGenState directly, no deps needed
|
|
6125
5494
|
const assignmentKind = AssignmentClassifier.classify(assignCtx);
|
|
@@ -6608,6 +5977,55 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6608
5977
|
);
|
|
6609
5978
|
}
|
|
6610
5979
|
|
|
5980
|
+
/**
|
|
5981
|
+
* Generate platform-portable IRQ wrappers for critical sections (ADR-050, Issue #778)
|
|
5982
|
+
*
|
|
5983
|
+
* Generates code that works on:
|
|
5984
|
+
* - ARM platforms (bare-metal or Arduino): Uses inline assembly for PRIMASK access
|
|
5985
|
+
* - AVR Arduino: Uses SREG save/restore pattern
|
|
5986
|
+
* - Other platforms: Falls back to CMSIS intrinsics
|
|
5987
|
+
*
|
|
5988
|
+
* This avoids dependencies on CMSIS headers which may not be available on all platforms
|
|
5989
|
+
* (e.g., Teensy 4.x via Arduino.h doesn't expose __get_PRIMASK/__set_PRIMASK).
|
|
5990
|
+
*/
|
|
5991
|
+
private generateIrqWrappers(): string[] {
|
|
5992
|
+
return [
|
|
5993
|
+
"// ADR-050: Platform-portable IRQ wrappers for critical sections",
|
|
5994
|
+
"#if defined(__arm__) || defined(__ARM_ARCH)",
|
|
5995
|
+
"// ARM platforms (including ARM Arduino like Teensy 4.x, Due, Zero)",
|
|
5996
|
+
"// Provide inline assembly PRIMASK access to avoid CMSIS header dependencies",
|
|
5997
|
+
"__attribute__((always_inline)) static inline uint32_t __cnx_get_PRIMASK(void) {",
|
|
5998
|
+
" uint32_t result;",
|
|
5999
|
+
' __asm volatile ("MRS %0, primask" : "=r" (result));',
|
|
6000
|
+
" return result;",
|
|
6001
|
+
"}",
|
|
6002
|
+
"__attribute__((always_inline)) static inline void __cnx_set_PRIMASK(uint32_t mask) {",
|
|
6003
|
+
' __asm volatile ("MSR primask, %0" :: "r" (mask) : "memory");',
|
|
6004
|
+
"}",
|
|
6005
|
+
"#if defined(ARDUINO)",
|
|
6006
|
+
"static inline void __cnx_disable_irq(void) { noInterrupts(); }",
|
|
6007
|
+
"#else",
|
|
6008
|
+
"__attribute__((always_inline)) static inline void __cnx_disable_irq(void) {",
|
|
6009
|
+
' __asm volatile ("cpsid i" ::: "memory");',
|
|
6010
|
+
"}",
|
|
6011
|
+
"#endif",
|
|
6012
|
+
"#elif defined(__AVR__)",
|
|
6013
|
+
"// AVR Arduino: use SREG for interrupt state",
|
|
6014
|
+
"// Note: Uses PRIMASK naming for API consistency across platforms (AVR has no PRIMASK)",
|
|
6015
|
+
"// Returns uint8_t which is implicitly widened to uint32_t at call sites - this is intentional",
|
|
6016
|
+
"static inline uint8_t __cnx_get_PRIMASK(void) { return SREG; }",
|
|
6017
|
+
"static inline void __cnx_set_PRIMASK(uint8_t mask) { SREG = mask; }",
|
|
6018
|
+
"static inline void __cnx_disable_irq(void) { cli(); }",
|
|
6019
|
+
"#else",
|
|
6020
|
+
"// Fallback: assume CMSIS is available",
|
|
6021
|
+
"static inline void __cnx_disable_irq(void) { __disable_irq(); }",
|
|
6022
|
+
"static inline uint32_t __cnx_get_PRIMASK(void) { return __get_PRIMASK(); }",
|
|
6023
|
+
"static inline void __cnx_set_PRIMASK(uint32_t mask) { __set_PRIMASK(mask); }",
|
|
6024
|
+
"#endif",
|
|
6025
|
+
"",
|
|
6026
|
+
];
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6611
6029
|
/**
|
|
6612
6030
|
* Mark a clamp operation as used (will trigger helper generation)
|
|
6613
6031
|
*/
|