All files / TRANSPILE/3-Render/codegen/assignment/handlers AccessPatternHandlers.ts

90% Statements 9/10
75% Branches 3/4
100% Functions 2/2
90% Lines 9/10

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                                                                10x     10x 10x                           14x   14x   2x       12x 12x               49x                
/**
 * Access pattern assignment handlers (ADR-065).
 *
 * Handles assignments with global/this prefix and member chains:
 * - GLOBAL_ARRAY: global.obj.field[i] <- value (member chain)
 * - GLOBAL_MEMBER: global.Counter.value <- 5
 * - THIS_MEMBER: this.count <- 5
 * - MEMBER_CHAIN: struct.field.subfield <- value
 */
import AssignmentKind from "../../../../../types/AssignmentKind";
import IAssignmentContext from "../../../../2-Plan/types/IAssignmentContext";
import AssignmentHandlerUtils from "./AssignmentHandlerUtils";
import TAssignmentHandler from "./TAssignmentHandler";
 
/**
 * Emission for a qualified access target: `global.Counter.value <- 5`,
 * `global.obj.field[i] <- v`, `this.count <- 5`.
 *
 * #1322: there were two functions here, one per qualifier, and they became
 * byte-identical when the checks they differed by moved to pass 2.1 --
 * cross-scope visibility is E0435/E0436, and `this` outside a scope is E0431.
 * What was left was one expression written twice, with the `this` copy still
 * documented as "validates scope context", which it no longer did.
 *
 * The three assignment KINDS stay distinct: the classifier tells them apart,
 * and a future rule may need to. What is shared is the emission, and it is
 * shared by being one function rather than by two that happen to agree.
 */
function handleQualifiedAccess(ctx: IAssignmentContext): string {
  // #1668 review: a qualified chain can end in bits too, `global.gp.bits[3]`;
  // this emitted `gp.bits[3] = true;`, which C rejects. The same decision and
  // the same write as any member chain.
  Iif (ctx.analyzeTargetForBitAccess().isBitAccess) {
    return AssignmentHandlerUtils.writeBits(ctx);
  }
  const target = ctx.renderTarget();
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * Handle member chain: struct.field.subfield <- value
 *
 * This is the catch-all for complex member access patterns
 * that don't match more specific handlers.
 *
 * Special case: Detects bit access at the end of chain
 * (e.g., grid[2][3].flags[0] <- true) and generates RMW.
 */
function handleMemberChain(ctx: IAssignmentContext): string {
  // Check if this is bit access on a struct member
  const bitAnalysis = ctx.analyzeTargetForBitAccess();
 
  if (bitAnalysis.isBitAccess) {
    // #1322: compound assignment on this target is E0857 in pass 2.1.
    return AssignmentHandlerUtils.writeBits(ctx);
  }
 
  // Normal member chain assignment
  const target = ctx.renderTarget();
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * All access pattern handlers for registration.
 */
const accessPatternHandlers: ReadonlyArray<
  [AssignmentKind, TAssignmentHandler]
> = [
  [AssignmentKind.GLOBAL_MEMBER, handleQualifiedAccess],
  [AssignmentKind.GLOBAL_ARRAY, handleQualifiedAccess],
  [AssignmentKind.THIS_MEMBER, handleQualifiedAccess],
  [AssignmentKind.MEMBER_CHAIN, handleMemberChain],
];
 
export default accessPatternHandlers;