2626package java .lang .runtime ;
2727
2828import java .lang .invoke .CallSite ;
29+ import java .lang .invoke .ConstantBootstraps ;
2930import java .lang .invoke .ConstantCallSite ;
3031import java .lang .invoke .MethodHandle ;
3132import java .lang .invoke .MethodHandles ;
3233import java .lang .invoke .MethodType ;
33- import java .util .Arrays ;
34- import java .util .Objects ;
3534import java .util .stream .Stream ;
3635
3736import jdk .internal .javac .PreviewFeature ;
@@ -53,12 +52,15 @@ private SwitchBootstraps() {}
5352
5453 private static final MethodHandles .Lookup LOOKUP = MethodHandles .lookup ();
5554
56- private static final MethodHandle DO_SWITCH ;
55+ private static final MethodHandle DO_TYPE_SWITCH ;
56+ private static final MethodHandle DO_ENUM_SWITCH ;
5757
5858 static {
5959 try {
60- DO_SWITCH = LOOKUP .findStatic (SwitchBootstraps .class , "doSwitch " ,
60+ DO_TYPE_SWITCH = LOOKUP .findStatic (SwitchBootstraps .class , "doTypeSwitch " ,
6161 MethodType .methodType (int .class , Object .class , int .class , Object [].class ));
62+ DO_ENUM_SWITCH = LOOKUP .findStatic (SwitchBootstraps .class , "doEnumSwitch" ,
63+ MethodType .methodType (int .class , Enum .class , int .class , Object [].class ));
6264 }
6365 catch (ReflectiveOperationException e ) {
6466 throw new ExceptionInInitializerError (e );
@@ -108,14 +110,13 @@ private SwitchBootstraps() {}
108110 * second parameter of type {@code int} and with {@code int} as its return type,
109111 * or if {@code labels} contains an element that is not of type {@code String},
110112 * {@code Integer} or {@code Class}.
111- * @throws Throwable if there is any error linking the call site
112113 * @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
113114 * @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
114115 */
115116 public static CallSite typeSwitch (MethodHandles .Lookup lookup ,
116117 String invocationName ,
117118 MethodType invocationType ,
118- Object ... labels ) throws Throwable {
119+ Object ... labels ) {
119120 if (invocationType .parameterCount () != 2
120121 || (!invocationType .returnType ().equals (int .class ))
121122 || invocationType .parameterType (0 ).isPrimitive ()
@@ -126,7 +127,7 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup,
126127 labels = labels .clone ();
127128 Stream .of (labels ).forEach (SwitchBootstraps ::verifyLabel );
128129
129- MethodHandle target = MethodHandles .insertArguments (DO_SWITCH , 2 , (Object ) labels );
130+ MethodHandle target = MethodHandles .insertArguments (DO_TYPE_SWITCH , 2 , (Object ) labels );
130131 return new ConstantCallSite (target );
131132 }
132133
@@ -142,7 +143,7 @@ private static void verifyLabel(Object label) {
142143 }
143144 }
144145
145- private static int doSwitch (Object target , int startIndex , Object [] labels ) {
146+ private static int doTypeSwitch (Object target , int startIndex , Object [] labels ) {
146147 if (target == null )
147148 return -1 ;
148149
@@ -167,4 +168,124 @@ private static int doSwitch(Object target, int startIndex, Object[] labels) {
167168 return labels .length ;
168169 }
169170
171+ /**
172+ * Bootstrap method for linking an {@code invokedynamic} call site that
173+ * implements a {@code switch} on a target of an enum type. The static
174+ * arguments are used to encode the case labels associated to the switch
175+ * construct, where each label can be encoded in two ways:
176+ * <ul>
177+ * <li>as a {@code String} value, which represents the name of
178+ * the enum constant associated with the label</li>
179+ * <li>as a {@code Class} value, which represents the enum type
180+ * associated with a type test pattern</li>
181+ * </ul>
182+ * <p>
183+ * The returned {@code CallSite}'s method handle will have
184+ * a return type of {@code int} and accepts two parameters: the first argument
185+ * will be an {@code Enum} instance ({@code target}) and the second
186+ * will be {@code int} ({@code restart}).
187+ * <p>
188+ * If the {@code target} is {@code null}, then the method of the call site
189+ * returns {@literal -1}.
190+ * <p>
191+ * If the {@code target} is not {@code null}, then the method of the call site
192+ * returns the index of the first element in the {@code labels} array starting from
193+ * the {@code restart} index matching one of the following conditions:
194+ * <ul>
195+ * <li>the element is of type {@code Class} that is assignable
196+ * from the target's class; or</li>
197+ * <li>the element is of type {@code String} and equals to the target
198+ * enum constant's {@link Enum#name()}.</li>
199+ * </ul>
200+ * <p>
201+ * If no element in the {@code labels} array matches the target, then
202+ * the method of the call site return the length of the {@code labels} array.
203+ *
204+ * @param lookup Represents a lookup context with the accessibility
205+ * privileges of the caller. When used with {@code invokedynamic},
206+ * this is stacked automatically by the VM.
207+ * @param invocationName unused
208+ * @param invocationType The invocation type of the {@code CallSite} with two parameters,
209+ * an enum type, an {@code int}, and {@code int} as a return type.
210+ * @param labels case labels - {@code String} constants and {@code Class} instances,
211+ * in any combination
212+ * @return a {@code CallSite} returning the first matching element as described above
213+ *
214+ * @throws NullPointerException if any argument is {@code null}
215+ * @throws IllegalArgumentException if any element in the labels array is null, if the
216+ * invocation type is not a method type whose first parameter type is an enum type,
217+ * second parameter of type {@code int} and whose return type is {@code int},
218+ * or if {@code labels} contains an element that is not of type {@code String} or
219+ * {@code Class} of the target enum type.
220+ * @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
221+ * @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
222+ */
223+ public static CallSite enumSwitch (MethodHandles .Lookup lookup ,
224+ String invocationName ,
225+ MethodType invocationType ,
226+ Object ... labels ) {
227+ if (invocationType .parameterCount () != 2
228+ || (!invocationType .returnType ().equals (int .class ))
229+ || invocationType .parameterType (0 ).isPrimitive ()
230+ || !invocationType .parameterType (0 ).isEnum ()
231+ || !invocationType .parameterType (1 ).equals (int .class ))
232+ throw new IllegalArgumentException ("Illegal invocation type " + invocationType );
233+ requireNonNull (labels );
234+
235+ labels = labels .clone ();
236+
237+ Class <?> enumClass = invocationType .parameterType (0 );
238+ labels = Stream .of (labels ).map (l -> convertEnumConstants (lookup , enumClass , l )).toArray ();
239+
240+ MethodHandle target =
241+ MethodHandles .insertArguments (DO_ENUM_SWITCH , 2 , (Object ) labels );
242+ target = target .asType (invocationType );
243+
244+ return new ConstantCallSite (target );
245+ }
246+
247+ private static <E extends Enum <E >> Object convertEnumConstants (MethodHandles .Lookup lookup , Class <?> enumClassTemplate , Object label ) {
248+ if (label == null ) {
249+ throw new IllegalArgumentException ("null label found" );
250+ }
251+ Class <?> labelClass = label .getClass ();
252+ if (labelClass == Class .class ) {
253+ if (label != enumClassTemplate ) {
254+ throw new IllegalArgumentException ("the Class label: " + label +
255+ ", expected the provided enum class: " + enumClassTemplate );
256+ }
257+ return label ;
258+ } else if (labelClass == String .class ) {
259+ @ SuppressWarnings ("unchecked" )
260+ Class <E > enumClass = (Class <E >) enumClassTemplate ;
261+ try {
262+ return ConstantBootstraps .enumConstant (lookup , (String ) label , enumClass );
263+ } catch (IllegalArgumentException ex ) {
264+ return null ;
265+ }
266+ } else {
267+ throw new IllegalArgumentException ("label with illegal type found: " + labelClass +
268+ ", expected label of type either String or Class" );
269+ }
270+ }
271+
272+ private static int doEnumSwitch (Enum <?> target , int startIndex , Object [] labels ) {
273+ if (target == null )
274+ return -1 ;
275+
276+ // Dumbest possible strategy
277+ Class <?> targetClass = target .getClass ();
278+ for (int i = startIndex ; i < labels .length ; i ++) {
279+ Object label = labels [i ];
280+ if (label instanceof Class <?> c ) {
281+ if (c .isAssignableFrom (targetClass ))
282+ return i ;
283+ } else if (label == target ) {
284+ return i ;
285+ }
286+ }
287+
288+ return labels .length ;
289+ }
290+
170291}
0 commit comments