Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java 9 support: use Unsafe-based reflection in Java 9+ #1218

Merged
merged 7 commits into from
Jan 3, 2018

Conversation

amogilev
Copy link
Contributor

@amogilev amogilev commented Jan 1, 2018

Fixes "illegal reflective access" warnings and exceptions, like one in #1216

fixes "illegal reflective access" warnings and exceptions
import com.google.gson.reflect.TypeToken;

/**
* Returns a function that can construct an instance of a requested type.
*/
public final class ConstructorConstructor {
private final Map<Type, InstanceCreator<?>> instanceCreators;
private final static ReflectionAccessor accessor = ReflectionAccessUtils.getReflectionAccessor();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't need to be static. There is just one instance of ConstructorConstructor in Gson

@@ -49,6 +51,7 @@
private final FieldNamingStrategy fieldNamingPolicy;
private final Excluder excluder;
private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
private final static ReflectionAccessor accessor = ReflectionAccessUtils.getReflectionAccessor();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again doesn't need to be static

* <p/>
* Works both for Java 9 and earlier Java versions.
*/
public class ReflectionAccessUtils {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you delete this class, and move this code to a getInstance() method under ReflectionAccessor?

* Provides a replacement for {@link AccessibleObject#setAccessible(boolean)}, useful when that basic operation is
* prohibited, e.g. throws {@link java.lang.reflect.InaccessibleObjectException} in Java 9.
*/
public interface ReflectionAccessor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This can be made an abstract class, no need of interface

* This implementation just calls {@link AccessibleObject#setAccessible(boolean) setAccessible(true)}, which worked
* fine before Java 9.
*/
public class PreJava9ReflectionAccessor implements ReflectionAccessor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

make final?

* NOTE: This implementation is designed for Java 9. Although it should work with earlier Java releases, it is better to
* use {@link PreJava9ReflectionAccessor} for them.
*/
public class UnsafeReflectionAccessor implements ReflectionAccessor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

make final

*/
public class UnsafeReflectionAccessor implements ReflectionAccessor {

private static final Unsafe theUnsafe = getUnsafeInstance();
Copy link
Collaborator

@inder123 inder123 Jan 2, 2018

Choose a reason for hiding this comment

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

remove static? There will be one instance of this class anyway

@inder123
Copy link
Collaborator

inder123 commented Jan 2, 2018

Thanks for the PR

@JakeWharton
Copy link
Contributor

All of these classes should be in internal. They're not public API.

@amogilev
Copy link
Contributor Author

amogilev commented Jan 2, 2018

@inder123 Thanks for the review, I have changed the code according to your comments.
@JakeWharton Do you mean that the code shall be moved to the package 'com.google.gson.internal.reflect'? Guess it makes sense...

@@ -1,5 +1,6 @@
/**
* This package provides utility classes for finding type information for generic types.
* This package provides utility classes for finding type information for generic types,
* as well as some utilities for working with Reflection API.
Copy link

Choose a reason for hiding this comment

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

two spaces between as and some


// singleton holder
private static class ReflectionAccessorHolder {
private static final ReflectionAccessor instance = createReflectionAccessor();
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you get rid of ReflectionAccessorHolder class, and just place the instance in ReflectionAccessor?

Copy link

Choose a reason for hiding this comment

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

If you get the holder, the instance field should not be private because of synthetic method (jvm does not allow such access so that java will generate extra methods to this field)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@inder123 You are right, there is no need of laziness for this singleton, so the holder can be removed

@@ -25,7 +25,7 @@
* This implementation just calls {@link AccessibleObject#setAccessible(boolean) setAccessible(true)}, which worked
* fine before Java 9.
*/
public class PreJava9ReflectionAccessor implements ReflectionAccessor {
public final class PreJava9ReflectionAccessor extends ReflectionAccessor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This class can be package private

@@ -27,7 +27,7 @@
* NOTE: This implementation is designed for Java 9. Although it should work with earlier Java releases, it is better to
* use {@link PreJava9ReflectionAccessor} for them.
*/
public class UnsafeReflectionAccessor implements ReflectionAccessor {
public final class UnsafeReflectionAccessor extends ReflectionAccessor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

package private?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It implies that 'reflect.impl' classes need to be moved up to the 'reflect' package. Seems fine after moving to a separate 'internal' package though...

* <p/>
* Works both for Java 9 and earlier Java versions.
*/
public abstract class ReflectionAccessor {
Copy link

Choose a reason for hiding this comment

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

package-private constructor please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why adding a constructor here? The class is abstract, so the default (public) constructor still cannot be used by anyone except of implementing classes

Copy link
Contributor

Choose a reason for hiding this comment

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

Because it prevents subclasses outside the package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but why shall we prevent such subclasses? I am not against it, but just do not understand...

/**
* {@inheritDoc}
*/
public void makeAccessible(AccessibleObject ao) {
Copy link

Choose a reason for hiding this comment

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

@Override annotation

/**
* {@inheritDoc}
*/
public void makeAccessible(AccessibleObject ao) {
Copy link

Choose a reason for hiding this comment

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

@Override annotation

}

private static ReflectionAccessor createReflectionAccessor() {
if (VersionUtils.getMajorJavaVersion() < 9) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could be inlined as
ReflectionAccessor instance = VersionUtils.getMajorJavaVersion() < 9 ? new PreJava9ReflectionAccessor() : new UnsafeReflectionAccessor();

*/
final class UnsafeReflectionAccessor extends ReflectionAccessor {

private static final Unsafe theUnsafe = getUnsafeInstance();
Copy link
Collaborator

Choose a reason for hiding this comment

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

do these need to be static? Please make them non-static

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure why you prefer instance fields over static ones, but sure, these may be change to non-static. What about static methods, like getUnsafeInstance()? If needed, they may be changed to non-static as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

static fields will get initialized when class is loaded which is an unnecessary penalty in memory and loading time.

Static methods are fine (and slightly preferred by me).

@inder123
Copy link
Collaborator

inder123 commented Jan 3, 2018

Thanks for patiently incorporating all the feedback

@googlebot
Copy link

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this State. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

@googlebot googlebot removed the cla: yes label Jan 3, 2018
@inder123 inder123 merged commit 8445689 into google:master Jan 3, 2018
@swankjesse
Copy link
Collaborator

I think this might be the wrong solution to the problem. Unfortunately, the right solution to this problem is a lot of work.

Instead of using a bigger, more powerful weapon to read & write platform types, I think Gson should have a new java-platform-typeadapters module.

Wherever Gson’s users are relying on reflection to read+write a platform type like java.util.UUID, we should handwrite a type adapter. That way we avoid getting into an arms race with the JDK maintainers who really don’t want their fields to be reflected upon.

@swankjesse
Copy link
Collaborator

... anyone who wants to use Gson with Java 9 to read+write platform types would need this module. And the Gson-using community would have to built the module to cover as many types as is reasonable.

@JakeWharton
Copy link
Contributor

Strongly agree #1216 (comment). The module system is the opportunity to enforce something which never should have been allowed!

@inder123
Copy link
Collaborator

inder123 commented Jan 3, 2018

@swankjesse Agree with you. We can start on adding some of these type adapters, and build upon it gradually.

sebasjm pushed a commit to sebasjm/gson that referenced this pull request Mar 11, 2018
* Java 9 support: use Unsafe-based reflection in Java 9+

fixes "illegal reflective access" warnings and exceptions

* fix Codacy warnings

* improve code quality based on PR review

* improve code quality based on PR review

* fix Codacy warning

* improve code quality based on PR review

* inlined createReflectionAccessor method
FredricMei pushed a commit to orientsec/gson that referenced this pull request Feb 11, 2019
* Java 9 support: use Unsafe-based reflection in Java 9+

fixes "illegal reflective access" warnings and exceptions

* fix Codacy warnings

* improve code quality based on PR review

* improve code quality based on PR review

* fix Codacy warning

* improve code quality based on PR review

* inlined createReflectionAccessor method
Marcono1234 added a commit to Marcono1234/gson that referenced this pull request Jun 3, 2021
Revert google#1218

Usage of sun.misc.Unsafe to change internal AccessibleObject.override field
to suppress JPMS warnings goes against the intentions of the JPMS and does not
work anymore in newer versions, see google#1540.
Therefore remove it and instead create a descriptive exception when making a
member accessible fails. If necessary users can also still use `java` command
line flags to open external modules.
eamonnmcmanus pushed a commit that referenced this pull request Nov 9, 2021
…tor (#1902)

* Remove UnsafeReflectionAccessor

Revert #1218

Usage of sun.misc.Unsafe to change internal AccessibleObject.override field
to suppress JPMS warnings goes against the intentions of the JPMS and does not
work anymore in newer versions, see #1540.
Therefore remove it and instead create a descriptive exception when making a
member accessible fails. If necessary users can also still use `java` command
line flags to open external modules.

* Fix failing to serialize Collection or Map with inaccessible constructor

Also remove tests which rely on Java implementation details.

* Don't keep reference to access exception of ConstructorConstructor

This also avoids a confusing stack trace, since the previously caught
exception might have had a complete unrelated stack trace.

* Remove Maven toolchain requirement

* Address review feedback

* Add back test for Security Manager
tibor-universe pushed a commit to getuniverse/gson that referenced this pull request Nov 21, 2021
…tor (google#1902)

* Remove UnsafeReflectionAccessor

Revert google#1218

Usage of sun.misc.Unsafe to change internal AccessibleObject.override field
to suppress JPMS warnings goes against the intentions of the JPMS and does not
work anymore in newer versions, see google#1540.
Therefore remove it and instead create a descriptive exception when making a
member accessible fails. If necessary users can also still use `java` command
line flags to open external modules.

* Fix failing to serialize Collection or Map with inaccessible constructor

Also remove tests which rely on Java implementation details.

* Don't keep reference to access exception of ConstructorConstructor

This also avoids a confusing stack trace, since the previously caught
exception might have had a complete unrelated stack trace.

* Remove Maven toolchain requirement

* Address review feedback

* Add back test for Security Manager
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants