R8 Assistant: Add AtomicUpdater support

Change-Id: I9deadcbf3155386059a5013bebf5a16e44035aa0
diff --git a/src/assistant/java/com/android/tools/r8/assistant/runtime/EmptyReflectiveOperationReceiver.java b/src/assistant/java/com/android/tools/r8/assistant/runtime/EmptyReflectiveOperationReceiver.java
index 6f874f5..0aa1326 100644
--- a/src/assistant/java/com/android/tools/r8/assistant/runtime/EmptyReflectiveOperationReceiver.java
+++ b/src/assistant/java/com/android/tools/r8/assistant/runtime/EmptyReflectiveOperationReceiver.java
@@ -53,4 +53,14 @@
 
   @Override
   public void onClassFlag(Stack stack, Class<?> clazz, ClassFlag classFlag) {}
+
+  @Override
+  public void onAtomicIntegerFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name) {}
+
+  @Override
+  public void onAtomicLongFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name) {}
+
+  @Override
+  public void onAtomicReferenceFieldUpdaterNewUpdater(
+      Stack stack, Class<?> clazz, Class<?> fieldClass, String name) {}
 }
diff --git a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java
index 76684c7..5df728a 100644
--- a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java
+++ b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java
@@ -83,6 +83,30 @@
     System.out.println("Reflectively got class flag " + classFlag);
   }
 
+  @Override
+  public void onAtomicIntegerFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name) {
+    System.out.println(
+        "Reflectively got AtomicIntegerFieldUpdater.newUpdater on " + clazz + "#" + name);
+  }
+
+  @Override
+  public void onAtomicLongFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name) {
+    System.out.println(
+        "Reflectively got AtomicLongFieldUpdater.newUpdater on " + clazz + "#" + name);
+  }
+
+  @Override
+  public void onAtomicReferenceFieldUpdaterNewUpdater(
+      Stack stack, Class<?> clazz, Class<?> fieldClass, String name) {
+    System.out.println(
+        "Reflectively got AtomicReferenceFieldUpdater.newUpdater on "
+            + fieldClass
+            + " "
+            + clazz
+            + "#"
+            + name);
+  }
+
   public boolean requiresStackInformation() {
     return true;
   }
diff --git a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationReceiver.java b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationReceiver.java
index d4c43b8..79ff0e5 100644
--- a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationReceiver.java
+++ b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationReceiver.java
@@ -41,6 +41,13 @@
 
   void onClassIsAssignableFrom(Stack stack, Class<?> clazz, Class<?> sup);
 
+  void onAtomicIntegerFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name);
+
+  void onAtomicLongFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name);
+
+  void onAtomicReferenceFieldUpdaterNewUpdater(
+      Stack stack, Class<?> clazz, Class<?> fieldClass, String name);
+
   @KeepForApi
   enum ClassFlag {
     ANNOTATION,
diff --git a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOracle.java b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOracle.java
index 3188493..54d0eb9 100644
--- a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOracle.java
+++ b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOracle.java
@@ -182,4 +182,18 @@
   public static void onClassGetField(Class<?> clazz, String name) {
     getInstance().onClassGetField(Stack.createStack(), clazz, name);
   }
+
+  public static void onAtomicIntegerFieldUpdaterNewUpdater(Class<?> clazz, String name) {
+    getInstance().onAtomicIntegerFieldUpdaterNewUpdater(Stack.createStack(), clazz, name);
+  }
+
+  public static void onAtomicLongFieldUpdaterNewUpdater(Class<?> clazz, String name) {
+    getInstance().onAtomicLongFieldUpdaterNewUpdater(Stack.createStack(), clazz, name);
+  }
+
+  public static void onAtomicReferenceFieldUpdaterNewUpdater(
+      Class<?> clazz, Class<?> fieldClass, String name) {
+    getInstance()
+        .onAtomicReferenceFieldUpdaterNewUpdater(Stack.createStack(), clazz, fieldClass, name);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/assistant/InstrumentedReflectiveMethodList.java b/src/main/java/com/android/tools/r8/assistant/InstrumentedReflectiveMethodList.java
index 9b9203a..928982f 100644
--- a/src/main/java/com/android/tools/r8/assistant/InstrumentedReflectiveMethodList.java
+++ b/src/main/java/com/android/tools/r8/assistant/InstrumentedReflectiveMethodList.java
@@ -130,6 +130,20 @@
             "getMethods"),
         getMethodReferenceWithClassParameter("onClassGetMethods"));
 
+    builder.put(
+        factory.atomicFieldUpdaterMethods.intUpdater,
+        getMethodReferenceWithClassAndStringParameter("onAtomicIntegerFieldUpdaterNewUpdater"));
+    builder.put(
+        factory.atomicFieldUpdaterMethods.longUpdater,
+        getMethodReferenceWithClassAndStringParameter("onAtomicLongFieldUpdaterNewUpdater"));
+    builder.put(
+        factory.atomicFieldUpdaterMethods.referenceUpdater,
+        getMethodReferenceWithParameterTypes(
+            "onAtomicReferenceFieldUpdaterNewUpdater",
+            factory.classType,
+            factory.classType,
+            factory.stringType));
+
     return builder.build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/assistant/AtomicUpdaterTest.java b/src/test/java/com/android/tools/r8/assistant/AtomicUpdaterTest.java
