Model ClassNewInstance in R8 Assistant

Bug: b/428836085
Change-Id: Ib7822f855d1d607d508c6637b56af316fc123e83
diff --git a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassNewInstance.java b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassNewInstance.java
new file mode 100644
index 0000000..dfdab8d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassNewInstance.java
@@ -0,0 +1,47 @@
+// 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.postprocessing.model;
+
+import com.android.tools.r8.assistant.runtime.ReflectiveEventType;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.KeepInfoCollectionExported;
+
+public class ClassNewInstance extends ReflectiveEvent {
+
+  private final DexType type;
+
+  protected ClassNewInstance(
+      ReflectiveEventType eventType, String[] stack, String[] args, DexItemFactory factory) {
+    super(eventType, stack);
+    type = toType(args[0], factory);
+  }
+
+  public DexType getType() {
+    return type;
+  }
+
+  @Override
+  public boolean isClassNewInstance() {
+    return true;
+  }
+
+  @Override
+  public ClassNewInstance asClassNewInstance() {
+    return this;
+  }
+
+  @Override
+  public String getContentsString() {
+    return type.toSourceString();
+  }
+
+  @Override
+  public boolean isKeptBy(KeepInfoCollectionExported keepInfoCollectionExported) {
+    // TODO(b/428836085): Check inner properties of the keep rules, holder, type and name may have
+    //  to be preserved.
+    return keepInfoCollectionExported.getKeepClassInfo(type.asTypeReference()) != null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java
index 26090d0..d217231 100644
--- a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java
+++ b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java
@@ -68,6 +68,14 @@
     return null;
   }
 
+  public boolean isClassNewInstance() {
+    return false;
+  }
+
+  public ClassNewInstance asClassNewInstance() {
+    return null;
+  }
+
   public boolean isClassGetMembers() {
     return false;
   }
@@ -89,7 +97,7 @@
       ReflectiveEventType eventType, String[] stack, String[] args, DexItemFactory factory) {
     switch (eventType) {
       case CLASS_NEW_INSTANCE:
-        break;
+        return new ClassNewInstance(eventType, stack, args, factory);
       case CLASS_GET_DECLARED_METHOD:
       case CLASS_GET_DECLARED_FIELD:
       case CLASS_GET_DECLARED_CONSTRUCTOR:
diff --git a/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java b/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java
index 5e1fbf8..ff05d32 100644
--- a/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java
+++ b/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.assistant.postprocessing.model.ClassGetMember;
 import com.android.tools.r8.assistant.postprocessing.model.ClassGetMembers;
 import com.android.tools.r8.assistant.postprocessing.model.ClassGetName;
+import com.android.tools.r8.assistant.postprocessing.model.ClassNewInstance;
 import com.android.tools.r8.assistant.postprocessing.model.ReflectiveEvent;
 import com.android.tools.r8.assistant.runtime.ReflectiveEventType;
 import com.android.tools.r8.assistant.runtime.ReflectiveOperationJsonLogger;
@@ -69,7 +70,7 @@
         .assertSuccess();
     List<ReflectiveEvent> reflectiveEvents =
         new ReflectiveOperationJsonParser(factoryBox.get()).parse(path);
-    Assert.assertEquals(29, reflectiveEvents.size());
+    Assert.assertEquals(30, reflectiveEvents.size());
 
     assertTrue(reflectiveEvents.get(4).isClassGetMember());
     ClassGetMember updater00 = reflectiveEvents.get(4).asClassGetMember();
@@ -163,6 +164,11 @@
         Reference.methodFromMethod(Bar.class.getConstructor()),
         updater24.getMember().asDexMethod().asMethodReference());
 
+    assertTrue(reflectiveEvents.get(29).isClassNewInstance());
+    ClassNewInstance updater29 = reflectiveEvents.get(29).asClassNewInstance();
+    assertEquals(ReflectiveEventType.CLASS_NEW_INSTANCE, updater29.getEventType());
+    assertEquals(Bar.class.getName(), updater29.getType().toSourceString());
+
     Box<KeepInfoCollectionExported> keepInfoBox = new Box<>();
     testForR8(parameters)
         .addProgramClasses(JavaLangClassTestClass.class, Foo.class, Bar.class)
@@ -188,6 +194,7 @@
             "public com.android.tools.r8.assistant.JavaLangClassTestClass$Bar()",
             "true",
             "class com.android.tools.r8.assistant.JavaLangClassTestClass$Bar",
+            "11",
             "END");
     KeepInfoCollectionExported keepInfoCollectionExported = keepInfoBox.get();
 
diff --git a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java
index 374014b..ce78e14 100644
--- a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java
+++ b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java
@@ -85,6 +85,8 @@
             "true",
             "40",
             "class com.android.tools.r8.assistant.JavaLangClassTestClass$Bar",
+            "50",
+            "11",
             "END");
   }
 
@@ -201,7 +203,7 @@
 
     @Override
     public void onClassNewInstance(Stack stack, Class<?> clazz) {
-      super.onClassNewInstance(stack, clazz);
+      printNumIfTrue(clazz.getName().endsWith("Bar"), 50);
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java
index a0308d6..feb74d1 100644
--- a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java
+++ b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java
@@ -52,6 +52,13 @@
       Bar cast = Bar.class.cast(o);
       System.out.println(Bar.class.isInstance(o));
       System.out.println(Bar.class.asSubclass(Foo.class));
+      Bar newBar = null;
+      try {
+        newBar = Bar.class.newInstance();
+        System.out.println(newBar.bar());
+      } catch (InstantiationException | IllegalAccessException e) {
+        throw new RuntimeException(e);
+      }
       System.out.println("END");
     } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) {
       System.out.println("EXCEPTION " + e.getMessage());