JDK-8335482 : Add notes for Error handling in Method.invoke and Constructor.newInstance
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Priority: P4
  • Status: Draft
  • Resolution: Unresolved
  • Fix Versions: tbd
  • Submitted: 2024-07-01
  • Updated: 2024-11-05
Related Reports
CSR :  
Description
Summary
-------

Add API notes about `Method.invoke` and `Constructor.newInstance` wrapping all `Error` in `InvocationTargetException`, recommending users to unwrap and handle those errors or the consequences.

Problem
-------

`Method.invoke` and `Constructor.newInstance` wraps `Error` instances in `InvocationTargetException`, which could be dangerous if `OutOfMemoryError` or `StackOverflowError` are wrapped; even further wrapping the InvocationTargetException into other throwables can lead to more errors from the VM, hiding the original error.

Meanwhile, the specifcations already require `Error` instances to be wrapped in `InvocationTargetException`, but the wording is not clear; it's easy for users to mishandle these APIs' `InvocationTargetException`.

Solution
--------

1. Specify that `InvocationTargetException` wraps any throwable, explicitly.
2. Add a note section about this wrapping of `Error` and the risk associated to mishandling/ignoring `Error`.

Specification
-------------

    diff --git a/src/java.base/share/classes/java/lang/reflect/Constructor.java b/src/java.base/share/classes/java/lang/reflect/Constructor.java
    index 99f14d01536..e3656df0a4c 100644
    --- a/src/java.base/share/classes/java/lang/reflect/Constructor.java
    +++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java
    @@ -450,6 +450,15 @@ void specificToGenericStringHeader(StringBuilder sb) {
          * <p>If the constructor completes normally, returns the newly
          * created and initialized instance.
          *
    +     * @apiNote
    +     * {@link Throwable}s thrown by the constructor, including {@link
    +     * Error}s, are wrapped in {@link InvocationTargetException}s.
    +     * The wrapped throwable should be handled; ignoring the wrapped
    +     * throwable, such as passing the {@link InvocationTargetException}
    +     * as the cause to construct a new throwable, may fail in cases
    +     * like {@link OutOfMemoryError} or {@link StackOverflowError}.
    +     * The JVM may throw new errors that hide the original ones.
    +     *
          * @param initargs array of objects to be passed as arguments to
          * the constructor call; values of primitive types are wrapped in
          * a wrapper object of the appropriate type (e.g. a {@code float}
    @@ -471,7 +480,7 @@ void specificToGenericStringHeader(StringBuilder sb) {
          * @throws    InstantiationException    if the class that declares the
          *              underlying constructor represents an abstract class.
          * @throws    InvocationTargetException if the underlying constructor
    -     *              throws an exception.
    +     *              throws any {@link Throwable}.
          * @throws    ExceptionInInitializerError if the initialization provoked
          *              by this method fails.
          */
    diff --git a/src/java.base/share/classes/java/lang/reflect/Method.java b/src/java.base/share/classes/java/lang/reflect/Method.java
    index b6ccbaa8294..33711f9953d 100644
    --- a/src/java.base/share/classes/java/lang/reflect/Method.java
    +++ b/src/java.base/share/classes/java/lang/reflect/Method.java
    @@ -526,6 +526,16 @@ void specificToGenericStringHeader(StringBuilder sb) {
          * underlying method return type is void, the invocation returns
          * null.
          *
    +     * @apiNote
    +     * {@link Throwable}s thrown by the underlying method, including
    +     * {@link Error}s like {@link AbstractMethodError}, are wrapped
    +     * in {@link InvocationTargetException}s.
    +     * The wrapped throwable should be handled; ignoring the wrapped
    +     * throwable, such as passing the {@link InvocationTargetException}
    +     * as the cause to construct a new throwable, may fail in cases
    +     * like {@link OutOfMemoryError} or {@link StackOverflowError}.
    +     * The JVM may throw new errors that hide the original ones.
    +     *
          * @param obj  the object the underlying method is invoked from
          * @param args the arguments used for the method call
          * @return the result of dispatching the method represented by
    @@ -546,7 +556,7 @@ void specificToGenericStringHeader(StringBuilder sb) {
          *              cannot be converted to the corresponding formal
          *              parameter type by a method invocation conversion.
          * @throws    InvocationTargetException if the underlying method
    -     *              throws an exception.
    +     *              throws any {@link Throwable}.
          * @throws    NullPointerException      if the specified object is null
          *              and the method is an instance method.
          * @throws    ExceptionInInitializerError if the initialization