Class ResourceCloser

java.lang.Object
org.ek9lang.compiler.phase7.generator.AbstractGenerator
org.ek9lang.compiler.phase7.generator.ResourceCloser

public final class ResourceCloser extends AbstractGenerator
THE compiler-generated close: the one place IR for closing a resource is built, so every route that closes something names the same owner, the same method, the same metadata and the same failure rule.

The failure rule (shape B). Each _close() runs in its own protected region (the JLS 14.20.3.1 nested-try shape), so one close that throws never stops the others. The FIRST EK9 Exception is the one that propagates once every close has run: the body's when the body failed (it is caught into the region's first-failure variable, never replaced), otherwise the first close's. Every later close failure is attached to it as SUPPRESSED (Ek9CloseFailures.suppress). A Panic is not an EK9 Exception: no region here catches it, so a Panic in a close aborts the closes that remain, and a Panic in the body still runs them (they are a finally) and then propagates unchanged - the first failure is only rethrown AFTER the region, on a path a pending Panic never reaches, so a close failure cannot replace a Panic.

The null guard is what makes a partial acquisition safe: a value that was never produced (an earlier call threw) is still null and is skipped (Contract 3).

  • Constructor Details

  • Method Details

    • newFirstFailure

      public ResourceCloser.FirstFailure newFirstFailure()
      A new first-failure variable for one region. Declare it (declareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)) before the region - for try-with-resources before the resource scope, beside the resources' own declarations.
    • declareFirstFailure

      public IRInstr declareFirstFailure(ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo)
      REFERENCE firstFailure, org.ek9.lang::Exception - null until a failure is recorded.
    • closeCall

      public IRInstr closeCall(ResourceCloser.Closing closing)
      CALL variable._close() on the type that declares close.
    • closingRegion

      public List<IRInstr> closingRegion(List<IRInstr> body, List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo)
      THE owned region: body, then - on the normal path, after an EK9 Exception, and while a Panic unwinds - every value in closings that is present is closed, last first, each in its own protected region; then the first EK9 Exception (the body's, else the first close's) is rethrown with the later close failures suppressed onto it.
        SCOPE_ENTER chain_scope
        CONTROL_FLOW_CHAIN TRY_CATCH_FINALLY
          try:     SCOPE_ENTER try_scope, body..., SCOPE_EXIT try_scope
          catch:   -> _t as org.ek9.lang::Exception          (never a Panic)
                   STORE firstFailure, _t
          finally: per closing, last first:
                     load..., IS_NULL / BRANCH_TRUE skip
                     TRY_CATCH_FINALLY  try: CALL value._close()   catch -> _ex: record _ex   (see below)
                     LABEL skip
        SCOPE_EXIT chain_scope
        IS_NULL firstFailure / BRANCH_TRUE done / THROW firstFailure / LABEL done
      
      Recording a close failure: the first one is stored into firstFailure; a later one is CALL_STATIC Ek9CloseFailures.suppress(firstFailure, _ex). The rethrow sits AFTER the region: a pending Panic leaves through the finally's own rethrow and never reaches it.

      The caller declares firstFailure beforehand (declareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)); the region registers it to the scope that is current when this is called. body must not contain the try scope's own enter/exit - the region adds them.

    • closeEach

      public List<IRInstr> closeEach(List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo)
      closingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo) with no body: every value that is present closed, last first, each in its own protected region, then the first close failure rethrown with the later ones suppressed onto it. With nothing to protect there is no outer region (a default operator close).
    • failureRegion

      public List<IRInstr> failureRegion(List<IRInstr> body, List<ResourceCloser.Closing> closings, ResourceCloser.FirstFailure firstFailure, DebugInfo debugInfo)
      The closes of closingRegion(List, List, ResourceCloser.FirstFailure, DebugInfo), run only when body fails with an EK9 Exception - which is then rethrown with any close failure suppressed onto it. A Panic is not caught: it leaves with nothing closed.
        SCOPE_ENTER chain_scope
        CONTROL_FLOW_CHAIN TRY_CATCH_FINALLY
          try:     SCOPE_ENTER try_scope, body..., SCOPE_EXIT try_scope
          catch:   -> _t as org.ek9.lang::Exception
                   STORE firstFailure, _t
        SCOPE_EXIT chain_scope
        IS_NULL firstFailure / BRANCH_TRUE done
        per closing, last first: load..., IS_NULL / BRANCH_TRUE skip, protected CALL value._close(), LABEL skip
        THROW firstFailure
        LABEL done
      
      The caller declares firstFailure beforehand (declareFirstFailure(ResourceCloser.FirstFailure, DebugInfo)).