All files / transpiler/output/codegen/generators/expressions CallExprUtils.ts

100% Statements 21/21
93.75% Branches 15/16
100% Functions 7/7
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                          16x               20x               13x                 81x               9x                   8x 8x               5x 5x                               109x 109x 97x     12x 6x 6x 7x 3x   3x   3x     3x                         9x          
/**
 * Pure utility functions for function call expression generation.
 * Extracted from CallExprGenerator for testability (Issue #420).
 */
import TYPE_MAP from "../../types/TYPE_MAP";
import IFunctionSignature from "../../types/IFunctionSignature";
import SymbolTable from "../../../../logic/symbols/SymbolTable";
import TypeResolver from "../../../../../utils/TypeResolver";
 
/**
 * Issue #315: Small primitive types that are always passed by value.
 * These match the types used in Issue #269 for pass-by-value optimization.
 */
const SMALL_PRIMITIVE_TYPES = new Set(["u8", "u16", "i8", "i16", "bool"]);
 
class CallExprUtils {
  /**
   * Issue #304: Map C-Next type to C type for static_cast.
   * Returns the input unchanged if not a known C-Next primitive type.
   */
  static mapTypeToCType(cnxType: string): string {
    return TYPE_MAP[cnxType] || cnxType;
  }
 
  /**
   * Issue #315: Check if a type is a small primitive that should be passed by value.
   * Used for cross-file function calls where modification info is unavailable.
   */
  static isSmallPrimitiveType(typeName: string): boolean {
    return SMALL_PRIMITIVE_TYPES.has(typeName);
  }
 
  /**
   * Issue #551: Check if a type is a known primitive type.
   * Known primitives use pass-by-reference (except floats and small types).
   * Unknown types (external enums, typedefs) use pass-by-value.
   */
  static isKnownPrimitiveType(typeName: string): boolean {
    return !!TYPE_MAP[typeName];
  }
 
  /**
   * Issue #551: Check if a type is a string type (string<N>).
   * String parameters use pass-by-reference (passed as char*).
   */
  static isStringType(typeName: string): boolean {
    return typeName.startsWith("string<") || typeName === "string";
  }
 
  /**
   * ADR-051: Generate the helper function name for safe_div/safe_mod.
   */
  static generateSafeDivModHelperName(
    funcName: "safe_div" | "safe_mod",
    cnxType: string,
  ): string {
    const op = funcName === "safe_div" ? "div" : "mod";
    return `cnx_safe_${op}_${cnxType}`;
  }
 
  /**
   * Issue #304: Generate a C++ static_cast expression.
   * Used for enum class to integer conversions.
   */
  static generateStaticCast(code: string, targetType: string): string {
    const cType = CallExprUtils.mapTypeToCType(targetType);
    return `static_cast<${cType}>(${code})`;
  }
 
  /**
   * Issue #315: Resolve target parameter info from local signature or cross-file SymbolTable.
   * Returns the parameter info and whether it came from a cross-file lookup.
   */
  static resolveTargetParam(
    sig: IFunctionSignature | undefined,
    paramIndex: number,
    funcName: string,
    symbolTable: SymbolTable | null,
  ): {
    param: IFunctionSignature["parameters"][0] | undefined;
    isCrossFile: boolean;
  } {
    const localParam = sig?.parameters[paramIndex];
    if (localParam) {
      return { param: localParam, isCrossFile: false };
    }
 
    if (symbolTable) {
      const symbols = symbolTable.getOverloads(funcName);
      for (const sym of symbols) {
        if (sym.kind === "function" && sym.parameters?.[paramIndex]) {
          const p = sym.parameters[paramIndex];
          // Convert TType to string for C-Next symbols, use string directly for C/C++
          const paramType = p.type;
          const baseType =
            typeof paramType === "string"
              ? paramType
              : TypeResolver.getTypeName(paramType);
          return {
            param: {
              name: p.name,
              baseType,
              isConst: p.isConst,
              isArray: p.isArray,
            },
            isCrossFile: true,
          };
        }
      }
    }
 
    return { param: undefined, isCrossFile: false };
  }
}
 
export default CallExprUtils;