Class CoverageProbePlacer

java.lang.Object
org.ek9lang.compiler.phase7.generation.CoverageProbePlacer

public final class CoverageProbePlacer extends Object
Places coverage probes at control flow points during IR generation.

Coverage probes are inserted at:

  • Function/method entry (FUNCTION_ENTRY)
  • If-then branch taken (BRANCH_TRUE)
  • Else/else-if branch taken (BRANCH_FALSE)
  • Switch case entry (SWITCH_CASE)
  • Catch block entry (CATCH_BLOCK)
  • Loop body entry (LOOP_BODY)

Each probe has a globally unique ID (GUID) generated from a deterministic FNV-1a 64-bit hash of its context (module, file, line, type, function). This ensures uniqueness across all source files without requiring coordination during parallel compilation.

Note: BLOCK and FUNCTION_EXIT probes are intentionally omitted as they are redundant in EK9 - explicit branch tracking already identifies paths taken, and EK9 functions have exactly one exit point (no return statement).

  • Constructor Details

    • CoverageProbePlacer

      public CoverageProbePlacer(CompilerFlags compilerFlags, long moduleId, String moduleName, boolean isDevSource)
      Create a coverage probe placer for a module.
      Parameters:
      compilerFlags - Compiler flags to check if coverage is enabled
      moduleId - Unique identifier for this module (unused, kept for API compatibility)
      moduleName - Human-readable module name
      isDevSource - True if this is dev/test source code (excluded from coverage)
  • Method Details

    • isCoverageEnabled

      public boolean isCoverageEnabled()
      Check if coverage instrumentation is enabled. Returns false for dev/test sources - like JaCoCo, we only measure coverage of production code, not the test harness code itself.
    • isProfilingEnabled

      public boolean isProfilingEnabled()
      Check if profiling instrumentation is enabled. Returns false for dev/test sources (same exclusion as coverage).
    • isInputVarietyEnabled

      public boolean isInputVarietyEnabled()
      Check if input-variety numerator instrumentation is enabled.

      The dynamic numerator (design §5) rides the coverage-instrumented -t run, so it is enabled exactly when coverage is (and, like coverage, excludes dev/test sources — the probe instruments the production construct being tested, not the test harness). This keeps every -t run producing the measurement with no extra flag; a dedicated flag / packaging gate is a later concern (§8).

    • isAssertWindowEnabled

      public boolean isAssertWindowEnabled()
      Check if the §5.3 assert-window markers should be emitted — and note this deliberately does not defer to isInputVarietyEnabled().

      The entry probe measures the production construct, so it is (correctly) suppressed in dev/test sources. The assert window is the exact inverse: an assert statement lives only in a dev/test source (a production assertion is E81012), so gating the markers on the probe's predicate would emit them nowhere and pin asserted at zero forever. The window is therefore enabled whenever the build is coverage-instrumented, dev source or not.

    • registerFileReadability

      public void registerFileReadability(String sourceFile, String readabilityScore)
      Register a file's readability score.

      ARI (Automated Readability Index) is calculated during parsing and provides a grade-level indicator of code readability. This is unique to EK9 - no competitor offers source code readability analysis.

      Parameters:
      sourceFile - Source file path
      readabilityScore - ARI score string (e.g., "10-11 Fifth Grade")
    • addFieldInjections

      public void addFieldInjections(int count)
      Accumulate field-level injection count from a component. Field injections are declared as properties with '!' (e.g., logger as Logger!).
      Parameters:
      count - Number of field-level injections in this component
    • addRegistrations

      public void addRegistrations(int count)
      Accumulate registration count from an application. Registrations are 'register' statements that offer components for injection.
      Parameters:
      count - Number of register statements in this application
    • enterFunction

      public void enterFunction(String functionName, String simpleName, String sourceFile, int startLine)
      Enter a function/method context for probe tracking. Call this when starting to generate IR for a function/method.
      Parameters:
      functionName - Fully qualified function/method name
      simpleName - Simple function/method name (e.g., "_add")
      sourceFile - Source file containing this function
      startLine - Starting line number of the function
    • enterFunction

      public void enterFunction(String functionName, String simpleName, String sourceFile, int startLine, FunctionMetrics metrics)
      Enter a function/method context for probe tracking with metrics. Call this when starting to generate IR for a function/method.
      Parameters:
      functionName - Fully qualified function/method name
      simpleName - Simple function/method name (e.g., "_add")
      sourceFile - Source file containing this function
      startLine - Starting line number of the function
      metrics - Code quality metrics from Phase 5
    • setSignatureEndLine

      public void setSignatureEndLine(int signatureEndLine)
      Set the end line of the function signature.

      The signature includes the function name, parameters (->), and return declaration (<-). This allows HTML coverage reports to color all signature lines consistently based on the FUNCTION_ENTRY coverage status.

      Parameters:
      signatureEndLine - Last line of the function signature (after return declaration)
    • exitFunction

      public void exitFunction(int endLine)
      Exit the current function/method context. Records function-level coverage metadata.
      Parameters:
      endLine - Ending line number of the function body
    • createFunctionEntryProbe

      public IRInstr createFunctionEntryProbe(DebugInfo debugInfo)
      Create a function entry probe instruction.
      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createBranchTrueProbe

      public IRInstr createBranchTrueProbe(DebugInfo debugInfo)
      Create a branch-true (if/then) probe instruction.
      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createBranchFalseProbe

      public IRInstr createBranchFalseProbe(DebugInfo debugInfo)
      Create a branch-false (else) probe instruction.
      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createSwitchCaseProbe

      public IRInstr createSwitchCaseProbe(String caseLabel, DebugInfo debugInfo)
      Create a switch case probe instruction.
      Parameters:
      caseLabel - Label or description of the case (for logging purposes)
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createCatchBlockProbe

      public IRInstr createCatchBlockProbe(String exceptionType, DebugInfo debugInfo)
      Create a catch block probe instruction.
      Parameters:
      exceptionType - Type of exception being caught (for logging purposes)
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createLoopBodyProbe

      public IRInstr createLoopBodyProbe(DebugInfo debugInfo)
      Create a loop body probe instruction.
      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createFinallyBlockProbe

      public IRInstr createFinallyBlockProbe(DebugInfo debugInfo)
      Create a finally block probe instruction. Tracks execution of explicit finally blocks only.

      Note: Synthetic finally blocks (from try-with-resources without explicit finally) should NOT be probed because the close() method's FUNCTION_ENTRY already proves cleanup happened.

      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createStatementProbe

      public IRInstr createStatementProbe(DebugInfo debugInfo)
      Create a statement probe instruction. Tracks execution of individual statements for fine-grained coverage.

      Unlike edge-based tools that infer coverage, EK9 directly measures each statement execution. This ensures accurate coverage even when exceptions are thrown mid-block, and requires zero coverage logic in backends.

      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage probe instruction, or null if coverage disabled
    • createModuleRegistration

      public IRInstr createModuleRegistration(DebugInfo debugInfo)
      Create a module registration instruction. Should be emitted once per module in static initializer.
      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage register instruction
    • createCoverageFinalization

      public IRInstr createCoverageFinalization(DebugInfo debugInfo)
      Create a coverage finalization instruction. Should be emitted in the main program's shutdown hook or finally block.
      Parameters:
      debugInfo - Source location information
      Returns:
      Coverage finalize instruction
    • createProfilingEntryProbe

      public IRInstr createProfilingEntryProbe(DebugInfo debugInfo)
      Create a profiling entry probe instruction. Uses the current function's ID and name (already tracked by enterFunction).
      Parameters:
      debugInfo - Source location information
      Returns:
      Profiling entry instruction, or null if profiling disabled
    • createProfilingExitProbe

      public IRInstr createProfilingExitProbe(DebugInfo debugInfo)
      Create a profiling exit probe instruction. Uses the current function's ID (already tracked by enterFunction).
      Parameters:
      debugInfo - Source location information
      Returns:
      Profiling exit instruction, or null if profiling disabled
    • createInputClassProbe

      public List<IRInstr> createInputClassProbe(ISymbol operationSymbol, DebugInfo debugInfo, String scopeId, IRGenerationContext stackContext)
      Create an input-class probe for a public construct's entry (the dynamic numerator, §5).

      In scope exactly when the phase-5 InputVarietyDenominatorRecorder squirrelled an INPUT_VARIETY_FLOOR on this callable — reusing that scoping decision rather than re-deriving genus/visibility rules (built-ins, entry shims and no-denominator constructs are thereby excluded, §7). When the recorder also counted the receiver as the implicit p0 (§4.5, marked by CommonValues.INPUT_VARIETY_RECEIVER) the probe emits a receiver cell at index 0 with the arguments following — so a stateful no-arg instance method (e.g. pop()) is still probed. The §5.3 result-into-assertion commit rule is a later iteration.

      Parameters:
      operationSymbol - the callable's symbol (carries the squirrelled scope signal + parameters)
      debugInfo - source location information
      Returns:
      the input-class probe, or null when disabled / out of scope / no dimensions
    • getProbeCount

      public int getProbeCount()
      Get the current probe count for this source file.
    • getModuleId

      public long getModuleId()
      Get the module ID (hash of module name).
    • getAllProbes

      public List<CoverageProbeDetails> getAllProbes()
      Get all probe details for this source file (for testing/verification).
    • getFunctionDetails

      public List<CoverageFunctionDetails> getFunctionDetails()
      Get function details for this source file (for testing/verification).
    • toMetadataJson

      public String toMetadataJson()
      Serialize probe and function metadata to JSON for runtime coverage computation.

      The JSON format is:

      {
        "probes": [
          {"id": "0x1234...", "type": "FUNCTION_ENTRY", "line": 10, "functionId": "0xABCD..."},
          {"id": "0x5678...", "type": "BRANCH_TRUE", "line": 15, "functionId": "0xABCD..."}
        ],
        "functions": [
          {"id": "0xABCD...", "name": "myFunction", "startLine": 10, "endLine": 20, "entryProbeId": "0x1234..."}
        ]
      }
      
      Returns:
      JSON string with coverage metadata, or null if no probes and no functions