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

* 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
This commit is contained in:
Andrey Mogilev 2018-01-04 02:08:50 +07:00 committed by inder123
parent fb7ab06f07
commit 8445689e4d
6 changed files with 161 additions and 3 deletions

View File

@ -40,6 +40,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonIOException;
import com.google.gson.internal.reflect.ReflectionAccessor;
import com.google.gson.reflect.TypeToken;
/**
@ -47,6 +48,7 @@ import com.google.gson.reflect.TypeToken;
*/
public final class ConstructorConstructor {
private final Map<Type, InstanceCreator<?>> instanceCreators;
private final ReflectionAccessor accessor = ReflectionAccessor.getInstance();
public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) {
this.instanceCreators = instanceCreators;
@ -98,7 +100,7 @@ public final class ConstructorConstructor {
try {
final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
accessor.makeAccessible(constructor);
}
return new ObjectConstructor<T>() {
@SuppressWarnings("unchecked") // T is the same raw type as is requested

View File

@ -28,6 +28,7 @@ import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.ObjectConstructor;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.reflect.ReflectionAccessor;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
@ -49,6 +50,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
private final FieldNamingStrategy fieldNamingPolicy;
private final Excluder excluder;
private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
private final ReflectionAccessor accessor = ReflectionAccessor.getInstance();
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
FieldNamingStrategy fieldNamingPolicy, Excluder excluder,
@ -154,7 +156,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
if (!serialize && !deserialize) {
continue;
}
field.setAccessible(true);
accessor.makeAccessible(field);
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
List<String> fieldNames = getFieldNames(field);
BoundField previous = null;

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2017 The Gson authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.internal.reflect;
import java.lang.reflect.AccessibleObject;
/**
* A basic implementation of {@link ReflectionAccessor} which is suitable for Java 8 and below.
* <p>
* This implementation just calls {@link AccessibleObject#setAccessible(boolean) setAccessible(true)}, which worked
* fine before Java 9.
*/
final class PreJava9ReflectionAccessor extends ReflectionAccessor {
/**
* {@inheritDoc}
*/
@Override
public void makeAccessible(AccessibleObject ao) {
ao.setAccessible(true);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2017 The Gson authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.internal.reflect;
import com.google.gson.util.VersionUtils;
import java.lang.reflect.AccessibleObject;
/**
* Provides a replacement for {@link AccessibleObject#setAccessible(boolean)}, which may be used to
* avoid reflective access issues appeared in Java 9, like {@link java.lang.reflect.InaccessibleObjectException}
* thrown or warnings like
* <pre>
* WARNING: An illegal reflective access operation has occurred
* WARNING: Illegal reflective access by ...
* </pre>
* <p/>
* Works both for Java 9 and earlier Java versions.
*/
public abstract class ReflectionAccessor {
// the singleton instance, use getInstance() to obtain
private static final ReflectionAccessor instance = VersionUtils.getMajorJavaVersion() < 9 ? new PreJava9ReflectionAccessor() : new UnsafeReflectionAccessor();
/**
* Does the same as {@code ao.setAccessible(true)}, but never throws
* {@link java.lang.reflect.InaccessibleObjectException}
*/
public abstract void makeAccessible(AccessibleObject ao);
/**
* Obtains a {@link ReflectionAccessor} instance suitable for the current Java version.
* <p>
* You may need one a reflective operation in your code throws {@link java.lang.reflect.InaccessibleObjectException}.
* In such a case, use {@link ReflectionAccessor#makeAccessible(AccessibleObject)} on a field, method or constructor
* (instead of basic {@link AccessibleObject#setAccessible(boolean)}).
*/
public static ReflectionAccessor getInstance() {
return instance;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2017 The Gson authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.internal.reflect;
import sun.misc.Unsafe;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
/**
* An implementation of {@link ReflectionAccessor} based on {@link Unsafe}.
* <p>
* 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.
*/
final class UnsafeReflectionAccessor extends ReflectionAccessor {
private final Unsafe theUnsafe = getUnsafeInstance();
private final Field overrideField = getOverrideField();
/**
* {@inheritDoc}
*/
@Override
public void makeAccessible(AccessibleObject ao) {
if (theUnsafe != null && overrideField != null) {
long overrideOffset = theUnsafe.objectFieldOffset(overrideField);
theUnsafe.putBoolean(ao, overrideOffset, true);
}
}
private static Unsafe getUnsafeInstance() {
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
return (Unsafe) unsafeField.get(null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static Field getOverrideField() {
try {
return AccessibleObject.class.getDeclaredField("override");
} catch (NoSuchFieldException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -1,6 +1,6 @@
/**
* This package provides utility classes for finding type information for generic types.
*
*
* @author Inderjeet Singh, Joel Leitch
*/
package com.google.gson.reflect;