Skip to content

Commit

Permalink
Java 9 support: use Unsafe-based reflection in Java 9+ (google#1218)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
amogilev authored and inder123 committed Jan 3, 2018
1 parent 74732d8 commit 30efaaa
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@

import com.google.gson.InstanceCreator;
import com.google.gson.JsonIOException;
import com.google.gson.internal.reflect.ReflectionAccessor;
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 ReflectionAccessor accessor = ReflectionAccessor.getInstance();

public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) {
this.instanceCreators = instanceCreators;
Expand Down Expand Up @@ -98,7 +100,7 @@ private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType)
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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;
Expand All @@ -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,
Expand Down Expand Up @@ -154,7 +156,7 @@ private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type,
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 30efaaa

Please sign in to comment.