Skip to content

filtering JAVAC.TRYWITH

Evgeny Mandrikov edited this page Oct 4, 2017 · 41 revisions

This page discusses a not yet available feature!

Description

Based on source code of javac and practical observations - it generates bytecode that is equivalent to the compilation of source code that is described in JLS 14.20.3. try-with-resources

Note that case of multiple resources looks like multiple nested try-with-resources statements.

javac from JDK 9 EA b160 does the same, but with some optimizations (see JDK-7020499):

  • null check for resource is omitted when it is initialized as new ...
  • when number of copies of closing logic reaches threshold, this logic is extracted into synthetic method $closeResource containing calls to methods addSuppressed and close, null check for resource (if present - see above) is done before call of this method; when synthetic method not used null check of primaryExc comes before null check of resource

In general:

  • body block
  • closing resources (in reverse order of opening) on a non-exceptional path
  • finally block on a non-exceptional path in case of return from body
  • return (note that this instruction belongs to a line of original return statement) or goto
  • handlers to close resources (in reverse order of opening) on exceptional path
  • finally block on exceptional path and additional handlers for catch blocks
  • finally block on a non-exceptional path in case of goto from body

Note that synthetic method either will be used for all resources, either for none of them. However this is not the case for null check.

@Godin: in current implementation association between resource and slot of variable is done on exceptional path and is used to find close of resource on a non-exceptional. Possibility that null check of resource comes after null check of primaryException implies that this order should be determined before doing association.

ECJ on the other hand generates "contiguous" block to close all resources that uses single variable primaryExc. Also note that in case of return from body ECJ won't generate jumps between blocks that close different resources.

Filtering

Uncovered corner cases - https://github.com/jacoco/jacoco/pull/500#issuecomment-296285412

Source Example

Bytecode Pattern

To close resource on exceptional path javac 7-8 generates

  ASTORE t
  ALOAD t
  ASTORE primaryExc
  ALOAD t
  ATHROW

  ASTORE t
  ALOAD primaryExc
  IFNULL L
  ALOAD r
  INVOKEINTERFACE close()V
  GOTO L
  ASTORE t
  ALOAD primaryExc
  ALOAD t
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V
  GOTO L
  ALOAD r
  INVOKEINTERFACE close()V
  ALOAD t
  ATHROW
L:

ECJ for

try (r0 = ...; r1 = ...; r2 = ...) {
  ...
} finally {
  ...
}

generates

// body

r0.close:
  ALOAD 5
  IFNULL r1.close
  ALOAD 5
  INVOKEVIRTUAL close()V
  GOTO r1.close
  ASTORE 1

  ALOAD 5
  IFNULL L
  ALOAD 5
  INVOKEVIRTUAL close()V
L:
  ALOAD 1
  ATHROW

r1.close:
  ALOAD 4
  IFNULL r2.close
  ALOAD 4
  INVOKEVIRTUAL close()V
  GOTO r2.close

  ASTORE 2
  ALOAD 1
  IFNONNULL S
  ALOAD 2 
  ASTORE 1
  GOTO E
S:
  ALOAD 1
  ALOAD 2
  IF_ACMPEQ E
  ALOAD 1
  ALOAD 2
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V;
E:

  ALOAD 4
  IFNULL L
  ALOAD 4
  INVOKEVIRTUAL close()V
L:
  ALOAD 1
  ATHROW

r2.close:
  ALOAD 3
  IFNULL end
  ALOAD 3
  INVOKEVIRTUAL close()V
  GOTO end

  ASTORE 2
  ALOAD 1
  IFNONNULL S
  ALOAD 2
  ASTORE 1
  GOTO E
S:
  ALOAD 1
  ALOAD 2
  IF_ACMPEQ E
  ALOAD 1
  ALOAD 2
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V
E:

  ALOAD 3
  IFNULL L
  ALOAD 3
  INVOKEVIRTUAL close()V
L:
  ALOAD 1
  ATHROW

  ASTORE 2
  ALOAD 1
  IFNONNULL S
  ALOAD 2
  ASTORE 1
  GOTO E
S:
  ALOAD 1
  ALOAD 2
  IF_ACMPEQ E
  ALOAD 1
  ALOAD 2
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V
E:

  // throw primaryExc
  ALOAD 1
  ATHROW

// additional handlers for `catch` blocks and finally on exceptional path

end:
// finally on normal path

ECJ for

try (r0 = ...; r1 = ...; r2 = ...) {
  ...
  return ...
} finally {
  ...
}

generates

// body

// close r0 on a normal path
  ALOAD 5
  IFNULL L
  ALOAD 5
  INVOKEVIRTUAL close()V
L:
// close r1 on a normal path
  ALOAD 4
  IFNULL L
  ALOAD 4
  INVOKEVIRTUAL close()V
L:
// close r2 on a normal path
  ALOAD 3
  IFNULL L
  ALOAD 3
  INVOKEVIRTUAL close()V
L:

// finally on a normal path
  ARETURN

// close r0 on exceptional path
  ALOAD 5
  IFNULL L
  ALOAD 5
  INVOKEVIRTUAL close()V
L:
  ALOAD 1
  ATHROW

  ASTORE 2
  ALOAD 1
  IFNONNULL S
  ALOAD 2
  ASTORE 1
  GOTO E
S:
  ALOAD 1
  ALOAD 2
  IF_ACMPEQ E
  ALOAD 1
  ALOAD 2
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V
E:

// close r1 on exceptional path
  ALOAD 4
  IFNULL L
  ALOAD 4
  INVOKEVIRTUAL close()V
L:
  ALOAD 1
  ATHROW

  ASTORE 2
  ALOAD 1
  IFNONNULL S
  ALOAD 2
  ASTORE 1
  GOTO E
S:
  ALOAD 1
  ALOAD 2
  IF_ACMPEQ E
  ALOAD 1
  ALOAD 2
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V
E:

// close r2 on exceptional path
  ALOAD 3
  IFNULL L
  ALOAD 3
  INVOKEVIRTUAL close()V
L:
  ALOAD 1
  ATHROW

  ASTORE 2
  ALOAD 1
  IFNONNULL S
  ALOAD 2
  ASTORE 1
  GOTO E
S:
  ALOAD 1
  ALOAD 2
  IF_ACMPEQ E
  ALOAD 1
  ALOAD 2
  INVOKEVIRTUAL java/lang/Throwable#addSuppressed(Ljava/lang/Throwable;)V
E:

  // throw primaryExc
  ALOAD 1
  ATHROW

// additional handlers for `catch` blocks and finally on exceptional path