Merge "Revert "Support niceness-based priority calls"" into main
diff --git a/libart/src/main/java/java/lang/invoke/StaticFieldVarHandle.java b/libart/src/main/java/java/lang/invoke/StaticFieldVarHandle.java
index c834d76..3b06df2 100644
--- a/libart/src/main/java/java/lang/invoke/StaticFieldVarHandle.java
+++ b/libart/src/main/java/java/lang/invoke/StaticFieldVarHandle.java
@@ -33,7 +33,7 @@
 
     static StaticFieldVarHandle create(Field staticField) {
         assert Modifier.isStatic(staticField.getModifiers());
-        // TODO(b/379259800): should this be handled at the invocation?
+        // TODO(b/399619087): Make initialization lazy.
         MethodHandleStatics.UNSAFE.ensureClassInitialized(staticField.getDeclaringClass());
         return new StaticFieldVarHandle(staticField);
     }
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java
index 50363cf..5465375 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleAccessorsTest.java
@@ -16,8 +16,6 @@
 
 package libcore.java.lang.invoke;
 
-import junit.framework.TestCase;
-
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.WrongMethodTypeException;
@@ -26,7 +24,12 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-public class MethodHandleAccessorsTest extends junit.framework.TestCase {
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MethodHandleAccessorsTest {
     public static class ValueHolder {
         public boolean m_z = false;
         public byte m_b = 0;
@@ -38,6 +41,16 @@
         public long m_j = 0;
         public String m_l = "a";
 
+        public volatile boolean m_v_z = false;
+        public volatile byte m_v_b = 0;
+        public volatile char m_v_c = 'a';
+        public volatile short m_v_s = 0;
+        public volatile int m_v_i = 0;
+        public volatile float m_v_f = 0.0f;
+        public volatile double m_v_d = 0.0;
+        public volatile long m_v_j = 0;
+        public volatile String m_v_l = "a";
+
         public static boolean s_z;
         public static byte s_b;
         public static char s_c;
@@ -48,6 +61,17 @@
         public static long s_j;
         public static String s_l;
 
+        public static boolean s_v_z;
+        public static byte s_v_b;
+        public static char s_v_c;
+        public static short s_v_s;
+        public static int s_v_i;
+        public static float s_v_f;
+        public static double s_v_d;
+        public static long s_v_j;
+        public static String s_v_l;
+
+
         public final int m_fi = 0xa5a5a5a5;
         public static final int s_fi = 0x5a5a5a5a;
     }
@@ -533,6 +557,7 @@
                 resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SGET));
     }
 
+    @Test
     public void testBooleanSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -540,19 +565,34 @@
         boolean[] booleans = {false, true, false};
         for (boolean b : booleans) {
             Boolean boxed = Boolean.valueOf(b);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_z", boolean.class),
                 valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_z", boolean.class),
                 valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_z == b);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_z", boolean.class),
+                valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_z", boolean.class),
+                valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_z == b);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class),
                 valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class),
                 valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_z == b);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_z", boolean.class),
+                valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_z", boolean.class),
+                valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_z == b);
         }
     }
 
+    @Test
     public void testByteSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -560,19 +600,33 @@
         byte[] bytes = {(byte) 0x73, (byte) 0xfe};
         for (byte b : bytes) {
             Byte boxed = Byte.valueOf(b);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_b", byte.class),
                 valueHolder, PrimitiveType.Byte, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_b", byte.class),
                 valueHolder, PrimitiveType.Byte, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_b == b);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_b", byte.class),
+                valueHolder, PrimitiveType.Byte, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_b", byte.class),
+                valueHolder, PrimitiveType.Byte, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_b == b);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_b", byte.class),
                 valueHolder, PrimitiveType.Byte, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_b", byte.class),
                 valueHolder, PrimitiveType.Byte, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_b == b);
-        }
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_b", byte.class),
+                valueHolder, PrimitiveType.Byte, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_b", byte.class),
+                valueHolder, PrimitiveType.Byte, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_b == b);        }
     }
 
+    @Test
     public void testCharSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -580,19 +634,34 @@
         char[] chars = {'a', 'b', 'c'};
         for (char c : chars) {
             Character boxed = Character.valueOf(c);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_c", char.class),
                 valueHolder, PrimitiveType.Char, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_c", char.class),
                 valueHolder, PrimitiveType.Char, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_c == c);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_c", char.class),
