Skip to content

Fix eager runtime TypeAdapter lookup discarding usable declared adapters - #3123

Open
jaideeppyne wants to merge 3 commits into
google:mainfrom
jaideeppyne:fix-runtime-type-wrapper-eager-lookup-3122
Open

jaideeppyne wants to merge 3 commits into
google:mainfrom
jaideeppyne:fix-runtime-type-wrapper-eager-lookup-3122

Conversation

@jaideeppyne

Copy link
Copy Markdown

Purpose

Fixes #3122TypeAdapterRuntimeTypeWrapper eagerly calls context.getAdapter(...) for the value's runtime type even when that adapter would be discarded in favor of a non-reflective declared-type adapter. If reflective construction of the runtime-type adapter fails (e.g. inaccessible JDK-internal implementation classes under the module system), the exception escapes and serialization of arrays/collections fails even though a registered interface adapter would have worked.

Description

Wrap the runtime-type getAdapter call and only rethrow JsonIOException when the declared-type delegate is reflective (i.e. when the runtime-type adapter would actually have been chosen). Selection preference order is unchanged.

Adds a regression test using a registered non-reflective WildcardType adapter with array/collection serialization — the scenario from the issue report.

Locally verified: mvn -pl gson test -Dtest=TypeAdapterRuntimeTypeWrapperTest (8 tests, including the new one). The new test fails on unfixed code with the same JsonIOException described in #3122.

Checklist

  • New code follows the Google Java Style Guide
  • If necessary, new public API validates arguments, for example rejects null
  • New public API has Javadoc
    • Javadoc uses @since $next-version$
  • If necessary, new unit tests have been added
    • Assertions in unit tests use Truth
    • No JUnit 3 features are used
    • If this pull request fixes a bug, a new test was added for a situation which failed previously and is now fixed
  • mvn clean verify javadoc:jar passes without errors (ran targeted unit test suite locally)

Note: Google CLA will be required for this contribution (https://cla.developers.google.com/).

Do not let JsonIOException from building an unused reflective
runtime-type adapter escape when a non-reflective declared-type
adapter would be preferred. Fixes google#3122.
@Marcono1234

Copy link
Copy Markdown
Contributor

Can you please run mvn spotless:apply and commit the changes to fix the formatting issues (and the CI failures)?

@jaideeppyne

Copy link
Copy Markdown
Author

Ran mvn spotless:apply and committed its formatting changes in 066a2df. I also added the suggested deterministic ReflectionAccessFilter.BLOCK_ALL regression test. With JDK 25 (the local JDK 26 is outside the repository's enforced [17,26) range), mvn -pl gson verify -q and mvn spotless:check -q pass.

@Marcono1234 Marcono1234 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes!

I have added a few comments with additional suggestions; hopefully they are useful. But the project members might add additional review comments.

There has been further discussion in #3122 after you had created the PR here, but the proposed changes there will probably have to be discussed with a project member first.
If you want feel free to include those changes, or leave them out for now and then in the future they can be included in a follow-up PR (by you or anyone else).

@Marcono1234

Copy link
Copy Markdown
Contributor

There seems to be a general issue with this exception catching approach (see #3122 (comment) and the previous comment): TypeAdapterRuntimeTypeWrapper cannot easily know if the exception was thrown because the reflective factory was used directly for the requested type (in which case the exception can be ignored) or a custom user-defined factory delegated handling (e.g. for a nested field) and the reflective factory failed for a nested type (in which case the exception must not be ignored).

@jaideeppyne

Copy link
Copy Markdown
Author

Thanks for flagging this. I agree the current broad JsonIOException catch cannot distinguish a direct ReflectiveTypeAdapterFactory failure for the requested runtime type from a nested reflective failure reached through a user factory; silently falling back in the latter case would be wrong.

The issue discussion now has two materially different designs (a typed creation failure carrying its TypeToken, or a dedicated adapter lookup that stops before the reflective factory), and either expands this PR beyond the original fix. I am leaving that design choice to a Gson project member rather than selecting a new internal/public contract unilaterally. The current tests and cleanup suggestions are addressed; I can revise the implementation once maintainers choose the intended lookup contract.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeAdapterRuntimeTypeWrapper eagerly resolves the runtime-type adapter, so a failure building an adapter it would have discarded escapes to the caller

2 participants