-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Conversation
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
Thanks for the PR |
All of these classes should be in |
@inder123 Thanks for the review, I have changed the code according to your comments. |
@@ -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. |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
package private?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
package-private constructor please
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
Thanks for patiently incorporating all the feedback |
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 |
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. |
... 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. |
Strongly agree #1216 (comment). The module system is the opportunity to enforce something which never should have been allowed! |
@swankjesse Agree with you. We can start on adding some of these type adapters, and build upon it gradually. |
* 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
* 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
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.
…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
…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
Fixes "illegal reflective access" warnings and exceptions, like one in #1216