+                valueHolder, PrimitiveType.Char, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_c", char.class),
+                valueHolder, PrimitiveType.Char, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_c == c);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_c", char.class),
                 valueHolder, PrimitiveType.Char, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_c", char.class),
                 valueHolder, PrimitiveType.Char, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_c == c);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_c", char.class),
+                valueHolder, PrimitiveType.Char, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_c", char.class),
+                valueHolder, PrimitiveType.Char, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_c == c);
         }
     }
 
+    @Test
     public void testShortSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -600,19 +669,34 @@
         short[] shorts = {(short) 0x1234, (short) 0x4321};
         for (short s : shorts) {
             Short boxed = Short.valueOf(s);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_s", short.class),
                 valueHolder, PrimitiveType.Short, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_s", short.class),
                 valueHolder, PrimitiveType.Short, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_s == s);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_s", short.class),
+                valueHolder, PrimitiveType.Short, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_s", short.class),
+                valueHolder, PrimitiveType.Short, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_s == s);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_s", short.class),
                 valueHolder, PrimitiveType.Short, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_s", short.class),
                 valueHolder, PrimitiveType.Short, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_s == s);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_s", short.class),
+                valueHolder, PrimitiveType.Short, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_s", short.class),
+                valueHolder, PrimitiveType.Short, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_s == s);
         }
     }
 
+    @Test
     public void testIntSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -620,19 +704,34 @@
         int[] ints = {-100000000, 10000000};
         for (int i : ints) {
             Integer boxed = Integer.valueOf(i);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_i", int.class),
                 valueHolder, PrimitiveType.Int, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_i", int.class),
                 valueHolder, PrimitiveType.Int, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_i == i);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_i", int.class),
+                valueHolder, PrimitiveType.Int, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_i", int.class),
+                valueHolder, PrimitiveType.Int, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_i == i);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_i", int.class),
                 valueHolder, PrimitiveType.Int, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_i", int.class),
                 valueHolder, PrimitiveType.Int, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_i == i);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_i", int.class),
+                valueHolder, PrimitiveType.Int, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_i", int.class),
+                valueHolder, PrimitiveType.Int, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_i == i);
         }
     }
 
+    @Test
     public void testFloatSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -640,19 +739,34 @@
         float[] floats = {0.99f, -1.23e-17f};
         for (float f : floats) {
             Float boxed = Float.valueOf(f);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_f", float.class),
                 valueHolder, PrimitiveType.Float, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_f", float.class),
                 valueHolder, PrimitiveType.Float, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_f == f);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_f", float.class),
+                    valueHolder, PrimitiveType.Float, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_f", float.class),
+                    valueHolder, PrimitiveType.Float, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_f == f);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_f", float.class),
                 valueHolder, PrimitiveType.Float, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_f", float.class),
                 valueHolder, PrimitiveType.Float, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_f == f);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_f", float.class),
+                    valueHolder, PrimitiveType.Float, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_f", float.class),
+                    valueHolder, PrimitiveType.Float, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_f == f);
         }
     }
 
+    @Test
     public void testDoubleSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -665,14 +779,28 @@
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_d", double.class),
                 valueHolder, PrimitiveType.Double, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_d == d);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_d", double.class),
+                    valueHolder, PrimitiveType.Double, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_d", double.class),
+                    valueHolder, PrimitiveType.Double, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_d == d);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_d", double.class),
                 valueHolder, PrimitiveType.Double, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_d", double.class),
                 valueHolder, PrimitiveType.Double, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_d == d);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_d", double.class),
+                    valueHolder, PrimitiveType.Double, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_d", double.class),
+                    valueHolder, PrimitiveType.Double, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_d == d);
         }
     }
 
