Add fields to SunMiscUnsafeApiTest.

Change-Id: Idaf022a065c64e31cbb6085b648971645854b762
diff --git a/src/main/java/com/android/tools/r8/references/Reference.java b/src/main/java/com/android/tools/r8/references/Reference.java
index a5d015d..3376b6d 100644
--- a/src/main/java/com/android/tools/r8/references/Reference.java
+++ b/src/main/java/com/android/tools/r8/references/Reference.java
@@ -57,6 +57,10 @@
     }
   }
 
+  public static TypeReference typeFromClassReference(ClassReference clazz) {
+    return typeFromDescriptor(clazz.getDescriptor());
+  }
+
   public static TypeReference typeFromTypeName(String typeName) {
     return typeFromDescriptor(DescriptorUtils.javaTypeToDescriptor(typeName));
   }
diff --git a/src/test/java/com/android/tools/r8/androidapi/SunMiscUnsafeApiTest.java b/src/test/java/com/android/tools/r8/androidapi/SunMiscUnsafeApiTest.java
index a0ae410..e12f4c2 100644
--- a/src/test/java/com/android/tools/r8/androidapi/SunMiscUnsafeApiTest.java
+++ b/src/test/java/com/android/tools/r8/androidapi/SunMiscUnsafeApiTest.java
@@ -4,13 +4,28 @@
 
 package com.android.tools.r8.androidapi;
 
+import static com.android.tools.r8.references.Reference.BOOL;
+import static com.android.tools.r8.references.Reference.BYTE;
+import static com.android.tools.r8.references.Reference.CHAR;
+import static com.android.tools.r8.references.Reference.DOUBLE;
+import static com.android.tools.r8.references.Reference.FLOAT;
+import static com.android.tools.r8.references.Reference.INT;
+import static com.android.tools.r8.references.Reference.LONG;
+import static com.android.tools.r8.references.Reference.SHORT;
 import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -34,247 +49,351 @@
     return TestParameters.builder().withDexRuntimes().withMaximumApiLevel().build();
   }
 
