benf.org :  other :  cfr :  JVM 11 dynamic constants.

Dynamic constants

JEP 309 (related reading JEP 303) describe a new feature that was introduced into the JVM as of v11, dynamic constants. A more narrative description is on javacodegeeks.

Unfortunately, this isn't generated by javac currently, you have to generate the bytecode yourself.

As such, there's no real way to decompile this accurately into java, so CFR approximates it into pseudocode. It won't quite be right, but you get a good idea what is going on!

Tueti Challenge 9.14 (thanks to JavierM for finding this!) asks you to reverse engineer some bytecode which makes use of this. (Along with lots of lovely duplicate method names which aren't legal java).
If we look at the bytecode for ????.class (catchy, I've renamed it to tmp.class below), we see:

C:\temp\artifact>"c:\Program Files\Java\jdk1.8.0_191"\bin\javap -c -p -v tmp.class
Error: error while reading constant pool for tmp.class: unexpected tag at #42: 17

Ooops, ok, we better use a more modern java..... (only a snippet of the javap output provided here)

C:\temp\artifact>"c:\Program Files\Java\jdk-13\bin"\javap -c -p -v tmp.class
Classfile /C:/temp/artifact/tmp.class
  Last modified 19 Mar 2019; size 816 bytes

  #5 = Utf8               a
  #40 = Utf8               Ljava/lang/String;
  #41 = NameAndType        #5:#40         // a:Ljava/lang/String;
  #42 = Dynamic            #0:#41         // #0:a:Ljava/lang/String;
  ...

  private final java.lang.String a();
    descriptor: ()Ljava/lang/String;
    flags: (0x1012) ACC_PRIVATE, ACC_FINAL, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: ldc           #42                 // Dynamic #0:a:Ljava/lang/String;
         2: areturn
		 
BootstrapMethods:
  0: #39 REF_invokeStatic "?".a:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/String;
    Method arguments: 

... So - it's loading a string constant with LDC, but that string constant is the result of calling 'a' (with the signature in bootstrapmethods #0). Good news - we can more or less figure out what's going on.....

/*
 * Decompiled with CFR 0.144.
 *
 * Could not load the following classes:
 *  \udb88\udd58  <-- that's because I renamed the class 'tmp.class'.
 */
import java.lang.invoke.MethodHandles;

/*
 * Uses jvm11+ dynamic constants - pseudocode provided - see https://www.benf.org/other/cfr/dynamic-constants.html
 */
final class \udb88\udd58 {
    private static /* synthetic */ String a(MethodHandles.Lookup lookup, String string, Class class_) {
        Class<?> class_2 = lookup.lookupClass();
        Class class_3 = class_2.getNestHost();
        for (Class class_4 : class_2.getNestMembers()) {
            if (class_4 == class_2 || class_4 == class_3) continue;
            return class_4.getSimpleName();
        }
        throw new RuntimeException();
    }

    private /* synthetic */ \udb88\udd58() {
    }

    private final /* synthetic */ String a() {
        return  /* dynamic constant */ (String)\udb88\udd58.a(MethodHandles.lookup(), "a", \udb88\udd58.class);
    }
}

CFR dynamic constant pseudocode

As of now, there's no way (Someone correct me!?) to get javac to explicitly use dynamic constants, i.e. there's no java language support.

So we currently HINT as to use of dynamic constants with

Enjoy!


Last updated 05/2019