+    @Test
     public void testLongSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -680,19 +808,34 @@
         long[] longs = {0x0123456789abcdefl, 0xfedcba9876543210l};
         for (long j : longs) {
             Long boxed = Long.valueOf(j);
+
             tryAccessor(lookup.findSetter(ValueHolder.class, "m_j", long.class),
                 valueHolder, PrimitiveType.Long, boxed, AccessorType.IPUT);
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_j", long.class),
                 valueHolder, PrimitiveType.Long, boxed, AccessorType.IGET);
             assertTrue(valueHolder.m_j == j);
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_j", long.class),
+                    valueHolder, PrimitiveType.Long, boxed, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_j", long.class),
+                    valueHolder, PrimitiveType.Long, boxed, AccessorType.IGET);
+            assertTrue(valueHolder.m_v_j == j);
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_j", long.class),
                 valueHolder, PrimitiveType.Long, boxed, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_j", long.class),
                 valueHolder, PrimitiveType.Long, boxed, AccessorType.SGET);
             assertTrue(ValueHolder.s_j == j);
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_j", long.class),
+                    valueHolder, PrimitiveType.Long, boxed, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_j", long.class),
+                    valueHolder, PrimitiveType.Long, boxed, AccessorType.SGET);
+            assertTrue(ValueHolder.s_v_j == j);
         }
     }
 
+    @Test
     public void testStringSettersAndGetters() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -704,14 +847,28 @@
             tryAccessor(lookup.findGetter(ValueHolder.class, "m_l", String.class),
                     valueHolder, PrimitiveType.String, s, AccessorType.IGET);
             assertTrue(s.equals(valueHolder.m_l));
+
+            tryAccessor(lookup.findSetter(ValueHolder.class, "m_v_l", String.class),
+                    valueHolder, PrimitiveType.String, s, AccessorType.IPUT);
+            tryAccessor(lookup.findGetter(ValueHolder.class, "m_v_l", String.class),
+                    valueHolder, PrimitiveType.String, s, AccessorType.IGET);
+            assertTrue(s.equals(valueHolder.m_v_l));
+
             tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_l", String.class),
                     valueHolder, PrimitiveType.String, s, AccessorType.SPUT);
             tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_l", String.class),
                     valueHolder, PrimitiveType.String, s, AccessorType.SGET);
             assertTrue(s.equals(ValueHolder.s_l));
+
+            tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_v_l", String.class),
+                    valueHolder, PrimitiveType.String, s, AccessorType.SPUT);
+            tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_v_l", String.class),
+                    valueHolder, PrimitiveType.String, s, AccessorType.SGET);
+            assertTrue(s.equals(ValueHolder.s_v_l));
         }
     }
 
+    @Test
     public void testLookup() throws Throwable {
         // NB having a static field test here is essential for
         // this test. MethodHandles need to ensure the class
@@ -755,6 +912,7 @@
         } catch (IllegalAccessException e) {}
     }
 
+    @Test
     public void testStaticGetter() throws Throwable {
         MethodHandles.Lookup lookup = MethodHandles.lookup();
         MethodHandle h0 = lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class);
@@ -777,6 +935,7 @@
         } catch (WrongMethodTypeException e) {}
     }
 
+    @Test
     public void testMemberGetter() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -810,6 +969,7 @@
         return Float.valueOf(-7.77f);
     }
 
+    @Test
     public void testMemberSetter() throws Throwable {
         ValueHolder valueHolder = new ValueHolder();
         MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -866,6 +1026,7 @@
         } catch (WrongMethodTypeException e) {}
     }
 
+    @Test
     public void testStaticSetter() throws Throwable {
         MethodHandles.Lookup lookup = MethodHandles.lookup();
         MethodHandle h0 = lookup.findStaticSetter(ValueHolder.class, "s_f", float.class);
@@ -919,4 +1080,40 @@
             fail();
         } catch (WrongMethodTypeException e) {}
     }
+
+    @Test
+    public void throws_wmte_when_too_many_arguments_are_supplied() throws Throwable {
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        MethodHandle setter = lookup.findStaticSetter(ValueHolder.class, "s_f", float.class);
+
+        try {
+            setter.invokeExact(0f, 1);
+            fail("Should throw WMTE");
+        } catch (WrongMethodTypeException ignored) {}
+
+        try {
+            setter.invokeExact(0f, 1, 2);
+            fail("Should throw WMTE");
+        } catch (WrongMethodTypeException ignored) {}
+
+        try {
+            setter.invokeExact(0f, 1, 2, 3);
+            fail("Should throw WMTE");
+        } catch (WrongMethodTypeException ignored) {}
+
+        try {
+            setter.invokeExact(0f, 1, 2, 3, 4);
+            fail("Should throw WMTE");
+        } catch (WrongMethodTypeException ignored) {}
+
+        try {
+            setter.invokeExact(0f, 1, 2, 3, 4, 5);
+            fail("Should throw WMTE");
+        } catch (WrongMethodTypeException ignored) {}
+
+        try {
+            setter.invokeExact(0f, 1, 2, 3, 4, 5, "str");
+            fail("Should throw WMTE");
+        } catch (WrongMethodTypeException ignored) {}
+    }
 }
diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
index baa3344..eaa5df0 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
@@ -16,6 +16,9 @@
 
 package libcore.java.text;
 
+import libcore.test.annotation.NonMts;
+import libcore.test.reasons.NonMtsReasons;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ObjectInputStream;
@@ -156,6 +159,7 @@
     }
 
     // http://b/7955614
+    @NonMts(reason = NonMtsReasons.ICU_VERSION_DEPENDENCY)
     public void test_getZoneStrings_Apia() {
         String[][] array = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
 
@@ -165,9 +169,9 @@
             // "GMT" strings for the short names.
             if (row[0].equals("Pacific/Apia")) {
                 TimeZone apiaTz = TimeZone.getTimeZone("Pacific/Apia");
-                assertEquals("Apia Standard Time", row[1]);
+                assertEquals("Samoa Standard Time", row[1]);
                 assertEquals(formattedStandardTimeOffset(apiaTz), row[2]);
-                assertEquals("Apia Daylight Time", row[3]);
+                assertEquals("Samoa Daylight Time", row[3]);
                 assertEquals(formattedDstOffset(apiaTz), row[4]);
             }
         }
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 2dcd992..f4bcc80 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -375,10 +375,11 @@
     }
 
     // http://b/7955614
+    @NonMts(reason = NonMtsReasons.ICU_VERSION_DEPENDENCY)
     public void testApia() {
         TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
-        assertEquals("Apia Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
-        assertEquals("Apia Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
+        assertEquals("Samoa Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
+        assertEquals("Samoa Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
 
         long samoaStandardTime = 1630315635000L; // 30 Aug 2021
         long samoaDst = 1614504435000L; // 28 Feb 2021
diff --git a/ojluni/src/main/java/java/lang/invoke/DirectMethodHandle.java b/ojluni/src/main/java/java/lang/invoke/DirectMethodHandle.java
new file mode 100644
index 0000000..2495b12
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/DirectMethodHandle.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import static java.lang.invoke.MethodHandleStatics.UNSAFE;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import jdk.internal.vm.annotation.ForceInline;
+
+// Android-changed: currently only 4 methods and the Holder class are needed, hence not importing
+// entire file.
+/**
+ * The flavor of method handle which implements a constant reference
+ * to a class member.
+ * @author jrose
+ */
+class DirectMethodHandle {
+
+    @ForceInline
+    /*non-public*/
+    static long fieldOffset(Object accessorObj) {
+        // Note: We return a long because that is what Unsafe.getObject likes.
+        // We store a plain int because it is more compact.
+        // Android-changed: there is only MethodHandleImpl.
+        // return ((Accessor)accessorObj).fieldOffset;
+        return ((MethodHandleImpl) accessorObj).field.getOffset();
+    }
+
+    @ForceInline
+    /*non-public*/
+    static Object checkBase(Object obj) {
+        // Note that the object's class has already been verified,
+        // since the parameter type of the Accessor method handle
+        // is either member.getDeclaringClass or a subclass.
+        // This was verified in DirectMethodHandle.make.
+        // Therefore, the only remaining check is for null.
+        // Since this check is *not* guaranteed by Unsafe.getInt
+        // and its siblings, we need to make an explicit one here.
+        return Objects.requireNonNull(obj);
+    }
+
+
+    @ForceInline
+    /*non-public*/
+    static Object staticBase(Object accessorObj) {
+        // Android-changed: there is only MethodHandleImpl.
+        // return ((StaticAccessor)accessorObj).staticBase;
+        return ((MethodHandleImpl) accessorObj).field.getDeclaringClass();
+    }
+
+    @ForceInline
+    /*non-public*/
+    static long staticOffset(Object accessorObj) {
+        // Android-changed: there is only MethodHandleImpl.
+        // return ((StaticAccessor)accessorObj).staticOffset;
+        return ((MethodHandleImpl) accessorObj).field.getOffset();
+    }
+
+    // BEGIN Android-added: different mechanism to tie actual implementation to a MethodHandle.
+    static Method getImplementation(String name, List<Class<?>> parameters) {
+        return ACCESSOR_IMPLEMENTATIONS.get(new MethodKey(name, parameters));
+    }
+
+    private static final Map<MethodKey, Method> ACCESSOR_IMPLEMENTATIONS;
+
+    static {
+        UNSAFE.ensureClassInitialized(Holder.class);
+
+        // 4 access kinds, 9 basic types and fields can be volatile or non-volatile.
+        HashMap<MethodKey, Method> accessorMethods = HashMap.newHashMap(4 * 9 * 2);
+
+        for (Method m : Holder.class.getDeclaredMethods()) {
+            accessorMethods.put(
+                    new MethodKey(m.getName(), Arrays.asList(m.getParameterTypes())), m);
+        }
+
+        ACCESSOR_IMPLEMENTATIONS = Collections.unmodifiableMap(accessorMethods);
+    }
+
+    private static final class MethodKey {
+        private final String name;
+        private final List<Class<?>> arguments;
+
+        MethodKey(String name, List<Class<?>> arguments) {
+            this.name = Objects.requireNonNull(name);
+            this.arguments = Objects.requireNonNull(arguments);
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * name.hashCode() + arguments.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof MethodKey methodKey) {
+                return name.equals(methodKey.name) && arguments.equals(methodKey.arguments);
+            }
+
+            return false;
+        }
+    }
+    // END Android-added: different mechanism to tie actual implementation to a MethodHandle.
+
+    // Android-changed: upstream inserts implementation at the link time (straight to bytecode, w/o
+    // compilation).
+    // Do not change this class manually: check AccessorMethodHandlesGenerator.
+    /* Placeholder class for DirectMethodHandles generated ahead of time */
+    static final class Holder {
+        static void putBoolean(Object base, boolean value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putBoolean(base, offset, value);
+        }
+
+        static void putBooleanVolatile(Object base, boolean value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putBooleanVolatile(base, offset, value);
+        }
+
+        static void putByte(Object base, byte value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putByte(base, offset, value);
+        }
+
+        static void putByteVolatile(Object base, byte value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putByteVolatile(base, offset, value);
+        }
+
+        static void putChar(Object base, char value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putChar(base, offset, value);
+        }
+
+        static void putCharVolatile(Object base, char value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putCharVolatile(base, offset, value);
+        }
+
+        static void putShort(Object base, short value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putShort(base, offset, value);
+        }
+
+        static void putShortVolatile(Object base, short value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putShortVolatile(base, offset, value);
+        }
+
+        static void putInt(Object base, int value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putInt(base, offset, value);
+        }
+
+        static void putIntVolatile(Object base, int value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putIntVolatile(base, offset, value);
+        }
+
+        static void putLong(Object base, long value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putLong(base, offset, value);
+        }
+
+        static void putLongVolatile(Object base, long value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putLongVolatile(base, offset, value);
+        }
+
+        static void putDouble(Object base, double value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putDouble(base, offset, value);
+        }
+
+        static void putDoubleVolatile(Object base, double value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putDoubleVolatile(base, offset, value);
+        }
+
+        static void putFloat(Object base, float value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putFloat(base, offset, value);
+        }
+
+        static void putFloatVolatile(Object base, float value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putFloatVolatile(base, offset, value);
+        }
+
+        static void putReference(Object base, Object value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putReference(base, offset, value);
+        }
+
+        static void putReferenceVolatile(Object base, Object value, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            UNSAFE.putReferenceVolatile(base, offset, value);
+        }
+
+        static boolean getBoolean(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getBoolean(base, offset);
+        }
+
+        static boolean getBooleanVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getBooleanVolatile(base, offset);
+        }
+
+        static byte getByte(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getByte(base, offset);
+        }
+
+        static byte getByteVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getByteVolatile(base, offset);
+        }
+
+        static char getChar(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getChar(base, offset);
+        }
+
+        static char getCharVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getCharVolatile(base, offset);
+        }
+
+        static short getShort(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getShort(base, offset);
+        }
+
+        static short getShortVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getShortVolatile(base, offset);
+        }
+
+        static int getInt(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getInt(base, offset);
+        }
+
+        static int getIntVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getIntVolatile(base, offset);
+        }
+
+        static long getLong(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getLong(base, offset);
+        }
+
+        static long getLongVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getLongVolatile(base, offset);
+        }
+
+        static double getDouble(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getDouble(base, offset);
+        }
+
+        static double getDoubleVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getDoubleVolatile(base, offset);
+        }
+
+        static float getFloat(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getFloat(base, offset);
+        }
+
+        static float getFloatVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getFloatVolatile(base, offset);
+        }
+
+        static Object getReference(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getReference(base, offset);
+        }
+
+        static Object getReferenceVolatile(Object base, MethodHandleImpl mh) {
+            checkBase(base);
+            long offset = fieldOffset(mh);
+            return UNSAFE.getReferenceVolatile(base, offset);
+        }
+
+        static void putBoolean(boolean value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putBoolean(base, offset, value);
+        }
+
+        static void putBooleanVolatile(boolean value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putBooleanVolatile(base, offset, value);
+        }
+
+        static void putByte(byte value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putByte(base, offset, value);
+        }
+
+        static void putByteVolatile(byte value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putByteVolatile(base, offset, value);
+        }
+
+        static void putChar(char value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putChar(base, offset, value);
+        }
+
+        static void putCharVolatile(char value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putCharVolatile(base, offset, value);
+        }
+
+        static void putShort(short value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putShort(base, offset, value);
+        }
+
+        static void putShortVolatile(short value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putShortVolatile(base, offset, value);
+        }
+
+        static void putInt(int value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putInt(base, offset, value);
+        }
+
+        static void putIntVolatile(int value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putIntVolatile(base, offset, value);
+        }
+
+        static void putLong(long value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putLong(base, offset, value);
+        }
+
+        static void putLongVolatile(long value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putLongVolatile(base, offset, value);
+        }
+
+        static void putDouble(double value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putDouble(base, offset, value);
+        }
+
+        static void putDoubleVolatile(double value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putDoubleVolatile(base, offset, value);
+        }
+
+        static void putFloat(float value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putFloat(base, offset, value);
+        }
+
+        static void putFloatVolatile(float value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putFloatVolatile(base, offset, value);
+        }
+
+        static void putReference(Object value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putReference(base, offset, value);
+        }
+
+        static void putReferenceVolatile(Object value, MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            UNSAFE.putReferenceVolatile(base, offset, value);
+        }
+
+        static boolean getBoolean(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getBoolean(base, offset);
+        }
+
+        static boolean getBooleanVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getBooleanVolatile(base, offset);
+        }
+
+        static byte getByte(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getByte(base, offset);
+        }
+
+        static byte getByteVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getByteVolatile(base, offset);
+        }
+
+        static char getChar(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getChar(base, offset);
+        }
+
+        static char getCharVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getCharVolatile(base, offset);
+        }
+
+        static short getShort(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getShort(base, offset);
+        }
+
+        static short getShortVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getShortVolatile(base, offset);
+        }
+
+        static int getInt(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getInt(base, offset);
+        }
+
+        static int getIntVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getIntVolatile(base, offset);
+        }
+
+        static long getLong(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getLong(base, offset);
+        }
+
+        static long getLongVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getLongVolatile(base, offset);
+        }
+
+        static double getDouble(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getDouble(base, offset);
+        }
+
+        static double getDoubleVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getDoubleVolatile(base, offset);
+        }
+
+        static float getFloat(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getFloat(base, offset);
+        }
+
+        static float getFloatVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getFloatVolatile(base, offset);
+        }
+
+        static Object getReference(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getReference(base, offset);
+        }
+
+        static Object getReferenceVolatile(MethodHandleImpl mh) {
+            Object base = staticBase(mh);
+            long offset = staticOffset(mh);
+            return UNSAFE.getReferenceVolatile(base, offset);
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java b/ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java
index db2c932..8a2fa8b 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java
@@ -26,6 +26,8 @@
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
 
 // Android-changed: Android specific implementation.
 // The whole class was implemented from scratch for the Android runtime based
@@ -38,13 +40,71 @@
  * @hide
  */
 public class MethodHandleImpl extends MethodHandle implements Cloneable {
-    private Field field;
+    // TODO(b/297147201): create separate AccessorMethodHandle class and move target and field
+    // into it.
+    // Used by runtime only.
+    private final long target;
     private Object targetClassOrMethodHandleInfo;
-    private long target;
+    Field field;
 
     MethodHandleImpl(long artFieldOrMethod, int handleKind, MethodType type) {
         super(artFieldOrMethod, handleKind, type);
         this.targetClassOrMethodHandleInfo = getMemberInternal().getDeclaringClass();
+        this.target = 0;
+    }
+
+    MethodHandleImpl(Field field, int handleKind, MethodType type) {
+        super(field.getArtField(), handleKind, type);
+        // To make sure that we won't operate on uninitialized fields.
+        // TODO (b/399619087): make initialization lazy.
+        MethodHandleStatics.UNSAFE.ensureClassInitialized(field.getDeclaringClass());
+        this.targetClassOrMethodHandleInfo = getMemberInternal().getDeclaringClass();
+        this.field = field;
+        this.target = resolveTarget(handleKind, field);
+    }
+
+    private static long resolveTarget(int handleKind, Field field) {
+        StringBuilder name = new StringBuilder();
+
+        if (handleKind == MethodHandle.SGET || handleKind == MethodHandle.IGET) {
+            name.append("get");
+        } else if (handleKind == MethodHandle.SPUT || handleKind == MethodHandle.IPUT) {
+            name.append("put");
+        } else {
+            throw new AssertionError("Unexpected handleKind: " + handleKind);
+        }
+
+        Class<?> type = field.getType();
+
+        if (type.isPrimitive()) {
+            String fieldTypeName = type.getName();
+            name.append(Character.toUpperCase(fieldTypeName.charAt(0)));
+            name.append(fieldTypeName.substring(1));
+        } else {
+            name.append("Reference");
+        }
+
+        if (Modifier.isVolatile(field.getModifiers())) {
+            name.append("Volatile");
+        }
+
+        List<Class<?>> signature = new ArrayList<>(3);
+        if (!Modifier.isStatic(field.getModifiers())) {
+            signature.add(Object.class);
+        }
+        if (handleKind == MethodHandle.SPUT || handleKind == MethodHandle.IPUT) {
+            if (type.isPrimitive()) {
+                signature.add(type);
+            } else {
+                signature.add(Object.class);
+            }
+        }
+        signature.add(MethodHandleImpl.class);
+        Method target = DirectMethodHandle.getImplementation(name.toString(), signature);
+        if (target == null) {
+            throw new InternalError("DirectMethodHandle$Holder is missing a method");
+        }
+        return target.getArtMethod();
     }
 
     @Override
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
index c6f7162..7bd59b2 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java
@@ -1545,7 +1545,7 @@
                 default:
                     throw new IllegalArgumentException("Invalid kind " + kind);
             }
-            return new MethodHandleImpl(field.getArtField(), kind, methodType);
+            return new MethodHandleImpl(field, kind, methodType);
         }
 
         /**
diff --git a/openjdk_java_files.bp b/openjdk_java_files.bp
index 9dc4a79..b8057de 100644
--- a/openjdk_java_files.bp
+++ b/openjdk_java_files.bp
@@ -262,6 +262,7 @@
         "ojluni/src/main/java/java/lang/invoke/LambdaConversionException.java",
         "ojluni/src/main/java/java/lang/invoke/CallSite.java",
         "ojluni/src/main/java/java/lang/invoke/ConstantCallSite.java",
+        "ojluni/src/main/java/java/lang/invoke/DirectMethodHandle.java",
         "ojluni/src/main/java/java/lang/invoke/MethodHandle.java",
         "ojluni/src/main/java/java/lang/invoke/MethodHandles.java",
         "ojluni/src/main/java/java/lang/invoke/MethodHandleImpl.java",
diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp
new file mode 100644
index 0000000..a7fb857
--- /dev/null
+++ b/tools/codegen/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "libcore_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_license"],
+}
+
+java_binary_host {
+    name: "libcore-methodhandles-accessors-codegen",
+    srcs: ["src/**/*.java"],
+    manifest: "src/manifest.txt",
+}
diff --git a/tools/codegen/src/libcore/codegen/AccessorMethodHandlesGenerator.java b/tools/codegen/src/libcore/codegen/AccessorMethodHandlesGenerator.java
new file mode 100644
index 0000000..1814f9d
--- /dev/null
+++ b/tools/codegen/src/libcore/codegen/AccessorMethodHandlesGenerator.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * 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 libcore.codegen;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.StringJoiner;
+
+/**
+ * Generates methods of {@link DirectMethodHandle.Holder} class.
+ */
+public class AccessorMethodHandlesGenerator {
+
+    static String capitalize(String str) {
+        if (str.isEmpty()) {
+            throw new IllegalArgumentException("Can't capitalize empty string");
+        }
+        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
+    }
+
+    enum HandleKind {
+        IPUT,
+        IGET,
+        SPUT,
+        SGET;
+
+        public boolean isStatic() {
+            return this == SGET || this == SPUT;
+        }
+        public boolean isPut() {
+            return this == IPUT || this == SPUT;
+        }
+        public boolean isGet() {
+            return !isPut();
+        }
+
+        /* Prefix of a method in Holder class implementing this HandleKind. */
+        public String prefix() {
+            if (isPut()) {
+                return "put";
+            } else {
+                return "get";
+            }
+        }
+    }
+
+    enum BasicType {
+        BOOLEAN,
+        BYTE,
+        CHAR,
+        SHORT,
+        INT,
+        LONG,
+        DOUBLE,
+        FLOAT,
+        REFERENCE;
+
+        public String typeName() {
+            return this == REFERENCE ? "Object" : name().toLowerCase(Locale.ROOT);
+        }
+
+        /* Returns a string corresponding to a method parameter of this type named as paramName,
+         * for example "int paramName".
+         */
+        public String param(String paramName) {
+            return typeName() + " " + paramName;
+        }
+    }
+
+    static String parameters(HandleKind kind, BasicType actingUpon) {
+        var params = new ArrayList<String>();
+        if (!kind.isStatic()) {
+            params.add(BasicType.REFERENCE.param("base"));
+        }
+
+        if (kind.isPut()) {
+            params.add(actingUpon.param("value"));
+        }
+
+        // There is always MethodHandle object.
+        params.add("MethodHandleImpl mh");
+
+        return String.join(", ", params);
+    }
+
+    static String function(HandleKind kind, BasicType actingUpon, boolean isVolatile) {
+        var sb = new StringBuilder();
+        var modifiersAndReturnType = new StringJoiner(" ");
+        modifiersAndReturnType.add("static");
+        if (kind.isPut()) {
+            modifiersAndReturnType.add("void");
+        } else {
+            modifiersAndReturnType.add(actingUpon.typeName());
+        }
+        sb.append(modifiersAndReturnType).append(" ");
+        sb.append(kind.prefix()).append(capitalize(actingUpon.name().toLowerCase(Locale.ROOT)));
+        if (isVolatile) {
+            sb.append("Volatile");
+        }
+        sb.append("(").append(parameters(kind, actingUpon)).append(") {\n");
+
+        if (kind.isStatic()) {
+            sb.append("  ").append("Object base = staticBase(mh);\n");
+            sb.append("  ").append("long offset = staticOffset(mh);\n");
+        } else {
+            sb.append("  ").append("checkBase(base);\n");
+            sb.append("  ").append("long offset = fieldOffset(mh);\n");
+        }
+        var accessMode = isVolatile ? "Volatile" : "";
+        sb.append("  ");
+        if (kind.isGet()) {
+            sb.append("return UNSAFE.")
+                .append(kind.prefix())
+                .append(capitalize(actingUpon.name().toLowerCase(Locale.ROOT)))
+                .append(accessMode)
+                .append("(base, offset);");
+        } else {
+            sb.append("UNSAFE.")
+                .append(kind.prefix())
+                .append(capitalize(actingUpon.name().toLowerCase(Locale.ROOT)))
+                .append(accessMode)
+                .append("(base, offset, value);");
+        }
+
+        sb.append("\n}\n");
+        return sb.toString();
+    }
+
+    public static void main(String[] args) {
+        for (HandleKind kind : HandleKind.values()) {
+            for (BasicType name : BasicType.values()) {
+                for (boolean isVolatile : List.of(false, true)) {
+                    System.out.println(function(kind, name, isVolatile));
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/codegen/src/manifest.txt b/tools/codegen/src/manifest.txt
new file mode 100644
index 0000000..452cead9
--- /dev/null
+++ b/tools/codegen/src/manifest.txt
@@ -0,0 +1 @@
+Main-Class: libcore.codegen.AccessorMethodHandlesGenerator
\ No newline at end of file