-
CSR
-
Resolution: Unresolved
-
P3
-
None
-
behavioral
-
low
-
Since the existing exception coverage is incomplete, and the removal of coverage does not produce bad class file, I expect the impact to be quite low.
-
Java API
-
SE
Summary
Remove the behavior for java.lang.classfile.CodeBuilder$CatchBuilder
throwing IllegalArgumentException
for duplicate catch types, and add documentation to clarify the effects instead.
Problem
The IllegalArgumentException
behavior for CodeBuilder$CatchBuilder
when the catch types duplicate is incomplete.
- The exception is not thrown properly for duplicate catch-all handlers.
- The exception is not thrown if a subsequent handler is already covered by a previous handler handling a supertype; it only handles exact type matches.
Solution
Remove the IllegalArgumentException
behavior of CatchBuilder
related to duplicate catch types. The IAE behavior for primitive catch types is retained.
Alternative solution considered was to expand the IAE behavior to perform a complete class hierarchy analysis to block any subsequent type that is already handled by a previous catch block. We reject this because:
- Duplicate catch types are representable in the
class
file format. - Complete class hierarchy checks would be costly and requires complex configuration of ClassHierarchyResolver.
To remedy the potential dead duplicate catch, a few normative specification is added to describe the process of exception handling, linking to JVMS section 2.10 Exceptions, and API notes about the implications of duplicates.
Specification
diff --git a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java
index e640700c2e9b3..c60268b6d2908 100644
--- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java
+++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java
@@ -325,6 +325,11 @@ default CodeBuilder ifThenElse(Opcode opcode,
/**
* A builder to add catch blocks.
+ * <p>
+ * The order of catch blocks is significant. When an exception is thrown
+ * by the try block, the first catch block whose exception type is {@linkplain
+ * Class#isAssignableFrom(Class) the same class as or a superclass of} the
+ * class of exception thrown is branched to (JVMS {@jvms 2.10}).
*
* @see #trying
* @see ExceptionCatch
@@ -343,13 +348,16 @@ sealed interface CatchBuilder permits CatchBuilderImpl {
* If the type of exception is {@code null} then the catch block catches
* all exceptions.
*
+ * @apiNote
+ * If the type of exception to catch is already handled by previous
+ * catch blocks, this block will never be executed.
+ *
* @param exceptionType the type of exception to catch, may be {@code null}
* @param catchHandler handler that receives a {@link BlockCodeBuilder} to
* generate the body of the catch block
* @return this builder
- * @throws IllegalArgumentException if an existing catch block catches
- * an exception of the given type or {@code exceptionType}
- * represents a primitive type
+ * @throws IllegalArgumentException if {@code exceptionType} represents
+ * a primitive type
* @see #catchingMulti
* @see #catchingAll
*/
@@ -367,12 +375,16 @@ sealed interface CatchBuilder permits CatchBuilderImpl {
* If list of exception types is empty then the catch block catches all
* exceptions.
*
+ * @apiNote
+ * If every type of exception to catch is already handled by previous
+ * catch blocks, this block will never be executed.
+ *
* @param exceptionTypes the types of exception to catch
* @param catchHandler handler that receives a {@link BlockCodeBuilder}
* to generate the body of the catch block
* @return this builder
- * @throws IllegalArgumentException if an existing catch block catches
- * one or more exceptions of the given types
+ * @throws IllegalArgumentException if any exception type represents a
+ * primitive type
* @see #catching
* @see #catchingAll
*/
@@ -387,10 +399,12 @@ sealed interface CatchBuilder permits CatchBuilderImpl {
* The caught exception will be on top of the operand stack when the
* catch block is entered.
*
+ * @apiNote
+ * Since this block intercepts all exceptions, all subsequent catch
+ * blocks will never be executed.
+ *
* @param catchAllHandler handler that receives a {@link BlockCodeBuilder}
* to generate the body of the catch block
- * @throws IllegalArgumentException if an existing catch block catches
- * all exceptions
* @see #catching
* @see #catchingMulti
*/
diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java
index e924ca718e77d..53c2b6c4599ee 100644
--- a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java
+++ b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java
@@ -39,8 +39,10 @@
* A pseudo-instruction modeling an entry in the {@code exception_table} array
* of a {@link CodeAttribute Code} attribute. Catch (JVMS {@jvms 3.12}) and
* finally (JVMS {@jvms 3.14}) blocks in Java source code compile to exception
- * table entries. Delivered as a {@link CodeElement} when traversing the
- * contents of a {@link CodeModel}.
+ * table entries. The order of exception table entries is significant: when an
+ * exception is thrown in a method, execution branches to the first matching
+ * exception handler if such a handler exists (JVMS {@jvms 2.10}). Delivered as
+ * a {@link CodeElement} when traversing the contents of a {@link CodeModel}.
* <p>
* An exception table entry is composite:
* {@snippet lang=text :
- csr of
-
JDK-8361638 java.lang.classfile.CodeBuilder.CatchBuilder should not throw IllegalArgumentException for representable exception handlers
-
- In Progress
-