-  private static class MethodReference {
+  private static final Map<ClassReference, AndroidApiLevel> classApiMap = new HashMap<>();
+  private static final Map<FieldReference, AndroidApiLevel> fieldApiMap = new HashMap<>();
+  private static final Map<MethodReference, AndroidApiLevel> methodApiMap = new HashMap<>();
 
-    public final String name;
-    public final Class<?>[] params;
+  private static final ClassReference SUN_MISC_UNSAFE =
+      Reference.classFromBinaryName("sun/misc/Unsafe");
+  private static final TypeReference SUN_MISC_UNSAFE_TYPE =
+      Reference.typeFromClassReference(SUN_MISC_UNSAFE);
+  private static final TypeReference JDK_INTERNAL_MISC_UNSAFE_TYPE =
+      Reference.typeFromClassReference(Reference.classFromBinaryName("jdk/internal/misc/Unsafe"));
+  private static final ClassReference CLASS = Reference.classFromClass(Class.class);
+  private static final ClassReference OBJECT = Reference.classFromClass(Object.class);
+  private static final ClassReference FIELD = Reference.classFromClass(Field.class);
+  private static final TypeReference VOID = Reference.returnTypeFromDescriptor("V");
 
-    private MethodReference(String name, Class<?>[] params) {
-      this.name = name;
-      this.params = params;
-    }
+  private static void addClass(AndroidApiLevel introducedAtApi) {
+    classApiMap.put(SUN_MISC_UNSAFE, introducedAtApi);
   }
 
-  private static final Map<MethodReference, AndroidApiLevel> apiMap = new HashMap<>();
+  private static void addField(String name, AndroidApiLevel introducedAtApi, TypeReference type) {
+    fieldApiMap.put(Reference.field(SUN_MISC_UNSAFE, name, type), introducedAtApi);
+  }
 
   private static void addMethod(
-      AndroidApiLevel introducedAtApi, String name, Class<?>... parameterTypes) {
-    apiMap.put(new MethodReference(name, parameterTypes), introducedAtApi);
+      String name,
+      AndroidApiLevel introducedAtApi,
+      TypeReference returnType,
+      TypeReference... parameters) {
+    methodApiMap.put(
+        Reference.method(SUN_MISC_UNSAFE, name, ImmutableList.copyOf(parameters), returnType),
+        introducedAtApi);
   }
 
   @Test
-  public void verifyApi() throws Exception {
+  public void test() throws Exception {
     parameters.assumeDexRuntime();
-    Class<TestClass> testClass = TestClass.class;
-    List<Matcher<String>> expectedMatchers = new ArrayList<>(apiMap.size());
-    apiMap.forEach(
-        (method, api) ->
-            expectedMatchers.add(
-                equalTo(
-                    method.name
-                        + "("
-                        + TestClass.parameterString(method.params)
-                        + "): "
-                        + (parameters.getApiLevel().isGreaterThanOrEqualTo(api)))));
+    Class<?> testClass = TestClass.class;
     testForD8(parameters)
         .addProgramClasses(testClass)
         .compile()
         .run(parameters.getRuntime(), testClass)
         .assertSuccess()
-        .assertStdoutLinesMatchesUnordered(expectedMatchers);
+        .assertStdoutLinesMatchesUnordered(expectedOutput());
+  }
+
+  private List<Matcher<String>> expectedOutput() {
+    List<Matcher<String>> expectedMatchers = new ArrayList<>();
+    AndroidApiLevel apiLevel = parameters.getApiLevel();
+    classApiMap.forEach(
+        (clazz, api) -> {
+          boolean shouldBeDefined = apiLevel.isGreaterThanOrEqualTo(api);
+          expectedMatchers.add(equalTo(clazz.getTypeName() + ": " + shouldBeDefined));
+        });
+    fieldApiMap.forEach(
+        (field, api) -> {
+          boolean shouldBeDefined = apiLevel.isGreaterThanOrEqualTo(api);
+          expectedMatchers.add(equalTo(field.toSourceString() + ": " + shouldBeDefined));
+        });
+    methodApiMap.forEach(
+        (method, api) -> {
+          boolean shouldBeDefined = apiLevel.isGreaterThanOrEqualTo(api);
+          expectedMatchers.add(equalTo(method.toSourceString() + ": " + shouldBeDefined));
+        });
+    return expectedMatchers;
   }
 
   static {
+    // This should be set to the lowest API tested NOT the lowest possible API.
     AndroidApiLevel always = AndroidApiLevel.I_MR1;
-    addMethod(AndroidApiLevel.N, "addressSize");
-    addMethod(AndroidApiLevel.J, "allocateInstance", Class.class);
-    addMethod(AndroidApiLevel.N, "allocateMemory", long.class);
-    addMethod(always, "arrayBaseOffset", Class.class);
-    addMethod(always, "arrayIndexScale", Class.class);
-    addMethod(always, "compareAndSwapInt", Object.class, long.class, int.class, int.class);
-    addMethod(always, "compareAndSwapLong", Object.class, long.class, long.class, long.class);
-    addMethod(always, "compareAndSwapObject", Object.class, long.class, Object.class, Object.class);
-    addMethod(AndroidApiLevel.N, "copyMemory", long.class, long.class, long.class);
-    addMethod(
-        AndroidApiLevel.N,
-        "copyMemoryFromPrimitiveArray",
-        Object.class,
-        long.class,
-        long.class,
-        long.class);
-    addMethod(
-        AndroidApiLevel.N,
-        "copyMemoryToPrimitiveArray",
-        long.class,
-        Object.class,
-        long.class,
-        long.class);
-    addMethod(AndroidApiLevel.MAIN, "forbidObtainingRecordFieldOffsets");
-    addMethod(AndroidApiLevel.N, "freeMemory", long.class);
-    addMethod(AndroidApiLevel.N, "fullFence");
-    addMethod(AndroidApiLevel.N, "getAndAddInt", Object.class, long.class, int.class);
-    addMethod(AndroidApiLevel.N, "getAndAddLong", Object.class, long.class, long.class);
-    addMethod(AndroidApiLevel.N, "getAndSetInt", Object.class, long.class, int.class);
-    addMethod(AndroidApiLevel.N, "getAndSetLong", Object.class, long.class, long.class);
-    addMethod(AndroidApiLevel.N, "getAndSetObject", Object.class, long.class, Object.class);
-    addMethod(AndroidApiLevel.L_MR1, "getArrayBaseOffsetForComponentType", Class.class);
-    addMethod(AndroidApiLevel.L_MR1, "getArrayIndexScaleForComponentType", Class.class);
-    addMethod(AndroidApiLevel.N, "getBoolean", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getByte", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getByte", long.class);
-    addMethod(AndroidApiLevel.N, "getChar", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getChar", long.class);
-    addMethod(AndroidApiLevel.N, "getDouble", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getDouble", long.class);
-    addMethod(AndroidApiLevel.N, "getFloat", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getFloat", long.class);
-    addMethod(always, "getInt", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getInt", long.class);
-    addMethod(always, "getIntVolatile", Object.class, long.class);
-    addMethod(always, "getLong", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getLong", long.class);
-    addMethod(always, "getLongVolatile", Object.class, long.class);
-    addMethod(always, "getObject", Object.class, long.class);
-    addMethod(always, "getObjectVolatile", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getShort", Object.class, long.class);
-    addMethod(AndroidApiLevel.N, "getShort", long.class);
-    addMethod(always, "getUnsafe");
-    addMethod(AndroidApiLevel.N, "loadFence");
-    addMethod(always, "objectFieldOffset", Field.class);
-    addMethod(AndroidApiLevel.N, "pageSize");
-    addMethod(always, "park", boolean.class, long.class);
-    addMethod(AndroidApiLevel.N, "putBoolean", Object.class, long.class, boolean.class);
-    addMethod(AndroidApiLevel.N, "putByte", Object.class, long.class, byte.class);
-    addMethod(AndroidApiLevel.N, "putByte", long.class, byte.class);
-    addMethod(AndroidApiLevel.N, "putChar", Object.class, long.class, char.class);
-    addMethod(AndroidApiLevel.N, "putChar", long.class, char.class);
-    addMethod(AndroidApiLevel.N, "putDouble", Object.class, long.class, double.class);
-    addMethod(AndroidApiLevel.N, "putDouble", long.class, double.class);
-    addMethod(AndroidApiLevel.N, "putFloat", Object.class, long.class, float.class);
-    addMethod(AndroidApiLevel.N, "putFloat", long.class, float.class);
-    addMethod(always, "putInt", Object.class, long.class, int.class);
-    addMethod(AndroidApiLevel.N, "putInt", long.class, int.class);
-    addMethod(always, "putIntVolatile", Object.class, long.class, int.class);
-    addMethod(always, "putLong", Object.class, long.class, long.class);
-    addMethod(AndroidApiLevel.N, "putLong", long.class, long.class);
-    addMethod(always, "putLongVolatile", Object.class, long.class, long.class);
-    addMethod(always, "putObject", Object.class, long.class, Object.class);
-    addMethod(always, "putObjectVolatile", Object.class, long.class, Object.class);
-    addMethod(always, "putOrderedInt", Object.class, long.class, int.class);
-    addMethod(always, "putOrderedLong", Object.class, long.class, long.class);
-    addMethod(always, "putOrderedObject", Object.class, long.class, Object.class);
-    addMethod(AndroidApiLevel.N, "putShort", Object.class, long.class, short.class);
-    addMethod(AndroidApiLevel.N, "putShort", long.class, short.class);
-    addMethod(AndroidApiLevel.N, "setMemory", long.class, long.class, byte.class);
-    addMethod(AndroidApiLevel.N, "storeFence");
-    addMethod(always, "unpark", Object.class);
+
+    addClass(always);
+
+    addField("INVALID_FIELD_OFFSET", AndroidApiLevel.N, Reference.INT);
+    addField("THE_ONE", always, SUN_MISC_UNSAFE_TYPE);
+    addField("theInternalUnsafe", AndroidApiLevel.MAIN, JDK_INTERNAL_MISC_UNSAFE_TYPE);
+    addField("theUnsafe", AndroidApiLevel.K, SUN_MISC_UNSAFE_TYPE);
+
+    addMethod("addressSize", AndroidApiLevel.N, INT);
+    addMethod("allocateInstance", AndroidApiLevel.J, OBJECT, CLASS);
+    addMethod("allocateMemory", AndroidApiLevel.N, LONG, LONG);
+    addMethod("arrayBaseOffset", always, INT, CLASS);
+    addMethod("arrayIndexScale", always, INT, CLASS);
+    addMethod("compareAndSwapInt", always, BOOL, OBJECT, LONG, INT, INT);
+    addMethod("compareAndSwapLong", always, BOOL, OBJECT, LONG, LONG, LONG);
+    addMethod("compareAndSwapObject", always, BOOL, OBJECT, LONG, OBJECT, OBJECT);
+    addMethod("copyMemory", AndroidApiLevel.N, VOID, LONG, LONG, LONG);
+    addMethod("copyMemoryFromPrimitiveArray", AndroidApiLevel.N, VOID, OBJECT, LONG, LONG, LONG);
+    addMethod("copyMemoryToPrimitiveArray", AndroidApiLevel.N, VOID, LONG, OBJECT, LONG, LONG);
+    addMethod("forbidObtainingRecordFieldOffsets", AndroidApiLevel.MAIN, BOOL);
+    addMethod("freeMemory", AndroidApiLevel.N, VOID, LONG);
+    addMethod("fullFence", AndroidApiLevel.N, VOID);
+    addMethod("getAndAddInt", AndroidApiLevel.N, INT, OBJECT, LONG, INT);
+    addMethod("getAndAddLong", AndroidApiLevel.N, LONG, OBJECT, LONG, LONG);
+    addMethod("getAndSetInt", AndroidApiLevel.N, INT, OBJECT, LONG, INT);
+    addMethod("getAndSetLong", AndroidApiLevel.N, LONG, OBJECT, LONG, LONG);
+    addMethod("getAndSetObject", AndroidApiLevel.N, OBJECT, OBJECT, LONG, OBJECT);
+    addMethod("getArrayBaseOffsetForComponentType", AndroidApiLevel.L_MR1, INT, CLASS);
+    addMethod("getArrayIndexScaleForComponentType", AndroidApiLevel.L_MR1, INT, CLASS);
+    addMethod("getBoolean", AndroidApiLevel.N, BOOL, OBJECT, LONG);
+    addMethod("getByte", AndroidApiLevel.N, BYTE, LONG);
+    addMethod("getByte", AndroidApiLevel.N, BYTE, OBJECT, LONG);
+    addMethod("getChar", AndroidApiLevel.N, CHAR, LONG);
+    addMethod("getChar", AndroidApiLevel.N, CHAR, OBJECT, LONG);
+    addMethod("getDouble", AndroidApiLevel.N, DOUBLE, LONG);
+    addMethod("getDouble", AndroidApiLevel.N, DOUBLE, OBJECT, LONG);
+    addMethod("getFloat", AndroidApiLevel.N, FLOAT, LONG);
+    addMethod("getFloat", AndroidApiLevel.N, FLOAT, OBJECT, LONG);
+    addMethod("getInt", AndroidApiLevel.N, INT, LONG);
+    addMethod("getInt", always, INT, OBJECT, LONG);
+    addMethod("getIntVolatile", always, INT, OBJECT, LONG);
+    addMethod("getLong", AndroidApiLevel.N, LONG, LONG);
+    addMethod("getLong", always, LONG, OBJECT, LONG);
+    addMethod("getLongVolatile", always, LONG, OBJECT, LONG);
+    addMethod("getObject", always, OBJECT, OBJECT, LONG);
+    addMethod("getObjectVolatile", always, OBJECT, OBJECT, LONG);
+    addMethod("getShort", AndroidApiLevel.N, SHORT, LONG);
+    addMethod("getShort", AndroidApiLevel.N, SHORT, OBJECT, LONG);
+    addMethod("getUnsafe", always, SUN_MISC_UNSAFE);
+    addMethod("loadFence", AndroidApiLevel.N, VOID);
+    addMethod("objectFieldOffset", always, LONG, FIELD);
+    addMethod("pageSize", AndroidApiLevel.N, INT);
+    addMethod("park", always, VOID, BOOL, LONG);
+    addMethod("putBoolean", AndroidApiLevel.N, VOID, OBJECT, LONG, BOOL);
+    addMethod("putByte", AndroidApiLevel.N, VOID, LONG, BYTE);
+    addMethod("putByte", AndroidApiLevel.N, VOID, OBJECT, LONG, BYTE);
+    addMethod("putChar", AndroidApiLevel.N, VOID, LONG, CHAR);
+    addMethod("putChar", AndroidApiLevel.N, VOID, OBJECT, LONG, CHAR);
+    addMethod("putDouble", AndroidApiLevel.N, VOID, LONG, DOUBLE);
+    addMethod("putDouble", AndroidApiLevel.N, VOID, OBJECT, LONG, DOUBLE);
+    addMethod("putFloat", AndroidApiLevel.N, VOID, LONG, FLOAT);
+    addMethod("putFloat", AndroidApiLevel.N, VOID, OBJECT, LONG, FLOAT);
+    addMethod("putInt", AndroidApiLevel.N, VOID, LONG, INT);
+    addMethod("putInt", always, VOID, OBJECT, LONG, INT);
+    addMethod("putIntVolatile", always, VOID, OBJECT, LONG, INT);
+    addMethod("putLong", AndroidApiLevel.N, VOID, LONG, LONG);
+    addMethod("putLong", always, VOID, OBJECT, LONG, LONG);
+    addMethod("putLongVolatile", always, VOID, OBJECT, LONG, LONG);
+    addMethod("putObject", always, VOID, OBJECT, LONG, OBJECT);
+    addMethod("putObjectVolatile", always, VOID, OBJECT, LONG, OBJECT);
+    addMethod("putOrderedInt", always, VOID, OBJECT, LONG, INT);
+    addMethod("putOrderedLong", always, VOID, OBJECT, LONG, LONG);
+    addMethod("putOrderedObject", always, VOID, OBJECT, LONG, OBJECT);
+    addMethod("putShort", AndroidApiLevel.N, VOID, LONG, SHORT);
+    addMethod("putShort", AndroidApiLevel.N, VOID, OBJECT, LONG, SHORT);
+    addMethod("setMemory", AndroidApiLevel.N, VOID, LONG, LONG, BYTE);
+    addMethod("storeFence", AndroidApiLevel.N, VOID);
+    addMethod("unpark", always, VOID, OBJECT);
   }
 
   static class TestClass {
+    private static final String UNSAFE = "sun.misc.Unsafe";
 
     public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
-      printExistence("addressSize");
-      printExistence("allocateInstance", Class.class);
-      printExistence("allocateMemory", long.class);
-      printExistence("arrayBaseOffset", Class.class);
-      printExistence("arrayIndexScale", Class.class);
-      printExistence("compareAndSwapInt", Object.class, long.class, int.class, int.class);
-      printExistence("compareAndSwapLong", Object.class, long.class, long.class, long.class);
-      printExistence("compareAndSwapObject", Object.class, long.class, Object.class, Object.class);
-      printExistence("copyMemory", long.class, long.class, long.class);
-      printExistence(
-          "copyMemoryFromPrimitiveArray", Object.class, long.class, long.class, long.class);
-      printExistence(
-          "copyMemoryToPrimitiveArray", long.class, Object.class, long.class, long.class);
-      printExistence("forbidObtainingRecordFieldOffsets");
-      printExistence("freeMemory", long.class);
-      printExistence("fullFence");
-      printExistence("getAndAddInt", Object.class, long.class, int.class);
-      printExistence("getAndAddLong", Object.class, long.class, long.class);
-      printExistence("getAndSetInt", Object.class, long.class, int.class);
-      printExistence("getAndSetLong", Object.class, long.class, long.class);
-      printExistence("getAndSetObject", Object.class, long.class, Object.class);
-      printExistence("getArrayBaseOffsetForComponentType", Class.class);
-      printExistence("getArrayIndexScaleForComponentType", Class.class);
-      printExistence("getBoolean", Object.class, long.class);
-      printExistence("getByte", Object.class, long.class);
-      printExistence("getByte", long.class);
-      printExistence("getChar", Object.class, long.class);
-      printExistence("getChar", long.class);
-      printExistence("getDouble", Object.class, long.class);
-      printExistence("getDouble", long.class);
-      printExistence("getFloat", Object.class, long.class);
-      printExistence("getFloat", long.class);
-      printExistence("getInt", Object.class, long.class);
-      printExistence("getInt", long.class);
-      printExistence("getIntVolatile", Object.class, long.class);
-      printExistence("getLong", Object.class, long.class);
-      printExistence("getLong", long.class);
-      printExistence("getLongVolatile", Object.class, long.class);
-      printExistence("getObject", Object.class, long.class);
-      printExistence("getObjectVolatile", Object.class, long.class);
-      printExistence("getShort", Object.class, long.class);
-      printExistence("getShort", long.class);
-      printExistence("getUnsafe");
-      printExistence("loadFence");
-      printExistence("objectFieldOffset", Field.class);
-      printExistence("pageSize");
-      printExistence("park", boolean.class, long.class);
-      printExistence("putBoolean", Object.class, long.class, boolean.class);
-      printExistence("putByte", Object.class, long.class, byte.class);
-      printExistence("putByte", long.class, byte.class);
-      printExistence("putChar", Object.class, long.class, char.class);
-      printExistence("putChar", long.class, char.class);
-      printExistence("putDouble", Object.class, long.class, double.class);
-      printExistence("putDouble", long.class, double.class);
-      printExistence("putFloat", Object.class, long.class, float.class);
-      printExistence("putFloat", long.class, float.class);
-      printExistence("putInt", Object.class, long.class, int.class);
-      printExistence("putInt", long.class, int.class);
-      printExistence("putIntVolatile", Object.class, long.class, int.class);
-      printExistence("putLong", Object.class, long.class, long.class);
-      printExistence("putLong", long.class, long.class);
-      printExistence("putLongVolatile", Object.class, long.class, long.class);
-      printExistence("putObject", Object.class, long.class, Object.class);
-      printExistence("putObjectVolatile", Object.class, long.class, Object.class);
-      printExistence("putOrderedInt", Object.class, long.class, int.class);
-      printExistence("putOrderedLong", Object.class, long.class, long.class);
-      printExistence("putOrderedObject", Object.class, long.class, Object.class);
-      printExistence("putShort", Object.class, long.class, short.class);
-      printExistence("putShort", long.class, short.class);
-      printExistence("setMemory", long.class, long.class, byte.class);
-      printExistence("storeFence");
-      printExistence("unpark", Object.class);
+      checkClass(UNSAFE);
+
+      checkField("INVALID_FIELD_OFFSET", int.class);
+      checkField("THE_ONE", UNSAFE);
+      checkField("theInternalUnsafe", "jdk.internal.misc.Unsafe");
+      checkField("theUnsafe", UNSAFE);
+
+      checkMethod("addressSize", int.class);
+      checkMethod("allocateInstance", Object.class, Class.class);
+      checkMethod("allocateMemory", long.class, long.class);
+      checkMethod("arrayBaseOffset", int.class, Class.class);
+      checkMethod("arrayIndexScale", int.class, Class.class);
+      checkMethod(
+          "compareAndSwapInt", boolean.class, Object.class, long.class, int.class, int.class);
+      checkMethod(
+          "compareAndSwapLong", boolean.class, Object.class, long.class, long.class, long.class);
+      checkMethod(
+          "compareAndSwapObject",
+          boolean.class,
+          Object.class,
+          long.class,
+          Object.class,
+          Object.class);
+      checkMethod("copyMemory", void.class, long.class, long.class, long.class);
+      checkMethod(
+          "copyMemoryFromPrimitiveArray",
+          void.class,
+          Object.class,
+          long.class,
+          long.class,
+          long.class);
+      checkMethod(
+          "copyMemoryToPrimitiveArray",
+          void.class,
+          long.class,
+          Object.class,
+          long.class,
+          long.class);
+      checkMethod("forbidObtainingRecordFieldOffsets", boolean.class);
+      checkMethod("freeMemory", void.class, long.class);
+      checkMethod("fullFence", void.class);
+      checkMethod("getAndAddInt", int.class, Object.class, long.class, int.class);
+      checkMethod("getAndAddLong", long.class, Object.class, long.class, long.class);
+      checkMethod("getAndSetInt", int.class, Object.class, long.class, int.class);
+      checkMethod("getAndSetLong", long.class, Object.class, long.class, long.class);
+      checkMethod("getAndSetObject", Object.class, Object.class, long.class, Object.class);
+      checkMethod("getArrayBaseOffsetForComponentType", int.class, Class.class);
+      checkMethod("getArrayIndexScaleForComponentType", int.class, Class.class);
+      checkMethod("getBoolean", boolean.class, Object.class, long.class);
+      checkMethod("getByte", byte.class, Object.class, long.class);
+      checkMethod("getByte", byte.class, long.class);
+      checkMethod("getChar", char.class, Object.class, long.class);
+      checkMethod("getChar", char.class, long.class);
+      checkMethod("getDouble", double.class, Object.class, long.class);
+      checkMethod("getDouble", double.class, long.class);
+      checkMethod("getFloat", float.class, Object.class, long.class);
+      checkMethod("getFloat", float.class, long.class);
+      checkMethod("getInt", int.class, Object.class, long.class);
+      checkMethod("getInt", int.class, long.class);
+      checkMethod("getIntVolatile", int.class, Object.class, long.class);
+      checkMethod("getLong", long.class, Object.class, long.class);
+      checkMethod("getLong", long.class, long.class);
+      checkMethod("getLongVolatile", long.class, Object.class, long.class);
+      checkMethod("getObject", Object.class, Object.class, long.class);
+      checkMethod("getObjectVolatile", Object.class, Object.class, long.class);
+      checkMethod("getShort", short.class, Object.class, long.class);
+      checkMethod("getShort", short.class, long.class);
+      checkMethod("getUnsafe", UNSAFE);
+      checkMethod("loadFence", void.class);
+      checkMethod("objectFieldOffset", long.class, Field.class);
+      checkMethod("pageSize", int.class);
+      checkMethod("park", void.class, boolean.class, long.class);
+      checkMethod("putBoolean", void.class, Object.class, long.class, boolean.class);
+      checkMethod("putByte", void.class, Object.class, long.class, byte.class);
+      checkMethod("putByte", void.class, long.class, byte.class);
+      checkMethod("putChar", void.class, Object.class, long.class, char.class);
+      checkMethod("putChar", void.class, long.class, char.class);
+      checkMethod("putDouble", void.class, Object.class, long.class, double.class);
+      checkMethod("putDouble", void.class, long.class, double.class);
+      checkMethod("putFloat", void.class, Object.class, long.class, float.class);
+      checkMethod("putFloat", void.class, long.class, float.class);
+      checkMethod("putInt", void.class, Object.class, long.class, int.class);
+      checkMethod("putInt", void.class, long.class, int.class);
+      checkMethod("putIntVolatile", void.class, Object.class, long.class, int.class);
+      checkMethod("putLong", void.class, Object.class, long.class, long.class);
+      checkMethod("putLong", void.class, long.class, long.class);
+      checkMethod("putLongVolatile", void.class, Object.class, long.class, long.class);
+      checkMethod("putObject", void.class, Object.class, long.class, Object.class);
+      checkMethod("putObjectVolatile", void.class, Object.class, long.class, Object.class);
+      checkMethod("putOrderedInt", void.class, Object.class, long.class, int.class);
+      checkMethod("putOrderedLong", void.class, Object.class, long.class, long.class);
+      checkMethod("putOrderedObject", void.class, Object.class, long.class, Object.class);
+      checkMethod("putShort", void.class, Object.class, long.class, short.class);
+      checkMethod("putShort", void.class, long.class, short.class);
+      checkMethod("setMemory", void.class, long.class, long.class, byte.class);
+      checkMethod("storeFence", void.class);
+      checkMethod("unpark", void.class, Object.class);
     }
 
-    private static String parameterString(Class<?>... parameterTypes) {
-      StringBuilder parameterList = new StringBuilder();
-      boolean isFirst = true;
-      for (Class<?> clazz : parameterTypes) {
-        if (isFirst) {
-          isFirst = false;
-        } else {
-          parameterList.append(", ");
-        }
-        parameterList.append(clazz.getCanonicalName());
-      }
-      return parameterList.toString();
+    private static void checkClass(String name) {
+      boolean doesClassExit = getClass(name) != null;
+      System.out.println(name + ": " + doesClassExit);
     }
 
-    private static Class<?> cachedUnsafe = null;
-
-    private static Class<?> unsafeClass() throws ClassNotFoundException {
-      if (cachedUnsafe == null) {
-        cachedUnsafe = Class.forName("sun.misc.Unsafe");
-      }
-      return cachedUnsafe;
-    }
-
-    private static void printExistence(String name, Class<?>... paramterTypes)
-        throws ClassNotFoundException {
-      boolean doesExist = doesMethodExist(name, paramterTypes);
-      String message = name + "(" + parameterString(paramterTypes) + "): " + doesExist;
-      System.out.println(message);
-    }
-
-    private static boolean doesMethodExist(String name, Class<?>... parameterTypes)
-        throws ClassNotFoundException {
+    private static Class<?> getClass(String name) {
       try {
-        unsafeClass().getDeclaredMethod(name, parameterTypes);
-        return true;
+        return Class.forName(name);
+      } catch (ClassNotFoundException e) {
+        return null;
+      }
+    }
+
+    private static void checkField(String name, Class<?> type) {
+      printField(name, typeString(type), doesUnsafeFieldExist(name, type));
+    }
+
+    private static void checkField(String name, String type) {
+      Class<?> fieldType = getClass(type);
+      if (fieldType == null) {
+        printField(name, type, false);
+      } else {
+        checkField(name, fieldType);
+      }
+    }
+
+    private static String typeString(Class<?> type) {
+      return type.getCanonicalName();
+    }
+
+    private static boolean doesUnsafeFieldExist(String name, Class<?> type) {
+      Class<?> unsafe = getClass(UNSAFE);
+      if (unsafe == null) {
+        return false;
+      }
+      try {
+        Field field = unsafe.getDeclaredField(name);
+        return type.equals(field.getType());
+      } catch (NoSuchFieldException e) {
+        return false;
+      }
+    }
+
+    private static void printField(String name, String type, boolean found) {
+      System.out.println(type + " " + UNSAFE + "." + name + ": " + found);
+    }
+
+    private static void checkMethod(String name, Class<?> returnType, Class<?>... parameterTypes) {
+      boolean doesExist = doesMethodExist(name, returnType, parameterTypes);
+      String[] params = new String[parameterTypes.length];
+      for (int i = 0; i < parameterTypes.length; i++) {
+        params[i] = typeString(parameterTypes[i]);
+      }
+      printMethod(name, typeString(returnType), params, doesExist);
+    }
+
+    private static void checkMethod(String name, String returnType) {
+      Class<?> returnTypeClass = getClass(returnType);
+      if (returnTypeClass == null) {
+        printMethod(name, returnType, new String[0], false);
+      } else {
+        checkMethod(name, returnTypeClass);
+      }
+    }
+
+    private static void printMethod(
+        String name, String returnType, String[] parameterTypes, boolean found) {
+      String output =
+          returnType
+              + " "
+              + UNSAFE
+              + "."
+              + name
+              + "("
+              + String.join(", ", parameterTypes)
+              + "): "
+              + found;
+      System.out.println(output);
+    }
+
+    private static boolean doesMethodExist(
+        String name, Class<?> returnType, Class<?>... parameterTypes) {
+      Class<?> unsafe = getClass(UNSAFE);
+      if (unsafe == null) {
+        return false;
+      }
+      try {
+        Method method = unsafe.getDeclaredMethod(name, parameterTypes);
+        return returnType.equals(method.getReturnType());
       } catch (NoSuchMethodException e) {
         return false;
       }
     }
   }
+
 }