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.
Files changed (62) hide show
  1. package/package.json +1 -1
  2. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +240 -204
  3. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +693 -0
  4. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +86 -5
  5. package/src/transpiler/{output/codegen → logic/analysis}/helpers/AssignmentTargetExtractor.ts +1 -1
  6. package/src/transpiler/{output/codegen → logic/analysis}/helpers/ChildStatementCollector.ts +1 -1
  7. package/src/transpiler/{output/codegen → logic/analysis}/helpers/StatementExpressionCollector.ts +1 -1
  8. package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/AssignmentTargetExtractor.test.ts +2 -2
  9. package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/ChildStatementCollector.test.ts +2 -2
  10. package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/StatementExpressionCollector.test.ts +2 -2
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +160 -742
  12. package/src/transpiler/output/codegen/TypeRegistrationUtils.ts +4 -6
  13. package/src/transpiler/output/codegen/TypeResolver.ts +2 -2
  14. package/src/transpiler/output/codegen/TypeValidator.ts +7 -7
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +2 -2
  16. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +29 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
  18. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +4 -6
  20. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +4 -2
  21. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
  22. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
  23. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
  24. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
  25. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
  26. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +49 -0
  27. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +15 -0
  28. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +30 -17
  29. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +25 -18
  30. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +19 -8
  31. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
  32. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
  33. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
  34. package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +9 -1
  35. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +41 -26
  36. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +29 -37
  37. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +27 -19
  38. package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +10 -1
  39. package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +51 -33
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +9 -1
  41. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
  42. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
  43. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
  44. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
  45. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
  46. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
  47. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
  48. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
  49. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
  50. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +6 -1
  51. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
  52. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
  53. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
  54. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
  55. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
  56. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
  57. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
  58. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
  59. package/src/transpiler/state/CodeGenState.ts +157 -5
  60. package/src/transpiler/state/__tests__/CodeGenState.test.ts +274 -6
  61. /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
  62. /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 #566: Statement expression collection for const inference
124
- import StatementExpressionCollector from "./helpers/StatementExpressionCollector";
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.typeRegistry,
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.scopeMembers,
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.typeRegistry.set(effect.name, effect.info);
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.scopeMembers.get(CodeGenState.currentScope);
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.typeRegistry.get(text);
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.typeRegistry.get(arrayName);
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.typeRegistry.get(exprCode);
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 this.functionUnmodifiedParams;
1504
+ return CodeGenState.getUnmodifiedParameters();
1563
1505
  }
1564
1506
 
1565
1507
  /**
1566
1508
  * Issue #268: Update symbol parameters with auto-const info.
1567
- * Issue #558: Now uses analysis-phase results for modification tracking.
1568
- */
1569
- updateFunctionParamsAutoConst(functionName: string): void {
1570
- // Collect unmodified parameters for this function using analysis results
1571
- const unmodifiedParams = new Set<string>();
1572
- const modifiedSet = CodeGenState.modifiedParameters.get(functionName);
1573
- for (const [paramName] of CodeGenState.currentParameters) {
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
- this.collectFunctionParametersAndModifications(tree);
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
- // If the param is NOT in the unmodified set, it was modified
1792
- return !unmodifiedParams.has(paramName);
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
- this._isParameterPassByValueByName(funcName, paramName),
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 first
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.scopeMembers.set(scopeName, new Set(members));
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
- this.analyzePassByValue(tree);
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
- const paramList = CodeGenState.functionParamLists.get(funcName);
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.typeRegistry.set(registryName, {
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.typeRegistry.get(registryName);
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.typeRegistry.set(registryName, {
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.typeRegistry.set(registryName, {
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.typeRegistry.set(registryName, {
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.typeRegistry.set(name, registeredType);
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.typeRegistry.delete(name);
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.typeRegistry.get(sourceName);
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.typeRegistry.get(baseId);
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.typeRegistry.get(baseId);
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.typeRegistry.get(baseId);
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.typeRegistry.get(id);
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.scopeMembers.get(CodeGenState.currentScope);
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
- this._isParameterPassByValueByName(CodeGenState.currentFunctionName, name)
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.typeRegistry.get(argName);
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.typeRegistry.get(scopedArgName);
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.typeRegistry.set(name, {
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.typeRegistry,
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
  */