new file mode 100644
index 0000000..360ea26
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/assistant/AtomicUpdaterTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.assistant;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.assistant.JavaLangClassTestClass.Bar;
+import com.android.tools.r8.assistant.JavaLangClassTestClass.Foo;
+import com.android.tools.r8.assistant.runtime.EmptyReflectiveOperationReceiver;
+import com.android.tools.r8.assistant.runtime.ReflectiveOracle.Stack;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AtomicUpdaterTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNativeMultidexDexRuntimes().withMaximumApiLevel().build();
+  }
+
+  @Test
+  public void testInstrumentationWithCustomOracle() throws Exception {
+    testForAssistant()
+        .addProgramClasses(AtomicUpdaterTestClass.class, Foo.class, Bar.class)
+        .addInstrumentationClasses(Instrumentation.class)
+        .setCustomReflectiveOperationReceiver(Instrumentation.class)
+        .setMinApi(parameters)
+        .compile()
+        .run(parameters.getRuntime(), AtomicUpdaterTestClass.class)
+        .assertSuccessWithOutputLines(
+            "int com.android.tools.r8.assistant.AtomicUpdaterTestClass#i",
+            "42",
+            "long com.android.tools.r8.assistant.AtomicUpdaterTestClass#l",
+            "42",
+            "java.lang.Object com.android.tools.r8.assistant.AtomicUpdaterTestClass#o",
+            "42");
+  }
+
+  public static class Instrumentation extends EmptyReflectiveOperationReceiver {
+
+    @Override
+    public void onAtomicIntegerFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name) {
+      System.out.println("int " + clazz.getName() + "#" + name);
+    }
+
+    @Override
+    public void onAtomicLongFieldUpdaterNewUpdater(Stack stack, Class<?> clazz, String name) {
+      System.out.println("long " + clazz.getName() + "#" + name);
+    }
+
+    @Override
+    public void onAtomicReferenceFieldUpdaterNewUpdater(
+        Stack stack, Class<?> clazz, Class<?> fieldClass, String name) {
+      System.out.println(fieldClass.getName() + " " + clazz.getName() + "#" + name);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/assistant/AtomicUpdaterTestClass.java b/src/test/java/com/android/tools/r8/assistant/AtomicUpdaterTestClass.java
new file mode 100644
index 0000000..adc0c22
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/assistant/AtomicUpdaterTestClass.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.assistant;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public class AtomicUpdaterTestClass {
+
+  volatile int i;
+  volatile long l;
+  volatile Object o;
+
+  public static void main(String[] args) {
+    AtomicUpdaterTestClass inst = new AtomicUpdaterTestClass();
+
+    AtomicIntegerFieldUpdater<AtomicUpdaterTestClass> intUpdater =
+        AtomicIntegerFieldUpdater.newUpdater(AtomicUpdaterTestClass.class, "i");
+    intUpdater.set(inst, 42);
+    System.out.println(inst.i);
+
+    AtomicLongFieldUpdater<AtomicUpdaterTestClass> longUpdater =
+        AtomicLongFieldUpdater.newUpdater(AtomicUpdaterTestClass.class, "l");
+    longUpdater.set(inst, 42L);
+    System.out.println(inst.l);
+
+    AtomicReferenceFieldUpdater<AtomicUpdaterTestClass, Object> referenceUpdater =
+        AtomicReferenceFieldUpdater.newUpdater(AtomicUpdaterTestClass.class, Object.class, "o");
+    referenceUpdater.set(inst, "42");
+    System.out.println(inst.o);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/assistant/ReflectiveCallExtractorTest.java b/src/test/java/com/android/tools/r8/assistant/ReflectiveCallExtractorTest.java
index ff6ca05..374407f 100644
--- a/src/test/java/com/android/tools/r8/assistant/ReflectiveCallExtractorTest.java
+++ b/src/test/java/com/android/tools/r8/assistant/ReflectiveCallExtractorTest.java
@@ -45,7 +45,7 @@
 
   @Test
   public void testGuava() throws Exception {
-    test(ToolHelper.GUAVA_JRE, 18, 20);
+    test(ToolHelper.GUAVA_JRE, 21, 17);
   }
 
   @Test
@@ -64,7 +64,7 @@
             "dump_app.zip");
     Path programArchive =
         CompilerDump.fromArchive(zip, temp.newFolder().toPath()).getProgramArchive();
-    test(programArchive, 23, 26);
+    test(programArchive, 26, 23);
   }
 
   private void test(Path jar, int success, int failure) throws Exception {