RFR: 8164714: Constructor.newInstance creates instance of inner class with null outer class
Chen Liang
liach at openjdk.org
Wed Mar 5 15:08:32 UTC 2025
The Java Language Specification anticipates that inner classes always have non-null enclosing instances. It ensures the non-nullness by enforcing null checks at the use sites that provides immediately enclosing instances to inner class constructors, such as for super invocations, or an `outer.new Inner()` invocation.
However, the translations do not require a null check in the actual constructor, when the immediately enclosing instance is received through a mandated parameter and stored into a synthetic field or discarded. As a result, class file constructs, such as core reflection, method handles, or arbitrary class files can pass in `null` for the immediately enclosing instance, and later execution may fail with NPE by chance if any enclosing instance is used.
This patch proposes to add a null check against the "outer this" in inner class constructors that call a superclass constructor, including when the "outer this" is discarded immediately thereafter (#4966) for consistency. This null check will be emitted regardless of source or target versions. This change is considered an implementation artifact like the synthetic field that captures the enclosing instance; as a result, there is no JLS change.
The reason for this eager NPE decision is that there is no compatibility of such NPE behaviors - any evolution of the inner classes constructed with null enclosing instances may suddenly start using an enclosing instance and fail with NPE. Therefore, there's no compatibility aspect in such out-of-spec usages of passing `null` as immediately enclosing instance, and this null check can be considered such an evolution.
---------
### Progress
- [ ] Change must be properly reviewed (1 review required, with at least 1 [Reviewer](https://openjdk.org/bylaws#reviewer))
- [x] Change must not contain extraneous whitespace
- [x] Commit message must refer to an issue
- [ ] Change requires CSR request [JDK-8351274](https://bugs.openjdk.org/browse/JDK-8351274) to be approved
### Reviewing
<details><summary>Using <code>git</code></summary>
Checkout this PR locally: \
`$ git fetch https://git.openjdk.org/jdk.git pull/23875/head:pull/23875` \
`$ git checkout pull/23875`
Update a local copy of the PR: \
`$ git checkout pull/23875` \
`$ git pull https://git.openjdk.org/jdk.git pull/23875/head`
</details>
<details><summary>Using Skara CLI tools</summary>
Checkout this PR locally: \
`$ git pr checkout 23875`
View PR using the GUI difftool: \
`$ git pr show -t 23875`
</details>
<details><summary>Using diff file</summary>
Download this PR as a diff file: \
<a href="https://git.openjdk.org/jdk/pull/23875.diff">https://git.openjdk.org/jdk/pull/23875.diff</a>
</details>
-------------
Commit messages:
- 8164714: Constructor.newInstance creates instance of inner class with null outer class
Changes: https://git.openjdk.org/jdk/pull/23875/files
Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=23875&range=00
Issue: https://bugs.openjdk.org/browse/JDK-8164714
Stats: 105 lines in 4 files changed: 85 ins; 3 del; 17 mod
Patch: https://git.openjdk.org/jdk/pull/23875.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/23875/head:pull/23875
PR: https://git.openjdk.org/jdk/pull/23875
More information about the compiler-dev
mailing list