New candidate JEP: 361: Switch Expressions (Standard)

JARvis PROgrammer mrjarviscraft at gmail.com
Fri Sep 27 21:05:04 UTC 2019


As you state that the feature is going to become a permanent one very soon,
then just brainstorming any possible additions to it which may be taken
into consideration as those which are not big enough for separate JEPs but
may come in handy as another small enhancements to the current proposal.
The following is not critical at all but might be a pleasant addition

Syntax like case *begin* .. *end*: might be allowed as a more user-friendly
literal for multiple cases of ∀ �� ∈ ([*begin*, *end*] ∧ Z) ⇔ case *begin*,
(*begin* + 1), (*begin* + 2) ... (*end* - 2), (*end* - 1), *end*:. This is
nothing but an alias for enumeration of all numbers in range and so this
just requires the compiler to be able to handle such literals in case
labels as equivalent explicit range enumerations. From point of generated
bytecode there of course are no differences (+ this form is usually
friendly towards tableswitch as the values are explicitly sequential).
One detail is that overlapping ranges may be allowed thus meaning that the
values of all the previous ones should be excluded. For sets of included
values declared as ranges (in order they appear as cases) A, B, C we
effectively have B \= A and C \= (A ∧ B).
Consider the example:

String result = switch (value) {
    case -1 .. 1 -> "Very small";
    case -4 .. 4 -> "Smalls";
    case 5 .. 8 -> "Up-to-8s";
    default -> "Bad guys";
};
System.out.println(result);

This is equivalent to

String result = switch (value) {
    case -1, 0,  1 -> "Very small";
    case -4, -3, -2, 2, 3,  4 -> "Smalls";
    case 5, 6, 7, 8 -> "Up-to-8s";
    default -> "Bad guys";
};
System.out.println(result);

For which the following bytecode gets generated:

 ILOAD 1 // push the value by which the switch happens onto the stack
 TABLESWITCH
        -4: L0
        -3: L0
        -2: L0
        -1: L1
         0: L1
         1: L1
         2: L0
         3: L0
         4: L0
         5: L2
         6: L2
         7: L2
         8: L2
   default: L3
L1 // "Very small" string value pushed onto the stack for [-1; 1] range
FRAME APPEND [I]
 LDC "Very small"
 GOTO L4
L0 // "Smalls" string value pushed onto the stack for [-4; -2] ∨ [2 ; 4] range
FRAME SAME
 LDC "Smalls"
 GOTO L4
L2 // "Up-to-8s" string value pushed onto the stack for [5; 8] range
FRAME SAME
 LDC "Up-to-8s"
 GOTO L4
L3 // "Bad guys" string value pushed for all other values
FRAME SAME
 LDC "Bad guys"
L4 // invoke the `System.out.println(String);` method passing the
selected String
FRAME SAME1 java/lang/String
 ASTORE 2
 GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
 ALOAD 2
 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V

Another point is that an optimization may be made in order to handle big
ranges as part of default branch + if_icmp.

As an example, consider the following switch-statement (method is an
instance of an example class Main.Method with virtual method
#addIntOpcode(Main.Opcode,
int) with Main.Opcode being an example enum with ICONST, BIPUSH, SIPUSH and
LDC constants):

method.addIntOpcode(switch (value) {
    case -1 .. 5 -> Opcode.ICONST;
    case Byte.MIN_VALUE .. Byte.MAX_VALUE -> Opcode.BIPUSH;
    case Short.MIN_VALUE .. Short.MAX_VALUE -> Opcode.SIPUSH;
    default: -> Opcode.LDC;
}, value);

Considering both notes, this is effectively identical to

method.addIntOpcode(switch (value) {
    case -1, 0, 1, 2, 3, 4, 5 -> Opcode.ICONST;
    default -> (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE) ?
Opcode.BIPUSH
            : (Short.MIN_VALUE <= value && value <= Short.MAX_VALUE) ?
Opcode.SIPUSH : Opcode.LDC;
}, value);

Which in bytecode looks like:

 ALOAD 1 // push Main$Method instance onto the stack
 ILOAD 2 // push the value by which the switch happens onto the stack
 TABLESWITCH
        -1: L0
         0: L0
         1: L0
         2: L0
         3: L0
         4: L0
         5: L0
   default: L1
L0 // ICONST enum value pushed onto the stack for [-1; 5] range
FRAME FULL [[Ljava/lang/String; Main$Method I] [Main$Method]
 GETSTATIC Main$Opcode.ICONST : LMain$Opcode;
 GOTO L2
L1 // BIPUSH enum value pushed onto the stack for [-128; -1) ∨ (5; 127] range
FRAME SAME1 Main$Method
 BIPUSH -128
 ILOAD 2
 IF_ICMPGT L3
 ILOAD 2
 BIPUSH 127
 IF_ICMPGT L3
 GETSTATIC Main$Opcode.BIPUSH : LMain$Opcode;
 GOTO L2
L3 // SIPUSH enum value pushed onto the stack for [-32768; -128) ∨
(127; 32767] range
FRAME SAME1 Main$Method
 SIPUSH -32768
 ILOAD 2
 IF_ICMPGT L4
 ILOAD 2
 SIPUSH 32767
 IF_ICMPGT L4
 GETSTATIC Main$Opcode.SIPUSH : LMain$Opcode;
 GOTO L2
L4
FRAME SAME1 Main$Method // LDC enum value pushed onto the stack for
[-2^31; -32768) ∨ (32767; 2^31-1] range
 GETSTATIC Main$Opcode.LDC : LMain$Opcode;
L2
FRAME FULL [[Ljava/lang/String; Main$Method I] [Main$Method Main$Opcode]
 ILOAD 2 // push the `value` local-variable's value onto the stack as
a method parameter
 INVOKEVIRTUAL Main$Method.addIntOpcode (LMain$Opcode;I)V // invoke
the very addIntOpcode method


Seems like that's all for this idea, wish that it gets implemented as while
not being a killer-feature it still is a way to reducs code-size making it
more obvious (which is one of the whole switch-expressions JEP) yet having
the most effective bytecode generated.

Excuse me if it is not what was meant by your request on feedback (just
inform me please about it in this case so that I don't continue generating
such ideas)

Thanks,
Peter
пт, 27 сент. 2019 г. в 19:47, Gavin Bierman <gavin.bierman at oracle.com>:

> Please note that we are considering making this a permanent feature, so
> this is your last chance to provide substantive feedback based on any new
> experience you may have had with this feature.
>
> A new, draft language spec for JEP 361 (Switch Expressions) is available
> at:
>
>
> http://cr.openjdk.java.net/~gbierman/jep361/jep361-20190927/specs/switch-expressions-jls.html
> <
> http://cr.openjdk.java.net/~gbierman/jep305/jep305-20190918/specs/patterns-instanceof-jls.html
> >
>
> This is identical to the version made available for JEP354, apart from
> some cosmetic changes around terminology following some feedback.
>
> [For spec nerds: The primary change is that what was previously called a
> "switch labeled rule" is now called, more simply, a "switch rule”. “Switch
> labeled expression” is now a “switch rule expression”, “switch labeled
> block” is now a “switch rule block” and a “switch labeled throw statement”
> is now a “switch rule throw statement”.]
>
> All feedback welcomed!
> Gavin
>
> > On 25 Sep 2019, at 23:32, mark.reinhold at oracle.com wrote:
> >
> > https://openjdk.java.net/jeps/361
> >
> > - Mark
>
>


More information about the amber-dev mailing list