c-next 0.1.69 → 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 (54) 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 +35 -607
  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 +5 -5
  15. package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
  16. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
  17. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +3 -3
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +1 -1
  19. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
  20. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
  21. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
  22. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
  23. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
  24. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +23 -17
  25. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +2 -2
  26. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +3 -3
  27. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
  28. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
  29. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
  30. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +23 -25
  31. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +20 -36
  32. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +18 -18
  33. package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +42 -32
  34. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
  35. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
  36. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
  37. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
  38. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
  39. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
  40. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
  41. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
  42. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
  43. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
  44. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
  45. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
  46. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
  47. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
  50. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
  51. package/src/transpiler/state/CodeGenState.ts +122 -4
  52. package/src/transpiler/state/__tests__/CodeGenState.test.ts +269 -1
  53. /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
  54. /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";
@@ -387,7 +383,7 @@ export default class CodeGenerator implements IOrchestrator {
387
383
  return {
388
384
  symbolTable: CodeGenState.symbolTable,
389
385
  symbols: CodeGenState.symbols,
390
- typeRegistry: CodeGenState.typeRegistry,
386
+ typeRegistry: CodeGenState.getTypeRegistryView(),
391
387
  functionSignatures: CodeGenState.functionSignatures,
392
388
  knownFunctions: CodeGenState.knownFunctions,
393
389
  knownStructs: CodeGenState.symbols?.knownStructs ?? new Set(),
@@ -451,7 +447,7 @@ export default class CodeGenerator implements IOrchestrator {
451
447
 
452
448
  // Type registration effects
453
449
  case "register-type":
454
- CodeGenState.typeRegistry.set(effect.name, effect.info);
450
+ CodeGenState.setVariableTypeInfo(effect.name, effect.info);
455
451
  break;
456
452
  case "register-local":
457
453
  CodeGenState.localVariables.add(effect.name);
@@ -727,7 +723,7 @@ export default class CodeGenerator implements IOrchestrator {
727
723
 
728
724
  // Check if it's a simple variable of string type
729
725
  if (/^[a-zA-Z_]\w*$/.exec(text)) {
730
- const typeInfo = CodeGenState.typeRegistry.get(text);
726
+ const typeInfo = CodeGenState.getVariableTypeInfo(text);
731
727
  if (typeInfo?.isString) {
732
728
  return true;
733
729
  }
@@ -759,7 +755,7 @@ export default class CodeGenerator implements IOrchestrator {
759
755
  }
760
756
 
761
757
  const arrayName = arrayAccessMatch[1];
762
- const typeInfo = CodeGenState.typeRegistry.get(arrayName);
758
+ const typeInfo = CodeGenState.getVariableTypeInfo(arrayName);
763
759
  if (!typeInfo) {
764
760
  return false;
765
761
  }
@@ -1368,7 +1364,7 @@ export default class CodeGenerator implements IOrchestrator {
1368
1364
  // Variable - check type registry
1369
1365
  const identifierRegex = /^[a-zA-Z_]\w*$/;
1370
1366
  if (identifierRegex.test(exprCode)) {
1371
- const typeInfo = CodeGenState.typeRegistry.get(exprCode);
1367
+ const typeInfo = CodeGenState.getVariableTypeInfo(exprCode);
1372
1368
  if (typeInfo?.isString && typeInfo.stringCapacity !== undefined) {
1373
1369
  return typeInfo.stringCapacity;
1374
1370
  }
@@ -1604,7 +1600,7 @@ export default class CodeGenerator implements IOrchestrator {
1604
1600
  const injectedFuncs = new Set(crossFileModifications?.keys() ?? []);
1605
1601
 
1606
1602
  // Run modification analysis on the tree (adds to what was injected)
1607
- this.collectFunctionParametersAndModifications(tree);
1603
+ PassByValueAnalyzer.collectFunctionParametersAndModifications(tree);
1608
1604
 
1609
1605
  // Issue #565: Run transitive propagation with full context
1610
1606
  TransitiveModificationPropagator.propagate(
@@ -1951,7 +1947,7 @@ export default class CodeGenerator implements IOrchestrator {
1951
1947
  isKnownPrimitive: (typeName: string) => this._isKnownPrimitive(typeName),
1952
1948
  knownEnums: CodeGenState.symbols!.knownEnums,
1953
1949
  isParameterPassByValue: (funcName: string, paramName: string) =>
1954
- this._isParameterPassByValueByName(funcName, paramName),
1950
+ PassByValueAnalyzer.isParameterPassByValueByName(funcName, paramName),
1955
1951
  currentFunctionName: CodeGenState.currentFunctionName,
1956
1952
  maybeDereference: (id: string) => CppModeHelper.maybeDereference(id),
1957
1953
  };
@@ -2196,7 +2192,7 @@ export default class CodeGenerator implements IOrchestrator {
2196
2192
  private initializeHelperObjects(tree: Parser.ProgramContext): void {
2197
2193
  // Collect function/callback information
2198
2194
  this.collectFunctionsAndCallbacks(tree);
2199
- this.analyzePassByValue(tree);
2195
+ PassByValueAnalyzer.analyze(tree);
2200
2196
  }
2201
2197
 
2202
2198
  /**
@@ -2623,581 +2619,15 @@ export default class CodeGenerator implements IOrchestrator {
2623
2619
  // Issue #63: validateBitmapFieldLiteral moved to TypeValidator
2624
2620
  // Issue #60: evaluateConstantExpression method removed - now in SymbolCollector
2625
2621
 
2626
- // ========================================================================
2627
- // Issue #269: Pass-by-value analysis for small unmodified parameters
2628
- // ========================================================================
2629
-
2630
- /**
2631
- * Analyze all functions to determine which parameters should pass by value.
2632
- * This runs before code generation and populates passByValueParams.
2633
- * SonarCloud S3776: Refactored to use helper methods.
2634
- */
2635
- private analyzePassByValue(tree: Parser.ProgramContext): void {
2636
- // Reset analysis state
2637
- CodeGenState.modifiedParameters.clear();
2638
- CodeGenState.passByValueParams.clear();
2639
- CodeGenState.functionCallGraph.clear();
2640
- CodeGenState.functionParamLists.clear();
2641
-
2642
- // Phase 1: Collect function parameter lists and direct modifications
2643
- this.collectFunctionParametersAndModifications(tree);
2644
-
2645
- // Issue #558: Inject cross-file data before transitive propagation
2646
- this.injectCrossFileModifications();
2647
- this.injectCrossFileParamLists();
2648
-
2649
- // Phase 2: Fixed-point iteration for transitive modifications
2650
- TransitiveModificationPropagator.propagate(
2651
- CodeGenState.functionCallGraph,
2652
- CodeGenState.functionParamLists,
2653
- CodeGenState.modifiedParameters,
2654
- );
2655
-
2656
- // Phase 3: Determine which parameters can pass by value
2657
- this.computePassByValueParams();
2658
- }
2659
-
2660
- /**
2661
- * Inject cross-file modification data into modifiedParameters.
2662
- * SonarCloud S3776: Extracted from analyzePassByValue().
2663
- */
2664
- private injectCrossFileModifications(): void {
2665
- if (!CodeGenState.pendingCrossFileModifications) return;
2666
-
2667
- for (const [
2668
- funcName,
2669
- params,
2670
- ] of CodeGenState.pendingCrossFileModifications) {
2671
- const existing = CodeGenState.modifiedParameters.get(funcName);
2672
- if (existing) {
2673
- for (const param of params) {
2674
- existing.add(param);
2675
- }
2676
- } else {
2677
- CodeGenState.modifiedParameters.set(funcName, new Set(params));
2678
- }
2679
- }
2680
- CodeGenState.pendingCrossFileModifications = null; // Clear after use
2681
- }
2682
-
2683
- /**
2684
- * Inject cross-file parameter lists into functionParamLists.
2685
- * SonarCloud S3776: Extracted from analyzePassByValue().
2686
- */
2687
- private injectCrossFileParamLists(): void {
2688
- if (!CodeGenState.pendingCrossFileParamLists) return;
2689
-
2690
- for (const [funcName, params] of CodeGenState.pendingCrossFileParamLists) {
2691
- if (!CodeGenState.functionParamLists.has(funcName)) {
2692
- CodeGenState.functionParamLists.set(funcName, [...params]);
2693
- }
2694
- }
2695
- CodeGenState.pendingCrossFileParamLists = null; // Clear after use
2696
- }
2697
-
2698
- /**
2699
- * Phase 1: Walk all functions to collect:
2700
- * - Parameter lists (for call graph resolution)
2701
- * - Direct modifications (param <- value)
2702
- * - Function calls where params are passed as arguments
2703
- */
2704
- private collectFunctionParametersAndModifications(
2705
- tree: Parser.ProgramContext,
2706
- ): void {
2707
- for (const decl of tree.declaration()) {
2708
- // Handle scope-level functions
2709
- if (decl.scopeDeclaration()) {
2710
- const scopeDecl = decl.scopeDeclaration()!;
2711
- const scopeName = scopeDecl.IDENTIFIER().getText();
2712
-
2713
- for (const member of scopeDecl.scopeMember()) {
2714
- if (member.functionDeclaration()) {
2715
- const funcDecl = member.functionDeclaration()!;
2716
- const funcName = funcDecl.IDENTIFIER().getText();
2717
- const fullName = `${scopeName}_${funcName}`;
2718
- this.analyzeFunctionForModifications(fullName, funcDecl);
2719
- }
2720
- }
2721
- }
2722
-
2723
- // Handle top-level functions
2724
- if (decl.functionDeclaration()) {
2725
- const funcDecl = decl.functionDeclaration()!;
2726
- const name = funcDecl.IDENTIFIER().getText();
2727
- this.analyzeFunctionForModifications(name, funcDecl);
2728
- }
2729
- }
2730
- }
2731
-
2732
- /**
2733
- * Analyze a single function for parameter modifications and call graph edges.
2734
- */
2735
- private analyzeFunctionForModifications(
2736
- funcName: string,
2737
- funcDecl: Parser.FunctionDeclarationContext,
2738
- ): void {
2739
- // Collect parameter names
2740
- const paramNames: string[] = [];
2741
- const paramList = funcDecl.parameterList();
2742
- if (paramList) {
2743
- for (const param of paramList.parameter()) {
2744
- paramNames.push(param.IDENTIFIER().getText());
2745
- }
2746
- }
2747
- CodeGenState.functionParamLists.set(funcName, paramNames);
2748
-
2749
- // Initialize modified set
2750
- CodeGenState.modifiedParameters.set(funcName, new Set());
2751
- // Issue #579: Initialize subscript access tracking
2752
- CodeGenState.subscriptAccessedParameters.set(funcName, new Set());
2753
- CodeGenState.functionCallGraph.set(funcName, []);
2754
-
2755
- // Walk the function body to find modifications and calls
2756
- const block = funcDecl.block();
2757
- if (block) {
2758
- this.walkBlockForModifications(funcName, paramNames, block);
2759
- }
2760
- }
2761
-
2762
- /**
2763
- * Walk a block to find parameter modifications and function calls.
2764
- */
2765
- private walkBlockForModifications(
2766
- funcName: string,
2767
- paramNames: string[],
2768
- block: Parser.BlockContext,
2769
- ): void {
2770
- const paramSet = new Set(paramNames);
2771
-
2772
- for (const stmt of block.statement()) {
2773
- this.walkStatementForModifications(funcName, paramSet, stmt);
2774
- }
2775
- }
2776
-
2777
- /**
2778
- * Walk a statement recursively looking for modifications and calls.
2779
- * Issue #566: Refactored to use helper methods for expression and child collection.
2780
- */
2781
- private walkStatementForModifications(
2782
- funcName: string,
2783
- paramSet: Set<string>,
2784
- stmt: Parser.StatementContext,
2785
- ): void {
2786
- // 1. Check for parameter modifications via assignment targets
2787
- if (stmt.assignmentStatement()) {
2788
- this.trackAssignmentModifications(funcName, paramSet, stmt);
2789
- }
2790
-
2791
- // 2. Walk all expressions in this statement for function calls and subscript access
2792
- for (const expr of StatementExpressionCollector.collectAll(stmt)) {
2793
- this.walkExpressionForCalls(funcName, paramSet, expr);
2794
- // Issue #579: Also track subscript read access on parameters
2795
- this.walkExpressionForSubscriptAccess(funcName, paramSet, expr);
2796
- }
2797
-
2798
- // 3. Recurse into child statements and blocks
2799
- const { statements, blocks } = ChildStatementCollector.collectAll(stmt);
2800
- for (const childStmt of statements) {
2801
- this.walkStatementForModifications(funcName, paramSet, childStmt);
2802
- }
2803
- for (const block of blocks) {
2804
- this.walkBlockForModifications(funcName, [...paramSet], block);
2805
- }
2806
- }
2807
-
2808
- /**
2809
- * Track assignment modifications for parameter const inference.
2810
- * SonarCloud S3776: Extracted from walkStatementForModifications().
2811
- */
2812
- private trackAssignmentModifications(
2813
- funcName: string,
2814
- paramSet: Set<string>,
2815
- stmt: Parser.StatementContext,
2816
- ): void {
2817
- const assign = stmt.assignmentStatement()!;
2818
- const target = assign.assignmentTarget();
2819
-
2820
- const { baseIdentifier, hasSingleIndexSubscript } =
2821
- AssignmentTargetExtractor.extract(target);
2822
-
2823
- // Issue #579: Track subscript access on parameters (for write path)
2824
- if (
2825
- hasSingleIndexSubscript &&
2826
- baseIdentifier &&
2827
- paramSet.has(baseIdentifier)
2828
- ) {
2829
- CodeGenState.subscriptAccessedParameters
2830
- .get(funcName)!
2831
- .add(baseIdentifier);
2832
- }
2833
-
2834
- // Track as modified parameter
2835
- if (baseIdentifier && paramSet.has(baseIdentifier)) {
2836
- CodeGenState.modifiedParameters.get(funcName)!.add(baseIdentifier);
2837
- }
2838
- }
2839
-
2840
- /**
2841
- * Walk an expression tree to find function calls where parameters are passed.
2842
- * Uses recursive descent through the expression hierarchy.
2843
- */
2844
- private walkExpressionForCalls(
2845
- funcName: string,
2846
- paramSet: Set<string>,
2847
- expr: Parser.ExpressionContext,
2848
- ): void {
2849
- // Expression -> TernaryExpression -> OrExpression -> ... -> PostfixExpression
2850
- const ternary = expr.ternaryExpression();
2851
- if (ternary) {
2852
- // Walk all orExpression children
2853
- for (const orExpr of ternary.orExpression()) {
2854
- this.walkOrExpressionForCalls(funcName, paramSet, orExpr);
2855
- }
2856
- }
2857
- }
2858
-
2859
- /**
2860
- * Issue #579: Walk an expression tree to find subscript access on parameters.
2861
- * This tracks read access like `buf[i]` where buf is a parameter.
2862
- * Parameters with subscript access must become pointers.
2863
- */
2864
- private walkExpressionForSubscriptAccess(
2865
- funcName: string,
2866
- paramSet: Set<string>,
2867
- expr: Parser.ExpressionContext,
2868
- ): void {
2869
- const ternary = expr.ternaryExpression();
2870
- if (ternary) {
2871
- for (const orExpr of ternary.orExpression()) {
2872
- this.walkOrExpression(orExpr, (unaryExpr) => {
2873
- this.handleSubscriptAccess(funcName, paramSet, unaryExpr);
2874
- });
2875
- }
2876
- }
2877
- }
2878
-
2879
- /**
2880
- * Issue #579: Handle subscript access on a unary expression.
2881
- * Only tracks single-index subscript access (which could be array access).
2882
- * Two-index subscript (e.g., value[start, width]) is always bit extraction,
2883
- * so it doesn't require the parameter to become a pointer.
2884
- */
2885
- private handleSubscriptAccess(
2886
- funcName: string,
2887
- paramSet: Set<string>,
2888
- unaryExpr: Parser.UnaryExpressionContext,
2889
- ): void {
2890
- const postfixExpr = unaryExpr.postfixExpression();
2891
- if (!postfixExpr) return;
2892
-
2893
- const primary = postfixExpr.primaryExpression();
2894
- const ops = postfixExpr.postfixOp();
2895
-
2896
- // Check if primary is a parameter and there's subscript access
2897
- const primaryId = primary.IDENTIFIER()?.getText();
2898
- if (!primaryId || !paramSet.has(primaryId)) {
2899
- return;
2900
- }
2901
-
2902
- // Only track SINGLE-index subscript access (potential array access)
2903
- // Two-index subscript like value[0, 8] is bit extraction, not array access
2904
- const hasSingleIndexSubscript = ops.some(
2905
- (op) => op.expression().length === 1,
2906
- );
2907
- if (hasSingleIndexSubscript) {
2908
- CodeGenState.subscriptAccessedParameters.get(funcName)!.add(primaryId);
2909
- }
2910
- }
2911
-
2912
- /**
2913
- * Generic walker for orExpression trees.
2914
- * Walks through the expression hierarchy and calls the handler for each unaryExpression.
2915
- * Used by both function call tracking and subscript access tracking.
2916
- */
2917
- private walkOrExpression(
2918
- orExpr: Parser.OrExpressionContext,
2919
- handler: (unaryExpr: Parser.UnaryExpressionContext) => void,
2920
- ): void {
2921
- orExpr
2922
- .andExpression()
2923
- .flatMap((and) => and.equalityExpression())
2924
- .flatMap((eq) => eq.relationalExpression())
2925
- .flatMap((rel) => rel.bitwiseOrExpression())
2926
- .flatMap((bor) => bor.bitwiseXorExpression())
2927
- .flatMap((bxor) => bxor.bitwiseAndExpression())
2928
- .flatMap((band) => band.shiftExpression())
2929
- .flatMap((shift) => shift.additiveExpression())
2930
- .flatMap((add) => add.multiplicativeExpression())
2931
- .flatMap((mul) => mul.unaryExpression())
2932
- .forEach(handler);
2933
- }
2934
-
2935
- /**
2936
- * Walk an orExpression tree for function calls.
2937
- */
2938
- private walkOrExpressionForCalls(
2939
- funcName: string,
2940
- paramSet: Set<string>,
2941
- orExpr: Parser.OrExpressionContext,
2942
- ): void {
2943
- this.walkOrExpression(orExpr, (unaryExpr) => {
2944
- this.walkUnaryExpressionForCalls(funcName, paramSet, unaryExpr);
2945
- });
2946
- }
2947
-
2948
- /**
2949
- * Walk a unaryExpression tree for function calls.
2950
- */
2951
- private walkUnaryExpressionForCalls(
2952
- funcName: string,
2953
- paramSet: Set<string>,
2954
- unaryExpr: Parser.UnaryExpressionContext,
2955
- ): void {
2956
- // Recurse into nested unary
2957
- if (unaryExpr.unaryExpression()) {
2958
- this.walkUnaryExpressionForCalls(
2959
- funcName,
2960
- paramSet,
2961
- unaryExpr.unaryExpression()!,
2962
- );
2963
- return;
2964
- }
2965
-
2966
- // Check postfix expression
2967
- const postfix = unaryExpr.postfixExpression();
2968
- if (postfix) {
2969
- this.walkPostfixExpressionForCalls(funcName, paramSet, postfix);
2970
- }
2971
- }
2972
-
2973
- /**
2974
- * Walk a postfixExpression for function calls.
2975
- * This is where function calls are found: primaryExpr followed by '(' args ')'
2976
- */
2977
- private walkPostfixExpressionForCalls(
2978
- funcName: string,
2979
- paramSet: Set<string>,
2980
- postfix: Parser.PostfixExpressionContext,
2981
- ): void {
2982
- const primary = postfix.primaryExpression();
2983
- const postfixOps = postfix.postfixOp();
2984
-
2985
- // Handle simple function calls: IDENTIFIER followed by '(' ... ')'
2986
- this.handleSimpleFunctionCall(funcName, paramSet, primary, postfixOps);
2987
-
2988
- // Issue #365: Handle scope-qualified calls: Scope.method(...) or global.Scope.method(...)
2989
- this.handleScopeQualifiedCalls(funcName, paramSet, primary, postfixOps);
2990
-
2991
- // Recurse into primary expression if it's a parenthesized expression
2992
- if (primary.expression()) {
2993
- this.walkExpressionForCalls(funcName, paramSet, primary.expression()!);
2994
- }
2995
-
2996
- // Walk arguments in any postfix function call ops (for nested calls)
2997
- this.walkPostfixOpsRecursively(funcName, paramSet, postfixOps);
2998
- }
2999
-
3000
- /**
3001
- * Handle simple function calls: IDENTIFIER followed by '(' ... ')'
3002
- */
3003
- private handleSimpleFunctionCall(
3004
- funcName: string,
3005
- paramSet: Set<string>,
3006
- primary: Parser.PrimaryExpressionContext,
3007
- postfixOps: Parser.PostfixOpContext[],
3008
- ): void {
3009
- if (!primary.IDENTIFIER() || postfixOps.length === 0) return;
3010
-
3011
- const firstOp = postfixOps[0];
3012
- if (!firstOp.LPAREN()) return;
3013
-
3014
- const calleeName = primary.IDENTIFIER()!.getText();
3015
- this.recordCallsFromArgList(funcName, paramSet, calleeName, firstOp);
3016
- }
3017
-
3018
- /**
3019
- * Handle scope-qualified calls: Scope.method(...) or global.Scope.method(...)
3020
- * Track member accesses to build the mangled callee name (e.g., Storage_load)
3021
- */
3022
- private handleScopeQualifiedCalls(
3023
- funcName: string,
3024
- paramSet: Set<string>,
3025
- primary: Parser.PrimaryExpressionContext,
3026
- postfixOps: Parser.PostfixOpContext[],
3027
- ): void {
3028
- if (postfixOps.length === 0) return;
3029
-
3030
- const memberNames = this.collectInitialMemberNames(funcName, primary);
3031
-
3032
- for (const op of postfixOps) {
3033
- if (op.IDENTIFIER()) {
3034
- memberNames.push(op.IDENTIFIER()!.getText());
3035
- } else if (op.LPAREN() && memberNames.length >= 1) {
3036
- const calleeName = memberNames.join("_");
3037
- this.recordCallsFromArgList(funcName, paramSet, calleeName, op);
3038
- memberNames.length = 0; // Reset for potential chained calls
3039
- } else if (op.expression().length > 0) {
3040
- memberNames.length = 0; // Array subscript breaks scope chain
3041
- }
3042
- }
3043
- }
3044
-
3045
- /**
3046
- * Collect initial member names from primary expression for scope resolution.
3047
- * Issue #561: When 'this' is used, resolve to the current scope name from funcName.
3048
- */
3049
- private collectInitialMemberNames(
3050
- funcName: string,
3051
- primary: Parser.PrimaryExpressionContext,
3052
- ): string[] {
3053
- const memberNames: string[] = [];
3054
- const primaryId = primary.IDENTIFIER()?.getText();
3055
-
3056
- if (primaryId && primaryId !== "global") {
3057
- memberNames.push(primaryId);
3058
- } else if (primary.THIS()) {
3059
- const scopeName = funcName.split("_")[0];
3060
- if (scopeName && scopeName !== funcName) {
3061
- memberNames.push(scopeName);
3062
- }
3063
- }
3064
- return memberNames;
3065
- }
3066
-
3067
- /**
3068
- * Record function calls to the call graph from an argument list.
3069
- * Also recurses into argument expressions.
3070
- */
3071
- private recordCallsFromArgList(
3072
- funcName: string,
3073
- paramSet: Set<string>,
3074
- calleeName: string,
3075
- op: Parser.PostfixOpContext,
3076
- ): void {
3077
- const argList = op.argumentList();
3078
- if (!argList) return;
3079
-
3080
- const args = argList.expression();
3081
- for (let i = 0; i < args.length; i++) {
3082
- const arg = args[i];
3083
- const argName = ExpressionUtils.extractIdentifier(arg);
3084
- if (argName && paramSet.has(argName)) {
3085
- CodeGenState.functionCallGraph.get(funcName)!.push({
3086
- callee: calleeName,
3087
- paramIndex: i,
3088
- argParamName: argName,
3089
- });
3090
- }
3091
- this.walkExpressionForCalls(funcName, paramSet, arg);
3092
- }
3093
- }
3094
-
3095
- /**
3096
- * Walk postfix ops recursively for nested calls and array subscripts.
3097
- */
3098
- private walkPostfixOpsRecursively(
3099
- funcName: string,
3100
- paramSet: Set<string>,
3101
- postfixOps: Parser.PostfixOpContext[],
3102
- ): void {
3103
- for (const op of postfixOps) {
3104
- if (op.argumentList()) {
3105
- for (const argExpr of op.argumentList()!.expression()) {
3106
- this.walkExpressionForCalls(funcName, paramSet, argExpr);
3107
- }
3108
- }
3109
- for (const expr of op.expression()) {
3110
- this.walkExpressionForCalls(funcName, paramSet, expr);
3111
- }
3112
- }
3113
- }
3114
-
3115
- /**
3116
- * Phase 3: Determine which parameters can pass by value.
3117
- * A parameter passes by value if:
3118
- * 1. It's a small primitive type (u8, i8, u16, i16, u32, i32, u64, i64, bool)
3119
- * 2. It's not modified (directly or transitively)
3120
- * 3. It's not an array, struct, string, or callback
3121
- */
3122
- private computePassByValueParams(): void {
3123
- const smallPrimitives = new Set([
3124
- "u8",
3125
- "i8",
3126
- "u16",
3127
- "i16",
3128
- "u32",
3129
- "i32",
3130
- "u64",
3131
- "i64",
3132
- "bool",
3133
- ]);
3134
-
3135
- for (const [funcName, paramNames] of CodeGenState.functionParamLists) {
3136
- const passByValue = new Set<string>();
3137
- const modified =
3138
- CodeGenState.modifiedParameters.get(funcName) ?? new Set();
3139
-
3140
- // Get function declaration to check parameter types
3141
- const funcSig = CodeGenState.functionSignatures.get(funcName);
3142
- if (funcSig) {
3143
- for (let i = 0; i < paramNames.length; i++) {
3144
- const paramName = paramNames[i];
3145
- const paramSig = funcSig.parameters[i];
3146
-
3147
- if (!paramSig) continue;
3148
-
3149
- // Check if eligible for pass-by-value:
3150
- // - Is a small primitive type
3151
- // - Not an array
3152
- // - Not modified
3153
- // - Not accessed via subscript (Issue #579)
3154
- const isSmallPrimitive = smallPrimitives.has(paramSig.baseType);
3155
- const isArray = paramSig.isArray ?? false;
3156
- const isModified = modified.has(paramName);
3157
- // Issue #579: Parameters with subscript access must become pointers
3158
- const hasSubscriptAccess =
3159
- CodeGenState.subscriptAccessedParameters
3160
- .get(funcName)
3161
- ?.has(paramName) ?? false;
3162
-
3163
- if (
3164
- isSmallPrimitive &&
3165
- !isArray &&
3166
- !isModified &&
3167
- !hasSubscriptAccess
3168
- ) {
3169
- passByValue.add(paramName);
3170
- }
3171
- }
3172
- }
3173
-
3174
- CodeGenState.passByValueParams.set(funcName, passByValue);
3175
- }
3176
- }
3177
-
3178
- /**
3179
- * Check if a parameter should be passed by value (by name).
3180
- * Used internally during code generation.
3181
- */
3182
- private _isParameterPassByValueByName(
3183
- funcName: string,
3184
- paramName: string,
3185
- ): boolean {
3186
- const passByValue = CodeGenState.passByValueParams.get(funcName);
3187
- return passByValue?.has(paramName) ?? false;
3188
- }
2622
+ // Issue #269: Pass-by-value analysis extracted to PassByValueAnalyzer
3189
2623
 
3190
2624
  /**
3191
2625
  * Issue #269: Check if a parameter should be passed by value (by index).
3192
2626
  * Part of IOrchestrator interface - used by CallExprGenerator.
2627
+ * Delegates to PassByValueAnalyzer.
3193
2628
  */
3194
2629
  isParameterPassByValue(funcName: string, paramIndex: number): boolean {
3195
- const paramList = CodeGenState.functionParamLists.get(funcName);
3196
- if (!paramList || paramIndex < 0 || paramIndex >= paramList.length) {
3197
- return false;
3198
- }
3199
- const paramName = paramList[paramIndex];
3200
- return this._isParameterPassByValueByName(funcName, paramName);
2630
+ return PassByValueAnalyzer.isParameterPassByValue(funcName, paramIndex);
3201
2631
  }
3202
2632
 
3203
2633
  /**
@@ -3256,7 +2686,6 @@ export default class CodeGenerator implements IOrchestrator {
3256
2686
  // ADR-017: Check if this is an enum type
3257
2687
  if (
3258
2688
  TypeRegistrationUtils.tryRegisterEnumType(
3259
- CodeGenState.typeRegistry,
3260
2689
  CodeGenState.symbols!,
3261
2690
  registrationOptions,
3262
2691
  )
@@ -3268,7 +2697,6 @@ export default class CodeGenerator implements IOrchestrator {
3268
2697
  const bitmapDimensions = this._evaluateArrayDimensions(arrayDim);
3269
2698
  if (
3270
2699
  TypeRegistrationUtils.tryRegisterBitmapType(
3271
- CodeGenState.typeRegistry,
3272
2700
  CodeGenState.symbols!,
3273
2701
  registrationOptions,
3274
2702
  bitmapDimensions,
@@ -3335,7 +2763,7 @@ export default class CodeGenerator implements IOrchestrator {
3335
2763
  const allDims =
3336
2764
  additionalDims.length > 0 ? [...additionalDims, stringDim] : [stringDim];
3337
2765
 
3338
- CodeGenState.typeRegistry.set(registryName, {
2766
+ CodeGenState.setVariableTypeInfo(registryName, {
3339
2767
  baseType: "char",
3340
2768
  bitWidth: 8,
3341
2769
  isArray: true,
@@ -3501,14 +2929,14 @@ export default class CodeGenerator implements IOrchestrator {
3501
2929
  )
3502
2930
  ) {
3503
2931
  // Enum/bitmap was registered, but we need to update with arrayType dimension
3504
- const existingInfo = CodeGenState.typeRegistry.get(registryName);
2932
+ const existingInfo = CodeGenState.getVariableTypeInfo(registryName);
3505
2933
  if (existingInfo) {
3506
2934
  const arrayTypeDim =
3507
2935
  this._parseArrayTypeDimensionFromCtx(arrayTypeCtx);
3508
2936
  const allDims = arrayTypeDim
3509
2937
  ? [arrayTypeDim, ...(existingInfo.arrayDimensions ?? [])]
3510
2938
  : existingInfo.arrayDimensions;
3511
- CodeGenState.typeRegistry.set(registryName, {
2939
+ CodeGenState.setVariableTypeInfo(registryName, {
3512
2940
  ...existingInfo,
3513
2941
  isArray: true,
3514
2942
  arrayDimensions: allDims,
@@ -3530,7 +2958,7 @@ export default class CodeGenerator implements IOrchestrator {
3530
2958
  arrayDim,
3531
2959
  );
3532
2960
 
3533
- CodeGenState.typeRegistry.set(registryName, {
2961
+ CodeGenState.setVariableTypeInfo(registryName, {
3534
2962
  baseType,
3535
2963
  bitWidth,
3536
2964
  isArray: true,
@@ -3609,7 +3037,7 @@ export default class CodeGenerator implements IOrchestrator {
3609
3037
  ? this._evaluateArrayDimensions(arrayDim)
3610
3038
  : undefined;
3611
3039
 
3612
- CodeGenState.typeRegistry.set(registryName, {
3040
+ CodeGenState.setVariableTypeInfo(registryName, {
3613
3041
  baseType,
3614
3042
  bitWidth,
3615
3043
  isArray,
@@ -3862,7 +3290,7 @@ export default class CodeGenerator implements IOrchestrator {
3862
3290
  stringCapacity,
3863
3291
  isParameter: true,
3864
3292
  };
3865
- CodeGenState.typeRegistry.set(name, registeredType);
3293
+ CodeGenState.setVariableTypeInfo(name, registeredType);
3866
3294
  }
3867
3295
 
3868
3296
  /**
@@ -3899,13 +3327,10 @@ export default class CodeGenerator implements IOrchestrator {
3899
3327
  private _clearParameters(): void {
3900
3328
  // ADR-025: Remove parameter types from typeRegistry
3901
3329
  for (const name of CodeGenState.currentParameters.keys()) {
3902
- CodeGenState.typeRegistry.delete(name);
3903
- CodeGenState.typeRegistry.delete(name);
3330
+ CodeGenState.deleteVariableTypeInfo(name);
3904
3331
  }
3905
3332
  CodeGenState.currentParameters.clear();
3906
3333
  CodeGenState.localArrays.clear();
3907
- CodeGenState.currentParameters.clear();
3908
- CodeGenState.localArrays.clear();
3909
3334
  }
3910
3335
 
3911
3336
  /**
@@ -4120,7 +3545,7 @@ export default class CodeGenerator implements IOrchestrator {
4120
3545
  const sourceName = sourceId.getText();
4121
3546
 
4122
3547
  // Check if source is a string type
4123
- const typeInfo = CodeGenState.typeRegistry.get(sourceName);
3548
+ const typeInfo = CodeGenState.getVariableTypeInfo(sourceName);
4124
3549
  if (!typeInfo?.isString || typeInfo.stringCapacity === undefined) {
4125
3550
  return null;
4126
3551
  }
@@ -4307,7 +3732,7 @@ export default class CodeGenerator implements IOrchestrator {
4307
3732
  baseId: string,
4308
3733
  targetParamBaseType: string,
4309
3734
  ): boolean {
4310
- const typeInfo = CodeGenState.typeRegistry.get(baseId);
3735
+ const typeInfo = CodeGenState.getVariableTypeInfo(baseId);
4311
3736
  return CppMemberHelper.needsComplexMemberConversion(
4312
3737
  this._toPostfixOps(ops),
4313
3738
  typeInfo,
@@ -4334,7 +3759,7 @@ export default class CodeGenerator implements IOrchestrator {
4334
3759
  const baseId = primary.IDENTIFIER()?.getText();
4335
3760
  if (!baseId) return false;
4336
3761
 
4337
- const typeInfo = CodeGenState.typeRegistry.get(baseId);
3762
+ const typeInfo = CodeGenState.getVariableTypeInfo(baseId);
4338
3763
  const paramInfo = CodeGenState.currentParameters.get(baseId);
4339
3764
 
4340
3765
  return CppMemberHelper.isStringSubscriptPattern(
@@ -4388,7 +3813,7 @@ export default class CodeGenerator implements IOrchestrator {
4388
3813
  // 2. Parameter: currentParameters.get(baseId).baseType
4389
3814
  let structType: string | undefined;
4390
3815
 
4391
- const typeInfo = CodeGenState.typeRegistry.get(baseId);
3816
+ const typeInfo = CodeGenState.getVariableTypeInfo(baseId);
4392
3817
  if (typeInfo) {
4393
3818
  structType = typeInfo.baseType;
4394
3819
  } else {
@@ -4455,7 +3880,7 @@ export default class CodeGenerator implements IOrchestrator {
4455
3880
 
4456
3881
  // Global arrays also decay to pointers (check typeRegistry)
4457
3882
  // But NOT strings - strings need & (they're char arrays but passed by reference)
4458
- const typeInfo = CodeGenState.typeRegistry.get(id);
3883
+ const typeInfo = CodeGenState.getVariableTypeInfo(id);
4459
3884
  if (typeInfo?.isArray && !typeInfo.isString) {
4460
3885
  return id;
4461
3886
  }
@@ -5325,7 +4750,10 @@ export default class CodeGenerator implements IOrchestrator {
5325
4750
  // Small unmodified primitives
5326
4751
  if (
5327
4752
  CodeGenState.currentFunctionName &&
5328
- this._isParameterPassByValueByName(CodeGenState.currentFunctionName, name)
4753
+ PassByValueAnalyzer.isParameterPassByValueByName(
4754
+ CodeGenState.currentFunctionName,
4755
+ name,
4756
+ )
5329
4757
  ) {
5330
4758
  return true;
5331
4759
  }
@@ -5784,14 +5212,14 @@ export default class CodeGenerator implements IOrchestrator {
5784
5212
  const argName = argNode.getText();
5785
5213
 
5786
5214
  // Check if it exists in type registry
5787
- const typeInfo = CodeGenState.typeRegistry.get(argName);
5215
+ const typeInfo = CodeGenState.getVariableTypeInfo(argName);
5788
5216
 
5789
5217
  // Also check scoped variables if inside a scope
5790
5218
  let scopedArgName = argName;
5791
5219
  let scopedTypeInfo = typeInfo;
5792
5220
  if (!typeInfo && CodeGenState.currentScope) {
5793
5221
  scopedArgName = `${CodeGenState.currentScope}_${argName}`;
5794
- scopedTypeInfo = CodeGenState.typeRegistry.get(scopedArgName);
5222
+ scopedTypeInfo = CodeGenState.getVariableTypeInfo(scopedArgName);
5795
5223
  }
5796
5224
 
5797
5225
  if (!typeInfo && !scopedTypeInfo) {
@@ -5815,7 +5243,7 @@ export default class CodeGenerator implements IOrchestrator {
5815
5243
  }
5816
5244
 
5817
5245
  // Track the variable in type registry (as an external C++ type)
5818
- CodeGenState.typeRegistry.set(name, {
5246
+ CodeGenState.setVariableTypeInfo(name, {
5819
5247
  baseType: type,
5820
5248
  bitWidth: 0, // Unknown for C++ types
5821
5249
  isArray: false,
@@ -6055,7 +5483,7 @@ export default class CodeGenerator implements IOrchestrator {
6055
5483
  // ADR-109: Dispatch to assignment handlers
6056
5484
  // Build context, classify, and dispatch - all patterns handled by handlers
6057
5485
  const assignCtx = buildAssignmentContext(ctx, {
6058
- typeRegistry: CodeGenState.typeRegistry,
5486
+ typeRegistry: CodeGenState.getTypeRegistryView(),
6059
5487
  generateExpression: () => value,
6060
5488
  generateAssignmentTarget: (targetCtx) =>
6061
5489
  this.generateAssignmentTarget(targetCtx),