All files / transpiler/output/codegen/assignment/handlers SpecialHandlers.ts

95.65% Statements 22/23
91.66% Branches 11/12
100% Functions 3/3
95.65% Lines 22/23

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                            14x                           40x     40x 34x       6x 3x 3x       3x 3x                           5x 5x   5x                         35x 35x     35x 2x     33x   33x 32x 32x       1x           14x            
/**
 * Special assignment handlers (ADR-109).
 *
 * Handles special compound assignment operations:
 * - ATOMIC_RMW: atomic counter +<- 1
 * - OVERFLOW_CLAMP: clamp u8 saturated +<- 200
 */
import AssignmentKind from "../AssignmentKind";
import IAssignmentContext from "../IAssignmentContext";
import IHandlerDeps from "./IHandlerDeps";
import TypeCheckUtils from "../../../../../utils/TypeCheckUtils";
import TAssignmentHandler from "./TAssignmentHandler";
 
/** Maps C operators to clamp helper operation names */
const CLAMP_OP_MAP: Record<string, string> = {
  "+=": "add",
  "-=": "sub",
  "*=": "mul",
};
 
/**
 * Get typeInfo for assignment target.
 * Handles simple identifiers, this.member, and global.member patterns.
 */
function getTargetTypeInfo(
  ctx: IAssignmentContext,
  deps: IHandlerDeps,
): { typeInfo: ReturnType<typeof deps.typeRegistry.get> } {
  const id = ctx.identifiers[0];
 
  // Simple identifier
  if (ctx.isSimpleIdentifier) {
    return { typeInfo: deps.typeRegistry.get(id) };
  }
 
  // this.member: lookup using scoped name
  if (ctx.isSimpleThisAccess && deps.currentScope) {
    const scopedName = `${deps.currentScope}_${id}`;
    return { typeInfo: deps.typeRegistry.get(scopedName) };
  }
 
  // global.member: lookup using direct name
  Eif (ctx.isSimpleGlobalAccess) {
    return { typeInfo: deps.typeRegistry.get(id) };
  }
 
  // Fallback to direct lookup
  return { typeInfo: deps.typeRegistry.get(id) };
}
 
/**
 * Handle atomic read-modify-write: atomic counter +<- 1
 *
 * Delegates to CodeGenerator's generateAtomicRMW which uses
 * LDREX/STREX on supported platforms or PRIMASK otherwise.
 */
function handleAtomicRMW(ctx: IAssignmentContext, deps: IHandlerDeps): string {
  const { typeInfo } = getTargetTypeInfo(ctx, deps);
  const target = deps.generateAssignmentTarget(ctx.targetCtx);
 
  return deps.generateAtomicRMW(target, ctx.cOp, ctx.generatedValue, typeInfo!);
}
 
/**
 * Handle overflow-clamped compound assignment: clamp u8 saturated +<- 200
 *
 * Generates calls to cnx_clamp_add_u8, cnx_clamp_sub_u8, etc.
 * Only applies to integers (floats use native C arithmetic with infinity).
 */
function handleOverflowClamp(
  ctx: IAssignmentContext,
  deps: IHandlerDeps,
): string {
  const { typeInfo } = getTargetTypeInfo(ctx, deps);
  const target = deps.generateAssignmentTarget(ctx.targetCtx);
 
  // Floats use native C arithmetic (overflow to infinity)
  if (TypeCheckUtils.usesNativeArithmetic(typeInfo!.baseType)) {
    return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
  }
 
  const helperOp = CLAMP_OP_MAP[ctx.cOp];
 
  if (helperOp) {
    deps.markClampOpUsed(helperOp, typeInfo!.baseType);
    return `${target} = cnx_clamp_${helperOp}_${typeInfo!.baseType}(${target}, ${ctx.generatedValue});`;
  }
 
  // Fallback for operators without clamp helpers (e.g., /=)
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * All special handlers for registration.
 */
const specialHandlers: ReadonlyArray<[AssignmentKind, TAssignmentHandler]> = [
  [AssignmentKind.ATOMIC_RMW, handleAtomicRMW],
  [AssignmentKind.OVERFLOW_CLAMP, handleOverflowClamp],
];
 
export default specialHandlers;