1 /*
2  * Copyright (C) 2017 The Gson authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package com.google.gson.internal.reflect;
17
18 import java.lang.reflect.AccessibleObject;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21
22 import com.google.gson.JsonIOException;
23
24 /**
25  * An implementation of {@link ReflectionAccessor} based on {@link Unsafe}.
26  * <p>
27  * NOTE: This implementation is designed for Java 9. Although it should work with earlier Java releases, it is better to
28  * use {@link PreJava9ReflectionAccessor} for them.
29  */

30 @SuppressWarnings({"unchecked""rawtypes"})
31 final class UnsafeReflectionAccessor extends ReflectionAccessor {
32
33   private static Class unsafeClass;
34   private final Object theUnsafe = getUnsafeInstance();
35   private final Field overrideField = getOverrideField();
36
37   /** {@inheritDoc} */
38   @Override
39   public void makeAccessible(AccessibleObject ao) {
40     boolean success = makeAccessibleWithUnsafe(ao);
41     if (!success) {
42       try {
43         // unsafe couldn't be found, so try using accessible anyway
44         ao.setAccessible(true);
45       } catch (SecurityException e) {
46         throw new JsonIOException("Gson couldn't modify fields for " + ao
47           + "\nand sun.misc.Unsafe not found.\nEither write a custom type adapter,"
48           + " or make fields accessible, or include sun.misc.Unsafe.", e);
49       }
50     }
51   }
52
53   // Visible for testing only
54   boolean makeAccessibleWithUnsafe(AccessibleObject ao) {
55     if (theUnsafe != null && overrideField != null) {
56       try {
57         Method method = unsafeClass.getMethod("objectFieldOffset", Field.class);
58         long overrideOffset = (Long) method.invoke(theUnsafe, overrideField);  // long overrideOffset = theUnsafe.objectFieldOffset(overrideField);
59         Method putBooleanMethod = unsafeClass.getMethod("putBoolean",  Object.classlong.classboolean.class);
60         putBooleanMethod.invoke(theUnsafe, ao, overrideOffset, true); // theUnsafe.putBoolean(ao, overrideOffset, true);
61         return true;
62       } catch (Exception ignored) { // do nothing
63       }
64     }
65     return false;
66   }
67
68   private static Object getUnsafeInstance() {
69     try {
70       unsafeClass = Class.forName("sun.misc.Unsafe");
71       Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
72       unsafeField.setAccessible(true);
73       return unsafeField.get(null);
74     } catch (Exception e) {
75       return null;
76     }
77   }
78
79   private static Field getOverrideField() {
80     try {
81       return AccessibleObject.class.getDeclaredField("override");
82     } catch (NoSuchFieldException e) {
83       return null;
84     }
85   }
86 }
87