Class SwitchRetrofittingTaskListener

java.lang.Object
com.github.bsideup.jabel.SwitchRetrofittingTaskListener
All Implemented Interfaces:
TaskListener

public class SwitchRetrofittingTaskListener extends Object implements TaskListener
Transforms modern switch constructs (Java 17+) into Java 8 compatible code.


This listener will rewrite entirely the switch for a better one.

Instead of placing the guard in the case body, it will be evaluated in the switch condition.
Avoiding restarting the switch everytimes a guard fail, and storing an index to not reevaluate previous cases.

So this method will convert cases labels into a ternary-chain that will placed in the switch condition.
And case labels will be changed to match their position in the switch.

Also, to avoid double method calls, if the switch condition was a method call, an uninitialized variable is inserted in the previous line, and then initialized in the ternary-chain.
Same way goes for cases using a record-pattern with a guard using a component from that record.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    class 
     
    static class 
    TreeCopier that preserves types and symbols.
  • Constructor Summary

    Constructors
    Constructor
    Description
    SwitchRetrofittingTaskListener(com.sun.tools.javac.util.Context context)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    addBindings(com.sun.tools.javac.tree.JCTree.JCCase caseTree, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCStatement> out)
    Emits binding variable declarations at the top of a case body.
    com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCStatement>
    buildCaseBody(com.sun.tools.javac.tree.JCTree cSwitch, com.sun.tools.javac.tree.JCTree.JCCase c, com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean expression)
     
    com.sun.tools.javac.tree.JCTree.JCExpression
    buildEnumSwitchCondition(com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean hasNull, com.sun.tools.javac.tree.JCTree.JCExpression rawSel)
    Builds a simple sel == null ? -1 : sel.ordinal() condition.
    com.sun.tools.javac.tree.JCTree.JCExpression
    buildGuard(com.sun.tools.javac.tree.JCTree label, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.tree.JCTree.JCCase src)
    Returns the guard expression for a pattern slot (with binding substitution), or null if none.
    void
    buildGuardPreDecls(com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCStatement> out)
    Pre-declares variables for record components referenced in a guard.
    com.sun.tools.javac.tree.JCTree.JCExpression
    buildLabelCondition(com.sun.tools.javac.tree.JCTree label, com.sun.tools.javac.tree.JCTree.JCExpression sel)
     
    com.sun.tools.javac.tree.JCTree.JCExpression
    buildTypeSwitchCondition(com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean hasNull, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> labels, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> slotCases, com.sun.tools.javac.tree.JCTree.JCExpression rawSel)
    Builds a ternary chain and groups the same type-pattern.
    com.sun.tools.javac.tree.JCTree.JCExpression
    captureSelector(com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCStatement> prefix, boolean init)
     
    Map<com.sun.tools.javac.util.Name, com.sun.tools.javac.tree.JCTree.JCExpression>
    collectBindings(com.sun.tools.javac.tree.JCTree label, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.tree.JCTree.JCCase caseTree)
     
    void
    extractRecordBindings(com.sun.tools.javac.util.List<? extends com.sun.tools.javac.tree.JCTree> nested, com.sun.tools.javac.tree.JCTree.JCExpression baseAccessor, com.sun.tools.javac.util.List<com.sun.tools.javac.util.Name> componentNames, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCVariableDecl> out)
    Recursively extract bindings from nested record pattern components.
    com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree>
    findPatternSwitches(com.sun.tools.javac.tree.JCTree node)
     
    void
     
    int
    getEnumOrdinal(com.sun.tools.javac.tree.JCTree.JCExpression expr)
     
    com.sun.tools.javac.util.List<com.sun.tools.javac.util.Name>
    getRecordComponentNames(com.sun.tools.javac.tree.JCTree pattern)
     
    com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase>
    injectDefault(com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases)
    Injects default: throw new IncompatibleClassChangeError(...) if no default exists.
    com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase>
    prepareSwichCases(com.sun.tools.javac.tree.JCTree currentSwitch, com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean isExpression, boolean isEnum, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> labels)
     
    void
     
    com.sun.tools.javac.tree.JCTree.JCSwitch
    transformSwitch(com.sun.tools.javac.tree.JCTree currentSwitch, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases, boolean expression, com.sun.tools.javac.tree.JCTree.JCExpression rawSel)
    Builds the standard switch from a pattern/null switch.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • SwitchRetrofittingTaskListener

      public SwitchRetrofittingTaskListener(com.sun.tools.javac.util.Context context)
  • Method Details

    • started

      public void started(TaskEvent e)
      Specified by:
      started in interface TaskListener
    • finished

      public void finished(TaskEvent e)
      Specified by:
      finished in interface TaskListener
    • findPatternSwitches

      public com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> findPatternSwitches(com.sun.tools.javac.tree.JCTree node)
    • transformSwitch

      public com.sun.tools.javac.tree.JCTree.JCSwitch transformSwitch(com.sun.tools.javac.tree.JCTree currentSwitch, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases, boolean expression, com.sun.tools.javac.tree.JCTree.JCExpression rawSel)
      Builds the standard switch from a pattern/null switch.
    • prepareSwichCases

      public com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> prepareSwichCases(com.sun.tools.javac.tree.JCTree currentSwitch, com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean isExpression, boolean isEnum, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> labels)
    • buildEnumSwitchCondition

      public com.sun.tools.javac.tree.JCTree.JCExpression buildEnumSwitchCondition(com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean hasNull, com.sun.tools.javac.tree.JCTree.JCExpression rawSel)
      Builds a simple sel == null ? -1 : sel.ordinal() condition.
    • buildTypeSwitchCondition

      public com.sun.tools.javac.tree.JCTree.JCExpression buildTypeSwitchCondition(com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean hasNull, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> labels, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> slotCases, com.sun.tools.javac.tree.JCTree.JCExpression rawSel)
      Builds a ternary chain and groups the same type-pattern.
    • getEnumOrdinal

      public int getEnumOrdinal(com.sun.tools.javac.tree.JCTree.JCExpression expr)
    • captureSelector

      public com.sun.tools.javac.tree.JCTree.JCExpression captureSelector(com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCStatement> prefix, boolean init)
    • buildCaseBody

      public com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCStatement> buildCaseBody(com.sun.tools.javac.tree.JCTree cSwitch, com.sun.tools.javac.tree.JCTree.JCCase c, com.sun.tools.javac.tree.JCTree.JCExpression sel, boolean expression)
    • buildGuard

      public com.sun.tools.javac.tree.JCTree.JCExpression buildGuard(com.sun.tools.javac.tree.JCTree label, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.tree.JCTree.JCCase src)
      Returns the guard expression for a pattern slot (with binding substitution), or null if none.
    • buildLabelCondition

      public com.sun.tools.javac.tree.JCTree.JCExpression buildLabelCondition(com.sun.tools.javac.tree.JCTree label, com.sun.tools.javac.tree.JCTree.JCExpression sel)
    • injectDefault

      public com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> injectDefault(com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases)
      Injects default: throw new IncompatibleClassChangeError(...) if no default exists.
    • getRecordComponentNames

      public com.sun.tools.javac.util.List<com.sun.tools.javac.util.Name> getRecordComponentNames(com.sun.tools.javac.tree.JCTree pattern)
    • collectBindings

      public Map<com.sun.tools.javac.util.Name, com.sun.tools.javac.tree.JCTree.JCExpression> collectBindings(com.sun.tools.javac.tree.JCTree label, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.tree.JCTree.JCCase caseTree)
    • addBindings

      public void addBindings(com.sun.tools.javac.tree.JCTree.JCCase caseTree, com.sun.tools.javac.tree.JCTree.JCExpression sel, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCStatement> out)
      Emits binding variable declarations at the top of a case body.
    • extractRecordBindings

      public void extractRecordBindings(com.sun.tools.javac.util.List<? extends com.sun.tools.javac.tree.JCTree> nested, com.sun.tools.javac.tree.JCTree.JCExpression baseAccessor, com.sun.tools.javac.util.List<com.sun.tools.javac.util.Name> componentNames, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCVariableDecl> out)
      Recursively extract bindings from nested record pattern components.

      Generates variable declarations like: final Point $tmp = parent.b(); final int bx = $tmp.x(); final int by = $tmp.y();

    • buildGuardPreDecls

      public void buildGuardPreDecls(com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCCase> cases, com.sun.tools.javac.util.ListBuffer<com.sun.tools.javac.tree.JCTree.JCStatement> out)
      Pre-declares variables for record components referenced in a guard.
      These are assigned inline in the ternary-chain via collectBindings(JCTree, JCTree.JCExpression, JCTree.JCCase), and aliased in the case body via addBindings(JCTree.JCCase, JCTree.JCExpression, ListBuffer).