Extend class generator to create empty DexProgramClass
Change-Id: I19ea4e03a675ffe3c06e0961ab691609fb700db1
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 51a4a4a..8f74d49 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -573,11 +573,11 @@
}
public boolean belongsToDirectPool() {
- return accessFlags.isStatic() || accessFlags.isPrivate() || accessFlags.isConstructor();
+ return accessFlags.belongsToDirectPool();
}
public boolean belongsToVirtualPool() {
- return !belongsToDirectPool();
+ return accessFlags.belongsToVirtualPool();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index a20ab1d..12da839 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -65,6 +65,14 @@
return new Builder();
}
+ public boolean belongsToDirectPool() {
+ return isStatic() || isPrivate() || isConstructor();
+ }
+
+ public boolean belongsToVirtualPool() {
+ return !belongsToDirectPool();
+ }
+
@Override
public MethodAccessFlags copy() {
return new MethodAccessFlags(originalFlags, modifiedFlags);
diff --git a/src/main/java/com/android/tools/r8/startup/InstrumentationServer.java b/src/main/java/com/android/tools/r8/startup/InstrumentationServer.java
index 98555f6..58f4d0f 100644
--- a/src/main/java/com/android/tools/r8/startup/InstrumentationServer.java
+++ b/src/main/java/com/android/tools/r8/startup/InstrumentationServer.java
@@ -8,4 +8,59 @@
package com.android.tools.r8.startup;
-public class InstrumentationServer {}
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.origin.Origin;
+import java.util.Collections;
+
+public final class InstrumentationServer {
+ public static DexProgramClass createClass(DexItemFactory dexItemFactory) {
+ return new DexProgramClass(
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;"),
+ Kind.CF,
+ Origin.unknown(),
+ ClassAccessFlags.fromCfAccessFlags(1025),
+ null,
+ DexTypeList.empty(),
+ dexItemFactory.createString("InstrumentationServer"),
+ NestHostClassAttribute.none(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ EnclosingMethodAttribute.none(),
+ Collections.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ createStaticFields(dexItemFactory),
+ createInstanceFields(dexItemFactory),
+ MethodCollectionFactory.fromMethods(
+ createDirectMethods(dexItemFactory), createVirtualMethods(dexItemFactory)),
+ dexItemFactory.getSkipNameValidationForTesting(),
+ DexProgramClass::invalidChecksumRequest);
+ }
+
+ private static DexEncodedField[] createInstanceFields(DexItemFactory dexItemFactory) {
+ return new DexEncodedField[0];
+ }
+
+ private static DexEncodedField[] createStaticFields(DexItemFactory dexItemFactory) {
+ return new DexEncodedField[0];
+ }
+
+ private static DexEncodedMethod[] createDirectMethods(DexItemFactory dexItemFactory) {
+ return new DexEncodedMethod[0];
+ }
+
+ private static DexEncodedMethod[] createVirtualMethods(DexItemFactory dexItemFactory) {
+ return new DexEncodedMethod[0];
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/startup/InstrumentationServerImpl.java b/src/main/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
index e9a3ebf..c51fed7 100644
--- a/src/main/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
+++ b/src/main/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
@@ -8,4 +8,59 @@
package com.android.tools.r8.startup;
-public class InstrumentationServerImpl {}
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.origin.Origin;
+import java.util.Collections;
+
+public final class InstrumentationServerImpl {
+ public static DexProgramClass createClass(DexItemFactory dexItemFactory) {
+ return new DexProgramClass(
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ Kind.CF,
+ Origin.unknown(),
+ ClassAccessFlags.fromCfAccessFlags(1),
+ null,
+ DexTypeList.empty(),
+ dexItemFactory.createString("InstrumentationServerImpl"),
+ NestHostClassAttribute.none(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ EnclosingMethodAttribute.none(),
+ Collections.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ createStaticFields(dexItemFactory),
+ createInstanceFields(dexItemFactory),
+ MethodCollectionFactory.fromMethods(
+ createDirectMethods(dexItemFactory), createVirtualMethods(dexItemFactory)),
+ dexItemFactory.getSkipNameValidationForTesting(),
+ DexProgramClass::invalidChecksumRequest);
+ }
+
+ private static DexEncodedField[] createInstanceFields(DexItemFactory dexItemFactory) {
+ return new DexEncodedField[0];
+ }
+
+ private static DexEncodedField[] createStaticFields(DexItemFactory dexItemFactory) {
+ return new DexEncodedField[0];
+ }
+
+ private static DexEncodedMethod[] createDirectMethods(DexItemFactory dexItemFactory) {
+ return new DexEncodedMethod[0];
+ }
+
+ private static DexEncodedMethod[] createVirtualMethods(DexItemFactory dexItemFactory) {
+ return new DexEncodedMethod[0];
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
index 353c560..e5cab71 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
@@ -4,32 +4,172 @@
package com.android.tools.r8.cfmethodgeneration;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.utils.FileUtils;
-import java.io.File;
+import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
-import java.io.PrintStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import java.util.function.Predicate;
public abstract class CfClassGenerator extends CodeGenerationBase {
- public String generateClass() throws IOException {
- File temporaryFile = File.createTempFile("output-", ".java");
- generateRawOutput(temporaryFile.toPath());
- String result = formatRawOutput(temporaryFile.toPath());
- temporaryFile.deleteOnExit();
- return result;
+ private final CfCodeGeneratorImportCollection imports = new CfCodeGeneratorImportCollection();
+
+ public abstract Class<?> getGeneratedClass();
+
+ @Override
+ protected DexType getGeneratedType() {
+ return factory.createType(descriptor(getGeneratedClass()));
}
- private void generateRawOutput(Path temporaryFile) throws IOException {
- try (PrintStream printer = new PrintStream(Files.newOutputStream(temporaryFile))) {
- printer.print(getHeaderString());
- printer.println("public class " + getGeneratedClassName() + " {");
- printer.println("}");
+ public String generateClass() throws IOException {
+ return formatRawOutput(generateRawOutput());
+ }
+
+ private String generateRawOutput() {
+ String classDeclaration = generateClassDeclaration();
+ return StringUtils.lines(getHeaderString(), imports.generateImports(), classDeclaration);
+ }
+
+ private String generateClassDeclaration() {
+ JavaStringBuilder builder = new JavaStringBuilder();
+ builder.append("public final class " + getGeneratedClassName() + " ").appendOpeningBrace();
+ generateCreateClassMethod(builder);
+ generateCreateFieldsMethod(builder, "createInstanceFields", not(FieldAccessFlags::isStatic));
+ generateCreateFieldsMethod(builder, "createStaticFields", FieldAccessFlags::isStatic);
+ generateCreateMethodsMethod(
+ builder, "createDirectMethods", MethodAccessFlags::belongsToDirectPool);
+ generateCreateMethodsMethod(
+ builder, "createVirtualMethods", MethodAccessFlags::belongsToVirtualPool);
+ builder.appendClosingBrace();
+ return builder.toString();
+ }
+
+ private void generateCreateClassMethod(JavaStringBuilder builder) {
+ builder
+ .startLine()
+ .append("public static ")
+ .append(imports.getDexProgramClass())
+ .append(" createClass(")
+ .append(imports.getDexItemFactory())
+ .append(" dexItemFactory) ")
+ .appendOpeningBrace();
+
+ builder
+ .startLine()
+ .append("return new ")
+ .append(imports.getDexProgramClass())
+ .appendOpeningMultiLineParenthesis();
+
+ builder
+ .startLine()
+ .append("dexItemFactory.createType(\"")
+ .append(getGeneratedClassDescriptor())
+ .appendLine("\"),");
+
+ builder.startLine().append(imports.getProgramResourceKind()).appendLine(".CF,");
+
+ builder.startLine().append(imports.getOrigin()).appendLine(".unknown(),");
+
+ builder
+ .startLine()
+ .append(imports.getClassAccessFlags())
+ .append(".fromCfAccessFlags(")
+ .append(getGeneratedClass().getModifiers())
+ .appendLine("),");
+
+ builder.startLine().appendLine("null,");
+
+ builder.startLine().append(imports.getDexTypeList()).appendLine(".empty(),");
+
+ builder
+ .startLine()
+ .append("dexItemFactory.createString(\"")
+ .append(getGeneratedClassName())
+ .appendLine("\"),");
+
+ builder.startLine().append(imports.getNestHostClassAttribute()).appendLine(".none(),");
+
+ for (int i = 0; i < 2; i++) {
+ builder.startLine().append(imports.getJavaUtilCollections()).appendLine(".emptyList(),");
}
+
+ builder.startLine().append(imports.getEnclosingMethodAttribute()).appendLine(".none(),");
+
+ builder.startLine().append(imports.getJavaUtilCollections()).appendLine(".emptyList(),");
+
+ builder.startLine().append(imports.getClassSignature()).appendLine(".noSignature(),");
+
+ builder.startLine().append(imports.getDexAnnotationSet()).appendLine(".empty(),");
+
+ builder.startLine().appendLine("createStaticFields(dexItemFactory),");
+
+ builder.startLine().appendLine("createInstanceFields(dexItemFactory),");
+
+ builder
+ .startLine()
+ .append(imports.getMethodCollectionFactory())
+ .appendLine(
+ ".fromMethods(createDirectMethods(dexItemFactory),"
+ + " createVirtualMethods(dexItemFactory)),");
+
+ builder.startLine().appendLine("dexItemFactory.getSkipNameValidationForTesting(),");
+
+ builder.startLine().append(imports.getDexProgramClass()).append("::invalidChecksumRequest");
+
+ builder.appendClosingMultiLineParenthesis().appendLine(';');
+ builder.appendClosingBrace();
+ }
+
+ private void generateCreateFieldsMethod(
+ JavaStringBuilder builder, String methodName, Predicate<FieldAccessFlags> predicate) {
+ builder
+ .startLine()
+ .append("private static ")
+ .append(imports.getDexEncodedField())
+ .append("[] ")
+ .append(methodName)
+ .append("(")
+ .append(imports.getDexItemFactory())
+ .append(" dexItemFactory) ")
+ .appendOpeningBrace();
+
+ builder
+ .startLine()
+ .append("return new ")
+ .append(imports.getDexEncodedField())
+ .appendLine("[0];");
+
+ builder.appendClosingBrace();
+ }
+
+ private void generateCreateMethodsMethod(
+ JavaStringBuilder builder, String methodName, Predicate<MethodAccessFlags> predicate) {
+ builder
+ .startLine()
+ .append("private static ")
+ .append(imports.getDexEncodedMethod())
+ .append("[] ")
+ .append(methodName)
+ .append("(")
+ .append(imports.getDexItemFactory())
+ .append(" dexItemFactory) ")
+ .appendOpeningBrace();
+
+ builder
+ .startLine()
+ .append("return new ")
+ .append(imports.getDexEncodedMethod())
+ .appendLine("[0];");
+
+ builder.appendClosingBrace();
}
public void writeClassToFile() throws IOException {
FileUtils.writeToFile(getGeneratedFile(), null, generateClass().getBytes());
}
}
+
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfCodeGeneratorImportCollection.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfCodeGeneratorImportCollection.java
new file mode 100644
index 0000000..9da5527
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfCodeGeneratorImportCollection.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2022, 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.cfmethodgeneration;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class CfCodeGeneratorImportCollection {
+
+ private final Set<String> imports = new LinkedHashSet<>();
+
+ String generateImports() {
+ StringBuilder builder = new StringBuilder();
+ for (String className : imports) {
+ builder.append("import ").append(className).append(";").append('\n');
+ }
+ return builder.toString();
+ }
+
+ String getClassAccessFlags() {
+ return getR8ClassName("graph", "ClassAccessFlags");
+ }
+
+ String getClassSignature() {
+ return getR8ClassName("graph.GenericSignature", "ClassSignature");
+ }
+
+ String getDexAnnotationSet() {
+ return getR8ClassName("graph", "DexAnnotationSet");
+ }
+
+ String getDexEncodedField() {
+ return getR8ClassName("graph", "DexEncodedField");
+ }
+
+ String getDexEncodedMethod() {
+ return getR8ClassName("graph", "DexEncodedMethod");
+ }
+
+ String getDexItemFactory() {
+ return getR8ClassName("graph", "DexItemFactory");
+ }
+
+ String getDexProgramClass() {
+ return getR8ClassName("graph", "DexProgramClass");
+ }
+
+ String getDexTypeList() {
+ return getR8ClassName("graph", "DexTypeList");
+ }
+
+ String getEnclosingMethodAttribute() {
+ return getR8ClassName("graph", "EnclosingMethodAttribute");
+ }
+
+ String getJavaUtilCollections() {
+ addImport("java.util.Collections");
+ return "Collections";
+ }
+
+ String getMethodCollectionFactory() {
+ return getR8ClassName("graph.MethodCollection", "MethodCollectionFactory");
+ }
+
+ String getNestHostClassAttribute() {
+ return getR8ClassName("graph", "NestHostClassAttribute");
+ }
+
+ String getOrigin() {
+ return getR8ClassName("origin", "Origin");
+ }
+
+ String getProgramResourceKind() {
+ return getR8ClassName("ProgramResource", "Kind");
+ }
+
+ private String getR8ClassName(String context, String name) {
+ String canonicalName =
+ "com.android.tools.r8." + (context != null ? (context + ".") : "") + name;
+ addImport(canonicalName);
+ return name;
+ }
+
+ private String addImport(String name) {
+ imports.add(name);
+ return name;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
index f1aa400..ee1a991 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
@@ -11,7 +11,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -24,6 +26,14 @@
protected final DexItemFactory factory = new DexItemFactory();
+ public static String formatRawOutput(String rawOutput) throws IOException {
+ File temporaryFile = File.createTempFile("output-", ".java");
+ Files.write(temporaryFile.toPath(), rawOutput.getBytes());
+ String result = formatRawOutput(temporaryFile.toPath());
+ temporaryFile.deleteOnExit();
+ return result;
+ }
+
public static String formatRawOutput(Path tempFile) throws IOException {
// Apply google format.
ProcessBuilder builder =
@@ -48,6 +58,10 @@
return content;
}
+ public String getGeneratedClassDescriptor() {
+ return getGeneratedType().toDescriptorString();
+ }
+
public String getGeneratedClassName() {
return getGeneratedType().getName();
}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java
new file mode 100644
index 0000000..13f2347
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, 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.cfmethodgeneration;
+
+public class JavaStringBuilder {
+
+ private final StringBuilder builder = new StringBuilder();
+ private int indentation = 0;
+
+ public JavaStringBuilder append(char c) {
+ builder.append(c);
+ return this;
+ }
+
+ public JavaStringBuilder append(int i) {
+ builder.append(i);
+ return this;
+ }
+
+ public JavaStringBuilder append(String string) {
+ builder.append(string);
+ return this;
+ }
+
+ public JavaStringBuilder appendLine() {
+ return append('\n');
+ }
+
+ public JavaStringBuilder appendLine(char c) {
+ return append(c).appendLine();
+ }
+
+ public JavaStringBuilder appendLine(String string) {
+ return append(string).appendLine();
+ }
+
+ public JavaStringBuilder appendOpeningBrace() {
+ return appendLine('{').indent(2);
+ }
+
+ public JavaStringBuilder appendClosingBrace() {
+ return indent(-2).startLine().appendLine('}');
+ }
+
+ public JavaStringBuilder appendOpeningMultiLineParenthesis() {
+ return appendLine('(').indent(4);
+ }
+
+ public JavaStringBuilder appendClosingMultiLineParenthesis() {
+ return append(')').indent(-4);
+ }
+
+ public JavaStringBuilder indent(int change) {
+ indentation += change;
+ return this;
+ }
+
+ public JavaStringBuilder startLine() {
+ for (int i = 0; i < indentation; i++) {
+ append(' ');
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/GenerateInstrumentationServerClassesTest.java b/src/test/java/com/android/tools/r8/startup/GenerateInstrumentationServerClassesTest.java
index 72c73ec..23f3436 100644
--- a/src/test/java/com/android/tools/r8/startup/GenerateInstrumentationServerClassesTest.java
+++ b/src/test/java/com/android/tools/r8/startup/GenerateInstrumentationServerClassesTest.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.cfmethodgeneration.CfClassGenerator;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -64,8 +63,8 @@
}
@Override
- protected DexType getGeneratedType() {
- return factory.createType(descriptor(clazz));
+ public Class<?> getGeneratedClass() {
+ return clazz;
}
@Override