[JEP-305] Inefficient compilation of Pattern Matching for instanceof

Evgeny Mandrikov mandrikov at gmail.com
Sat Jan 18 13:55:03 UTC 2020


Hello!

I'm the project lead and developer of open source project JaCoCo [1] - one
of the most popular code coverage tools for Java.

During our experiments with JEP-305,
we realized that javac in JDK 14 EA b32
compiles

class Example {
    static void example(Object e) {
        if (e instanceof String s) {
            nop(s);
        }
    }

    public static void main(String[] args) {
        example(new Object());
        example("");
    }

    static void nop(String s) {
    }
}

into following bytecode

  static void example(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: astore_2
         2: aload_2
         3: instanceof    #7                  // class java/lang/String
         6: ifeq          26
         9: aload_2
        10: checkcast     #7                  // class java/lang/String
        13: dup
        14: astore_1
        15: aload_2
        16: checkcast     #7                  // class java/lang/String
        19: if_acmpne     26
        22: aload_1
        23: invokestatic  #9                  // Method
nop:(Ljava/lang/String;)V
        26: return

which contains two comparisons (at offsets 6 and 19).
Presence of the second one seems redundant and quite unfortunate for users
of JaCoCo,
because one of the two branches of this comparison is never executed
and will be present in code coverage report as uncovered,
whereas looking at the source code users will expect to see only one
comparison.
See attached screenshots.

And I guess that other tools that analyze bytecode might be affected,
e.g. SpotBugs [2] which as far as I know detects redundant comparisons [3].

After further investigations [4],
we understood that JEP-305 was implemented in javac as AST-to-AST
transformation [5]
and that the second comparison is a way to place an assignment into a
condition in AST:

void example(Object e) {
    String s;
    Object temp = e;
    if (
        (temp instanceof String)
         && ((s = (String) temp) == (String) temp)
    ) {
        nop(s);
    }
}

During the same investigations [4], we also looked at ongoing
implementation of JEP-305 in Eclipse Compiler [6]
and seems that it will be producing bytecode with only one comparison:

  static void example(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: aconst_null
         1: astore_1
         2: aload_0
         3: instanceof    #13                 // class java/lang/String
         6: ifeq          18
         9: aload_0
        10: checkcast     #13                 // class java/lang/String
        13: astore_1
        14: aload_1
        15: invokestatic  #15                 // Method
nop:(Ljava/lang/String;)V
        18: return

and thus also smaller class files.

So we are wondering if there is a chance to improve javac to get rid of
this redundant comparison?

I can try to work on such improvement,
but ultimately will need a sponsor as I have only author status in OpenJDK
census [7].

Regards,
Evgeny

[1] https://github.com/jacoco/jacoco
[2] https://github.com/spotbugs/spotbugs
[3] https://github.com/spotbugs/spotbugs/issues/756[4]
https://groups.google.com/d/msg/jacoco-dev/XgsMix2BBGc/zbMjqi5eEAAJ
[5] https://hg.openjdk.java.net/jdk/jdk/rev/7799a51dbe30#l19.155
[6] https://bugs.eclipse.org/bugs/show_bug.cgi?id=531715
[7] https://openjdk.java.net/census#godin


More information about the amber-dev mailing list