Class Scenario2WrapperHelper

java.lang.Object
org.ek9lang.compiler.phase7.Scenario2WrapperHelper

final class Scenario2WrapperHelper extends Object
Shared utilities for Scenario 2 wrapper IR generation.

Provides common operations needed by all Scenario 2 specialist generators:

  • Temporary variable and scope ID generation
  • Type name resolution from Ek9Types
  • Operator name mapping (symbol to method name)
  • Type erasure for delegation (finding base method parameter/return types)
  • Basic block creation
  • Delegate field load pattern
  • Field Details

  • Constructor Details

    • Scenario2WrapperHelper

      Scenario2WrapperHelper(Ek9Types ek9Types, IRContext baseContext, PossibleGenericSymbol parameterizedAggregate)
      One helper per wrapper.
      Parameters:
      ek9Types - the built-in types
      baseContext - the generation's IR context, copied for each operation's names
      parameterizedAggregate - the parameterised type the wrapper is generated for
  • Method Details

    • beginOperation

      void beginOperation()
      Begin generating one wrapper operation: its temps, scope ids and labels are numbered afresh, exactly as IRGenerationContext.enterMethodScope numbers every other operation's.
    • generateTempName

      String generateTempName()
    • generateScopeId

      String generateScopeId(String prefix)
      A scope id of the operation: _scope_<name>_<n> (ShouldRegisterVariableInScope keys on the _scope_ form), the operator's method name for an operator.
    • generateLabelName

      String generateLabelName(String prefix)
      A label of the operation: _<prefix>_<n>.
    • toMethodName

      String toMethodName(String operatorOrMethodName)
      Convert operator symbols to their mapped method names for use in IR.
    • createDebugInfo

      DebugInfo createDebugInfo()
    • createBasicBlock

      BasicBlockInstr createBasicBlock(List<IRInstr> instructions)
    • typeName

      String typeName(ISymbol type)
      THE IR name of a type in a Scenario-2 wrapper: its fully qualified name. Scenario 2 never runs under a generic type mapping (the wrapper is generated for one concrete parameterisation), so the name needs no resolving.
    • getBooleanTypeName

      String getBooleanTypeName()
    • getVoidTypeName

      String getVoidTypeName()
    • getStringTypeName

      String getStringTypeName()
    • getIntegerTypeName

      String getIntegerTypeName()
    • isAny

      boolean isAny(ISymbol type)
      Is this the built-in Any (by symbol identity, never by name)?
    • getAnyTypeName

      String getAnyTypeName()
    • isVoid

      boolean isVoid(ISymbol type)
      Is this the built-in Void (by symbol identity)? An absent type is a void return.
    • isThisWrapper

      boolean isThisWrapper(ISymbol type)
      Is this exactly THIS wrapper's type (parameterised wrappers are closed: a different parameterisation of the same generic is a different type)?
    • rawDelegateTypeName

      String rawDelegateTypeName(PossibleGenericSymbol parameterizedType)
      The RAW type a built-in parameterised value is held as - its generic (List of Integer -> List).
    • loadDelegateOf

      String loadDelegateOf(List<IRInstr> instructions, String wrapperVar, String wrapperType, String delegateFieldType, String scopeId, DebugInfo debugInfo)
      THE delegate load of the Scenario-2 wrappers: a wrapper value's delegate field, borrowed for the scope (DelegateLoad, shared with the constrained types).
        delegateVar = LOAD_FIELD wrapperVar.delegate (wrapperType -> delegateType)
        borrowed in scopeId
      
    • loadDelegate

      String loadDelegate(List<IRInstr> instructions, String scopeId, DebugInfo debugInfo)
      This wrapper's own delegate (this.delegate), borrowed for the scope.
    • loadDelegate

      String loadDelegate(List<IRInstr> instructions, String wrapperVar, String scopeId, DebugInfo debugInfo)
      The delegate of wrapperVar, a value of THIS wrapper's type, borrowed for the scope.
    • unwrapDelegate

      String unwrapDelegate(List<IRInstr> instructions, String wrapperVar, PossibleGenericSymbol parameterizedType, String scopeId, DebugInfo debugInfo)
      Load the raw delegate out of a built-in parameterised wrapper value (List of Integer -> its List), for a base-type call that takes the raw type, borrowed for the scope.
    • rewrapRawDelegate

      String rewrapRawDelegate(List<IRInstr> instructions, String rawVar, PossibleGenericSymbol wrapper, String scopeId, DebugInfo debugInfo)
      THE wrap of a RAW delegate value back into its built-in parameterised wrapper (the inverse of unwrapDelegate(List, String, PossibleGenericSymbol, String, DebugInfo)): wrapper(rawVar), the new wrapper owned by the scope. A built-in container holds a parameterised element as its raw delegate, so it is wrapped again wherever it is handed out as the element type - the callback edge and the _call(Any...) bridge.
    • castBorrowed

      String castBorrowed(List<IRInstr> instructions, String value, String typeName, String scopeId, DebugInfo debugInfo)
      A value this scope does not own, CAST to typeName and the cast borrowed for the scope (a parameter given as Any).
    • borrow

      void borrow(List<IRInstr> instructions, String value, String scopeId, DebugInfo debugInfo)
      A value this scope does not own - a LOAD of a field or parameter, or a CAST alias of a value owned elsewhere - borrowed for the scope: OwnershipStatements.borrowInScope(String, String, DebugInfo).
    • owned

      void owned(List<IRInstr> instructions, String value, String scopeId, DebugInfo debugInfo)
      A value just made (+1) that this scope owns and drops at its exit: OwnershipStatements.droppedAtExit(String, String, DebugInfo).
    • ownedCast

      String ownedCast(List<IRInstr> instructions, String value, String typeName, String scopeId, DebugInfo debugInfo)
      A value just made (+1), cast to typeName: the CAST takes the value's count (V-D) and the scope owns the cast, dropping it at exit - one count, one drop, whichever name holds the object.
    • returnFresh

      void returnFresh(List<IRInstr> instructions, String value, DebugInfo debugInfo)
      A value just made (+1), moved into the return variable (empty here): OwnershipStatements.returnFresh(String, String, DebugInfo) - the caller receives the producer's count.
    • storeDelegateField

      void storeDelegateField(List<IRInstr> instructions, String value, DebugInfo debugInfo)
      The wrapper's delegate field, its first store in a constructor: the field takes its own reference to a value the constructor's scope still owns (FIELD_ASSIGN [copy, init]).
    • castWhenThisWrapper

      String castWhenThisWrapper(List<IRInstr> instructions, String operandName, String notThisWrapperLabel, String scopeId, DebugInfo debugInfo)
      THE test of an operand declared Any for being exactly THIS wrapper, and its cast when it is - shared by wrapper equality (Scenario2EqualityGenerator) and every other operator the generic declares as taking Any (Scenario2AggregateWrapperGenerator, D60: DictEntry <=> Any). EXACT: parameterised wrappers are closed, so a different parameterisation of the same generic is a different type and is NOT this wrapper. The callers differ only in where a value that is not this wrapper goes (notThisWrapperLabel). This wrapper's form of THE shared WrapperGivenAsAny.castWhenExactly(List, Supplier, String, String, String, String, DebugInfo).
        isWrapper = INSTANCE_OF_EXACT operand, wrapperType
        BRANCH_FALSE isWrapper, notThisWrapperLabel
        casted = CAST operand -> wrapperType
        RETAIN casted
        SCOPE_REGISTER casted, scopeId
      
      Returns:
      the variable holding the operand cast to this wrapper (valid only on the fall-through path)
    • generateConstructorCallInstructions

      String generateConstructorCallInstructions(List<IRInstr> instructions, ISymbol type, String typeName, List<String> paramTypes, List<String> argNames, String scopeId, DebugInfo debugInfo)
      Generate a constructor call on a type: the new value (+1) is owned by the scope, dropped at its exit. A caller that keeps it states that itself - a field store copies it, a return moves it. A GENERATED wrapping constructor takes its own reference to its argument, as a PRE_BUILT one does, so the argument needs no extra count.
    • generateConstructorCall

      String generateConstructorCall(List<IRInstr> instructions, ISymbol type, boolean externallyLinked, String typeName, List<String> paramTypes, List<String> argNames, DebugInfo debugInfo)
      A constructor call whose new value (+1) the caller states the owner of itself (it moves it).
    • returnVirtualCall

      void returnVirtualCall(List<IRInstr> instructions, String targetVar, String targetType, ISymbol targetTypeSymbol, String methodName, List<String> paramTypes, String returnType, List<String> argNames, String scopeId, DebugInfo debugInfo)
      A virtual method call whose result (+1) is moved straight into the return variable: the method hands its caller exactly the count the callee handed it. (A RETAIN as a "borrow" of the result and the return's own RETAIN handed it back at +2 - a leak on every call of a function wrapper's _call, _eq and _isSet.)
    • generateProducerVirtualCall

      String generateProducerVirtualCall(List<IRInstr> instructions, String targetVar, String targetType, ISymbol targetTypeSymbol, String methodName, List<String> paramTypes, String returnType, List<String> argNames, String scopeId, DebugInfo debugInfo)
      Generate a virtual method call whose result is a +1 PRODUCER — a value-method or operator that hands back a freshly-owned object (e.g. Result.isOk() returns a NEW Boolean; Result.ok() ek9_retains the stored value before returning, so the caller receives it at +1). Per the uniform +1 ARC ownership-transfer convention (THE authority: VariableMemoryManagement — a CALL result is owned), the scope owns it and its exit consumes the +1. An additional RETAIN (the borrow form) hands the value back at +2, so the single scope-exit release leaves +1 and leaks it once per invocation. Use this for internal producer call results consumed within the method body; a result that IS the method's return goes through returnVirtualCall(List, String, String, ISymbol, String, List, String, List, String, DebugInfo).
    • emitVirtualCall

      String emitVirtualCall(List<IRInstr> instructions, String targetVar, String targetType, ISymbol targetTypeSymbol, String methodName, List<String> paramTypes, String returnType, List<String> argNames, DebugInfo debugInfo)
      A virtual method call whose result (+1) the caller states the owner of itself - it casts it (ownedCast(List, String, String, String, DebugInfo)) or moves it.
    • extractPrimitiveBoolean

      String extractPrimitiveBoolean(List<IRInstr> instructions, String booleanVar, DebugInfo debugInfo)
      Extract primitive boolean from Boolean object via _true() method.
    • storeUnsetBooleanReturn

      void storeUnsetBooleanReturn(List<IRInstr> instructions, String scopeId, DebugInfo debugInfo)
      An unset Boolean as the return value: made, and moved into the return variable (empty here).
    • generateUnsetBoolean

      String generateUnsetBoolean(List<IRInstr> instructions, String scopeId, DebugInfo debugInfo)
      Generate an unset Boolean constructor call.
    • containedTypeHasOperator

      boolean containedTypeHasOperator(ISymbol type, String operatorGlyph)
      Whether the contained (element) type has a REAL binary operatorGlyph (==, <>) - its own, a super's or a trait's, never only the degenerate Any one. THE test is HasRealOperator; this once scanned the element's own scope only, so an INHERITED == made the wrapper's == a constant UNSET (B18 prerequisite P1).
    • delegatesBaseEquality

      boolean delegatesBaseEquality()
      Whether a parameterised wrapper's equality should DELEGATE to the base type's own _eq rather than extract-and-compare the contained value.

      Collections (List/Dict/PriorityQueue) compare element-wise and MutexLock compares by IDENTITY — in both cases the base type's own _eq already yields the correct wrapper semantics, so we delegate. Only the single-value containers (Optional, Result) need value-extraction, because their _eq compares the contained value via Java equals() (which bypasses the EK9 contained type's _eq). MutexLock additionally exposes NO value accessor (no get()), so the extraction path would emit a call to a non-existent MutexLock.get() — fine on the JVM (the method is never invoked) but an undefined-symbol LINK failure on the eagerly-linked native backend.

    • baseIsResult

      boolean baseIsResult()
      True iff the wrapped base generic is Result — its _eq compares the contained value via Java equals(), so the wrapper must value-extract rather than delegate. Symbol identity.
    • baseIsIterator

      boolean baseIsIterator()
      True iff the wrapped base generic is Iterator, whose ? is "can it give a next item": the wrapper's own hasNext(), dispatched, never the delegate's - a type extending the wrapper has no meaningful delegate, and its own hasNext() is the answer. Symbol identity.
    • containerAccess

      Scenario2WrapperHelper.ContainerAccess containerAccess(String forMethod)
      The presence check and accessor to use for the wrapper method forMethod.
    • baseIsDictEntry

      boolean baseIsDictEntry()
      True iff the wrapped base generic is DictEntry, whose no-argument constructor must supply an unset V (decision 11, docs/language-design/EK9_ABSENCE_UNOBSERVABLE.md). Symbol identity.
    • hasPublicNoArgConstructor

      boolean hasPublicNoArgConstructor(ISymbol type)
      Whether type() can be called here to make an unset value of it: a concrete aggregate with a public no-argument constructor. False for a trait, an abstract type, a function, or a type whose no-argument constructor is private or absent (FE39) - none of those has an unset value a wrapper can make.
    • getBaseMethodParameterTypes

      List<String> getBaseMethodParameterTypes(MethodSymbol method)
      Get the parameter types from the BASE generic type's method, not the substituted version.
    • getBaseMethodReturnType

      String getBaseMethodReturnType(MethodSymbol method)
      Get the return type from the BASE generic type's method, not the substituted version.
    • isFunctionParameterisation

      boolean isFunctionParameterisation(ISymbol type)
      THE shared check for "is this type a parameterisation of a generic FUNCTION" (e.g. Supplier of String, Comparator of T) — use this in the Scenario-2 generators rather than re-deriving the instanceof chain.
    • isDelegatingParameterisedType

      boolean isDelegatingParameterisedType(ISymbol type)
      THE shared check for "is this a parameterised BUILT-IN generic that carries a raw delegate" — one whose value must be UNWRAPPED via .delegate on store and RE-WRAPPED on return / at a callback edge. True for Scenario-1 (pre-built) and Scenario-2 (extern delegation) wrappers; FALSE for a user-defined (Scenario-3) generic, whose monomorphised class IS the value and has NO delegate field. Unwrapping a Scenario-3 value via .delegate throws NoSuchFieldError, and re-wrapping it needs a wrapping constructor it does not have. Every unwrap/re-wrap gate in the Scenario-2 generators must use THIS rather than bare isParameterisedType() (which is true for all three scenarios), so a built-in container over a user generic element stores/retrieves it directly.
    • isBareConceptualOnTemplate

      boolean isBareConceptualOnTemplate(ISymbol templateDeclaredType)
      True when the generic template declared this position as a bare conceptual type parameter (T), as opposed to a parameterisation such as Iterator of T or Comparator of T. Mirrors the discrimination used by getBaseMethodParameterTypes/getBaseMethodReturnType.
    • findOriginalMethodOnGenericTemplate

      MethodSymbol findOriginalMethodOnGenericTemplate(MethodSymbol method)
      The generic template's method a wrapper method was cloned from - READ what phase 6 encoded, never re-derived. TypeSubstitution clones the template's methods one-for-one and records the correspondence on each clone (setGenericTemplateMethod), and every Scenario-2 wrapper method is such a clone (the hidden _sinkBuffer() records a stand-in). A method without one is a compiler defect, reported as one: the shape comparison that used to stand behind this could not separate +=(T) from +=(List of T) once T is substituted, and it never ran (MONO-12: proved by this throw over compiler-main and -Pfuzz before it went).