Add generation of first set of androidx keep annotations
* @UsesReflectionToConstruct
* @UsesReflectionToAccessMethod
* @UsesReflectionToAccessField
The CL does not add actual support to process these annotations.
Bug: b/392865072
Change-Id: I621403525607dc66e04843d3006a812c01010f54
diff --git a/src/keepanno/java/androidx/annotation/keep/AnnotationPattern.kt b/src/keepanno/java/androidx/annotation/keep/AnnotationPattern.kt
index a9eb9f8..623b80d 100644
--- a/src/keepanno/java/androidx/annotation/keep/AnnotationPattern.kt
+++ b/src/keepanno/java/androidx/annotation/keep/AnnotationPattern.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/ClassNamePattern.kt b/src/keepanno/java/androidx/annotation/keep/ClassNamePattern.kt
index 4abf83c..cf646db 100644
--- a/src/keepanno/java/androidx/annotation/keep/ClassNamePattern.kt
+++ b/src/keepanno/java/androidx/annotation/keep/ClassNamePattern.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/InstanceOfPattern.kt b/src/keepanno/java/androidx/annotation/keep/InstanceOfPattern.kt
index 01a1e51..ad1b9e8 100644
--- a/src/keepanno/java/androidx/annotation/keep/InstanceOfPattern.kt
+++ b/src/keepanno/java/androidx/annotation/keep/InstanceOfPattern.kt
@@ -24,9 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
-
/**
* A pattern structure for matching instances of classes and interfaces.
*
diff --git a/src/keepanno/java/androidx/annotation/keep/KeepBinding.kt b/src/keepanno/java/androidx/annotation/keep/KeepBinding.kt
index 526d374..0be3b9e 100644
--- a/src/keepanno/java/androidx/annotation/keep/KeepBinding.kt
+++ b/src/keepanno/java/androidx/annotation/keep/KeepBinding.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/KeepCondition.kt b/src/keepanno/java/androidx/annotation/keep/KeepCondition.kt
index f64cc7f..4219dc5 100644
--- a/src/keepanno/java/androidx/annotation/keep/KeepCondition.kt
+++ b/src/keepanno/java/androidx/annotation/keep/KeepCondition.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/KeepForApi.kt b/src/keepanno/java/androidx/annotation/keep/KeepForApi.kt
index aa8f2fe..070926c 100644
--- a/src/keepanno/java/androidx/annotation/keep/KeepForApi.kt
+++ b/src/keepanno/java/androidx/annotation/keep/KeepForApi.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/KeepTarget.kt b/src/keepanno/java/androidx/annotation/keep/KeepTarget.kt
index d8a5926..0bf1cdb 100644
--- a/src/keepanno/java/androidx/annotation/keep/KeepTarget.kt
+++ b/src/keepanno/java/androidx/annotation/keep/KeepTarget.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/StringPattern.kt b/src/keepanno/java/androidx/annotation/keep/StringPattern.kt
index a81c2b0..34e8e59 100644
--- a/src/keepanno/java/androidx/annotation/keep/StringPattern.kt
+++ b/src/keepanno/java/androidx/annotation/keep/StringPattern.kt
@@ -24,9 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
-
/**
* A pattern structure for matching strings.
*
diff --git a/src/keepanno/java/androidx/annotation/keep/TypePattern.kt b/src/keepanno/java/androidx/annotation/keep/TypePattern.kt
index 3d9c032..3f4a779 100644
--- a/src/keepanno/java/androidx/annotation/keep/TypePattern.kt
+++ b/src/keepanno/java/androidx/annotation/keep/TypePattern.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/UnconditionallyKeep.kt b/src/keepanno/java/androidx/annotation/keep/UnconditionallyKeep.kt
new file mode 100644
index 0000000..544b54e
--- /dev/null
+++ b/src/keepanno/java/androidx/annotation/keep/UnconditionallyKeep.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+// ***********************************************************************************
+// MAINTAINED AND TESTED IN THE R8 REPO. PLEASE MAKE CHANGES THERE AND REPLICATE.
+// ***********************************************************************************
+
+package androidx.annotation.keep
+
+/**
+ * Indicates code which is accessed by references from outside of the application code directly,
+ * such as via JNI, reflection, or instantiation from platform framework code.
+ *
+ * NOTE: This keep rule is unconditional, meaning that the annotated class, method, or field will
+ * always be preserved in the final application even if, for example, the surrounding code is never
+ * used.
+ *
+ * If reflection or JNI access occurs inside the application, instead prefer the
+ * `@UsesReflection***` annotations, as they will keep conditionally, rather than unconditionally as
+ * this annotation does.
+ *
+ * @see UsesReflectionToConstruct
+ * @see UsesReflectionToAccessMethod
+ * @see UsesReflectionToAccessField
+ */
+@Retention(AnnotationRetention.BINARY)
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FIELD,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.CONSTRUCTOR,
+)
+public annotation class UnconditionallyKeep(
+
+ /**
+ * Should the name be preserved.
+ *
+ * Generally this is true if the reference is external, but this can be disabled if the name
+ * isn't important.
+ */
+ val shouldPreserveName: Boolean = true
+)
diff --git a/src/keepanno/java/androidx/annotation/keep/Unspecified.kt b/src/keepanno/java/androidx/annotation/keep/Unspecified.kt
new file mode 100644
index 0000000..101ec13
--- /dev/null
+++ b/src/keepanno/java/androidx/annotation/keep/Unspecified.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.
+ */
+
+// ***********************************************************************************
+// MAINTAINED AND TESTED IN THE R8 REPO. PLEASE MAKE CHANGES THERE AND REPLICATE.
+// ***********************************************************************************
+
+package androidx.annotation.keep
+
+/**
+ * Used to define an unspecified class value in the Keep annotations.
+ *
+ * For example, when a method return class isn't important to specify in
+ * [UsesReflectionToAccessMethod.returnClass], it defaults to `Unspecified::class`, which signifies
+ * that any class is accepted.
+ *
+ * This is used as `null` cannot be used as a default in annotations.
+ */
+public class Unspecified
diff --git a/src/keepanno/java/androidx/annotation/keep/UsedByNative.kt b/src/keepanno/java/androidx/annotation/keep/UsedByNative.kt
index 668c549..c807dc8 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsedByNative.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsedByNative.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/UsedByReflection.kt b/src/keepanno/java/androidx/annotation/keep/UsedByReflection.kt
index 6d29600d..b624aa0 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsedByReflection.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsedByReflection.kt
@@ -24,8 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
import kotlin.reflect.KClass
/**
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflection.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflection.kt
index 102fe77..6690527 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflection.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflection.kt
@@ -24,9 +24,6 @@
package androidx.annotation.keep
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
-
/**
* Annotation to declare the reflective usages made by a class, method or field.
*
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
new file mode 100644
index 0000000..d80fb81
--- /dev/null
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.
+ */
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+// ***********************************************************************************
+// MAINTAINED AND TESTED IN THE R8 REPO. PLEASE MAKE CHANGES THERE AND REPLICATE.
+// ***********************************************************************************
+
+package androidx.annotation.keep
+
+import kotlin.reflect.KClass
+
+/**
+ * The annotated code uses reflection to indirectly access a field of the specified class/interface,
+ * or its subclasses / interface implementers.
+ *
+ * This annotation indicates to optimizers or shrinkers that the target field should be preserved if
+ * the annotated code is reachable in the final application build.
+ *
+ * @see UsesReflectionToConstruct
+ * @see UsesReflectionToAccessMethod
+ */
+@Repeatable
+@Retention(AnnotationRetention.BINARY)
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FIELD,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.CONSTRUCTOR,
+)
+public annotation class UsesReflectionToAccessField(
+
+ /**
+ * Class containing the field accessed by reflection.
+ *
+ * Mutually exclusive with [className].
+ */
+ val classConstant: KClass<*> = Unspecified::class,
+
+ /**
+ * Class name (or pattern) containing the field accessed by reflection.
+ *
+ * Mutually exclusive with [classConstant].
+ */
+ val className: String = "",
+
+ /** Field name (or pattern) accessed by reflection. */
+ val fieldName: String,
+
+ /**
+ * Class of field accessed by reflection.
+ *
+ * Ignored if not specified.
+ *
+ * Mutually exclusive with [fieldClassName].
+ */
+ val fieldClass: KClass<*> = Unspecified::class,
+
+ /**
+ * Class (or class pattern) of field accessed by reflection.
+ *
+ * Ignored if not specified.
+ *
+ * Mutually exclusive with [fieldClass].
+ */
+ val fieldClassName: String = "",
+)
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
new file mode 100644
index 0000000..ad67456
--- /dev/null
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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.
+ */
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+// ***********************************************************************************
+// MAINTAINED AND TESTED IN THE R8 REPO. PLEASE MAKE CHANGES THERE AND REPLICATE.
+// ***********************************************************************************
+
+package androidx.annotation.keep
+
+import kotlin.reflect.KClass
+
+/**
+ * The annotated code uses reflection to indirectly access a method of the specified
+ * class/interface, or its subclasses / interface implementers.
+ *
+ * This annotation indicates to optimizers or shrinkers that the target method should be preserved
+ * if the annotated code is reachable in the final application build.
+ *
+ * @see UsesReflectionToConstruct
+ * @see UsesReflectionToAccessField
+ */
+@Repeatable
+@Retention(AnnotationRetention.BINARY)
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FIELD,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.CONSTRUCTOR,
+)
+public annotation class UsesReflectionToAccessMethod(
+
+ /**
+ * Class containing the method accessed by reflection.
+ *
+ * Mutually exclusive with [className].
+ */
+ val classConstant: KClass<*> = Unspecified::class,
+
+ /**
+ * Class name (or pattern) containing the method accessed by reflection.
+ *
+ * Mutually exclusive with [classConstant].
+ */
+ val className: String = "",
+
+ /** Method name (or pattern) accessed by reflection. */
+ val methodName: String,
+
+ /**
+ * Defines which method to keep by specifying set of parameter classes passed.
+ *
+ * If neither `param` nor `paramClassNames` is specified then methods with all parameter lists
+ * are kept.
+ *
+ * Mutually exclusive with [paramClassNames].
+ */
+ val params: Array<KClass<*>> = [Unspecified::class],
+
+ /**
+ * Defines which method to keep by specifying set of parameter classes passed.
+ *
+ * If neither `param` nor `paramClassNames` is specified then methods with all parameter lists
+ * are kept.
+ *
+ * Mutually exclusive with [params].
+ */
+ val paramClassNames: Array<String> = [""],
+
+ /**
+ * Return type of the method accessed by reflection.
+ *
+ * Ignored if not specified.
+ *
+ * Mutually exclusive with [returnClassName].
+ */
+ val returnClass: KClass<*> = Unspecified::class,
+
+ /**
+ * Return type (or type pattern) of the method accessed by reflection.
+ *
+ * Ignored if not specified.
+ *
+ * Mutually exclusive with [returnClass].
+ */
+ val returnClassName: String = "",
+)
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
new file mode 100644
index 0000000..0741c2f
--- /dev/null
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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.
+ */
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+// ***********************************************************************************
+// MAINTAINED AND TESTED IN THE R8 REPO. PLEASE MAKE CHANGES THERE AND REPLICATE.
+// ***********************************************************************************
+
+package androidx.annotation.keep
+
+import kotlin.reflect.KClass
+
+/**
+ * The annotated code uses reflection to indirectly invoke a constructor of a class/interface, or
+ * its subclasses / interface implementers.
+ *
+ * This annotation indicates to optimizers or shrinkers that the target constructor should be kept
+ * if the annotated code is reachable in the final application build.
+ *
+ * `@UsesReflectionToConstruct()` is a convenience for `@UsesReflectionToAccessMethod(methodName =
+ * "<init>")`
+ *
+ * @see UsesReflectionToAccessMethod
+ * @see UsesReflectionToAccessField
+ */
+@Repeatable
+@Retention(AnnotationRetention.BINARY)
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FIELD,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.CONSTRUCTOR,
+)
+public annotation class UsesReflectionToConstruct(
+
+ /**
+ * Class to be instantiated.
+ *
+ * Mutually exclusive with [className].
+ */
+ val classConstant: KClass<*> = Unspecified::class,
+
+ /**
+ * Class to be instantiated.
+ *
+ * Mutually exclusive with [classConstant].
+ */
+ val className: String = "",
+
+ /**
+ * Defines which constructor to keep by specifying the parameter list types.
+ *
+ * If neither `param` nor `paramClassNames` is specified then constructors with all parameter
+ * lists are kept.
+ *
+ * Mutually exclusive with [paramClassNames].
+ */
+ val params: Array<KClass<*>> = [Unspecified::class],
+
+ /**
+ * Defines which constructor to keep by specifying the parameter list types.
+ *
+ * If neither `param` nor `paramClassNames` is specified then constructors with all parameter
+ * lists are kept.
+ *
+ * Mutually exclusive with [params].
+ */
+ val paramClassNames: Array<String> = [""],
+)
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index c174fa7..59bd281 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -131,6 +131,89 @@
}
}
+ public static final class UsesReflectionToConstruct {
+ private static final String DESCRIPTOR = "Landroidx/annotation/keep/UsesReflectionToConstruct;";
+ private static final String DESCRIPTOR_CONTAINER =
+ "Landroidx/annotation/keep/UsesReflectionToConstruct$Container;";
+ private static final String DESCRIPTOR_LEGACY =
+ "Lcom/android/tools/r8/keepanno/annotations/UsesReflectionToConstruct;";
+
+ public static boolean isDescriptor(String descriptor) {
+ return DESCRIPTOR.equals(descriptor) || DESCRIPTOR_LEGACY.equals(descriptor);
+ }
+
+ public static boolean isKotlinRepeatableContainerDescriptor(String descriptor) {
+ return DESCRIPTOR_CONTAINER.equals(descriptor);
+ }
+
+ public static String getDescriptor() {
+ return DESCRIPTOR;
+ }
+
+ public static final String classSelectionGroup = "class-selection";
+ public static final String classConstant = "classConstant";
+ public static final String className = "className";
+ public static final String constructorParametersGroup = "constructor-parameters";
+ public static final String params = "params";
+ public static final String paramClassNames = "paramClassNames";
+ }
+
+ public static final class UsesReflectionToAccessMethod {
+ private static final String DESCRIPTOR =
+ "Landroidx/annotation/keep/UsesReflectionToAccessMethod;";
+ private static final String DESCRIPTOR_CONTAINER =
+ "Landroidx/annotation/keep/UsesReflectionToAccessMethod$Container;";
+ private static final String DESCRIPTOR_LEGACY =
+ "Lcom/android/tools/r8/keepanno/annotations/UsesReflectionToAccessMethod;";
+
+ public static boolean isDescriptor(String descriptor) {
+ return DESCRIPTOR.equals(descriptor) || DESCRIPTOR_LEGACY.equals(descriptor);
+ }
+
+ public static boolean isKotlinRepeatableContainerDescriptor(String descriptor) {
+ return DESCRIPTOR_CONTAINER.equals(descriptor);
+ }
+
+ public static String getDescriptor() {
+ return DESCRIPTOR;
+ }
+
+ public static final String classSelectionGroup = "class-selection";
+ public static final String classConstant = "classConstant";
+ public static final String className = "className";
+ public static final String constructorParametersGroup = "constructor-parameters";
+ public static final String params = "params";
+ public static final String paramClassNames = "paramClassNames";
+ }
+
+ public static final class UsesReflectionToAccessField {
+ private static final String DESCRIPTOR =
+ "Landroidx/annotation/keep/UsesReflectionToAccessField;";
+ private static final String DESCRIPTOR_CONTAINER =
+ "Landroidx/annotation/keep/UsesReflectionToAccessField$Container;";
+ private static final String DESCRIPTOR_LEGACY =
+ "Lcom/android/tools/r8/keepanno/annotations/UsesReflectionToAccessField;";
+
+ public static boolean isDescriptor(String descriptor) {
+ return DESCRIPTOR.equals(descriptor) || DESCRIPTOR_LEGACY.equals(descriptor);
+ }
+
+ public static boolean isKotlinRepeatableContainerDescriptor(String descriptor) {
+ return DESCRIPTOR_CONTAINER.equals(descriptor);
+ }
+
+ public static String getDescriptor() {
+ return DESCRIPTOR;
+ }
+
+ public static final String classSelectionGroup = "class-selection";
+ public static final String classConstant = "classConstant";
+ public static final String className = "className";
+ public static final String constructorParametersGroup = "constructor-parameters";
+ public static final String params = "params";
+ public static final String paramClassNames = "paramClassNames";
+ }
+
/** Item properties common to binding items, conditions and targets. */
public static final class Item {
public static final String classGroup = "class";
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/DocPrinterBase.java b/src/test/java/com/android/tools/r8/keepanno/utils/DocPrinterBase.java
index 6aacce6..d2c6b45 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/DocPrinterBase.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/DocPrinterBase.java
@@ -35,7 +35,7 @@
public T setDocTitle(String title) {
assert this.title == null;
- assert title.endsWith(".");
+ assert title.endsWith(".") : "Title '" + title + "' should end with a dot.";
this.title = title;
return self();
}
@@ -54,6 +54,25 @@
return self();
}
+ public T addLines(String... lines) {
+ return addLines(Arrays.asList(lines));
+ }
+
+ public T addLines(List<String> lines) {
+ additionalLines.addAll(lines);
+ return self();
+ }
+
+ public T addSection(String... lines) {
+ return addSection(Arrays.asList(lines));
+ }
+
+ public T addSection(List<String> lines) {
+ additionalLines.add("");
+ additionalLines.addAll(lines);
+ return self();
+ }
+
public T addParagraph(String... lines) {
return addParagraph(Arrays.asList(lines));
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index b0089be..17ab6c3 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.cfmethodgeneration.CodeGenerationBase;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -23,6 +24,7 @@
import java.io.IOException;
import java.io.PrintStream;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -90,6 +92,7 @@
}
public static class EnumReference {
+
public final ClassReference enumClass;
public final String enumValue;
@@ -177,17 +180,23 @@
}
}
- private String kotlinValueType() {
- if (valueType.equals("Class<?>")) {
+ private static String kotlinValueType(String type) {
+ if (type.equals("Class<?>")) {
return "KClass<*>";
}
- if (valueType.equals("boolean")) {
+ if (type.equals("boolean")) {
return "Boolean";
}
+ return type;
+ }
+
+ private String kotlinValueType() {
if (valueType.endsWith("[]")) {
- return "Array<" + valueType.substring(0, valueType.length() - 2) + ">";
+ String baseType = valueType.substring(0, valueType.length() - 2);
+ assert !baseType.endsWith("[]") : "Multi dimensional arrays are not supported";
+ return "Array<" + kotlinValueType(baseType) + ">";
}
- return valueType;
+ return kotlinValueType(valueType);
}
private String kotlinValueDefault() {
@@ -235,6 +244,11 @@
return setValue("{" + value + "}");
}
+ public GroupMember defaultArrayClass(String value) {
+ setType("Class<?>" + "[]");
+ return setValue("{" + value + "}");
+ }
+
public GroupMember defaultEmptyString() {
return defaultValue(JAVA_STRING, quote(""));
}
@@ -246,10 +260,19 @@
public GroupMember defaultArrayEmpty(ClassReference type) {
return defaultArrayValue(type, "");
}
+
+ public GroupMember defaultUnspecifiedClass() {
+ return setType("Class<?>").setValue("Unspecified::class");
+ }
+
+ public GroupMember defaultUnspecifiedArray() {
+ return setType("Array<KClass<*>>").setValue("[Unspecified::class]");
+ }
}
public static class Group {
+ boolean forAndroidX = false;
final String name;
final List<GroupMember> members = new ArrayList<>();
final List<String> footers = new ArrayList<>();
@@ -261,6 +284,15 @@
this.name = name;
}
+ Group forAndroidX(boolean forAndroidX) {
+ this.forAndroidX = forAndroidX;
+ return this;
+ }
+
+ Group forAndroidX() {
+ return forAndroidX(true);
+ }
+
Group allowMutuallyExclusiveWithOtherGroups() {
mutuallyExclusiveWithOtherGroups = true;
return this;
@@ -293,12 +325,17 @@
group.members.forEach(m -> mutuallyExclusiveProperties.add(m.name));
});
if (mutuallyExclusiveProperties.size() == 1) {
- member.addParagraph(
- "Mutually exclusive with the property `"
- + mutuallyExclusiveProperties.get(0)
- + "` also defining "
- + name
- + ".");
+ if (forAndroidX) {
+ member.addSection(
+ "Mutually exclusive with [" + mutuallyExclusiveProperties.get(0) + "].");
+ } else {
+ member.addParagraph(
+ "Mutually exclusive with the property `"
+ + mutuallyExclusiveProperties.get(0)
+ + "` also defining "
+ + name
+ + ".");
+ }
} else if (mutuallyExclusiveProperties.size() > 1) {
member.addParagraph(
"Mutually exclusive with the following other properties defining " + name + ":");
@@ -361,6 +398,12 @@
final ClassReference KEEP_CONDITION;
final ClassReference KEEP_FOR_API;
+ final ClassReference USES_REFLECTION_TO_CONSTRUCT;
+ final ClassReference USES_REFLECTION_TO_ACCESS_METHOD;
+ final ClassReference USES_REFLECTION_TO_ACCESS_FIELD;
+ final ClassReference UNCONDITIONALLY_KEEP;
+ final List<ClassReference> REPEATABLE_ANNOTATIONS;
+
final ClassReference KEEP_ITEM_KIND;
final EnumReference KIND_ONLY_CLASS;
final EnumReference KIND_ONLY_MEMBERS;
@@ -422,8 +465,6 @@
final List<Class<?>> ANNOTATION_IMPORTS =
ImmutableList.of(ElementType.class, Retention.class, RetentionPolicy.class, Target.class);
- final List<Class<?>> KOTLIN_ANNOTATION_IMPORTS =
- ImmutableList.of(kotlin.annotation.Retention.class, kotlin.annotation.Target.class);
private final PrintStream writer;
private final String pkg;
@@ -451,6 +492,15 @@
KEEP_CONDITION = annoClass("KeepCondition");
KEEP_FOR_API = annoClass("KeepForApi");
+ USES_REFLECTION_TO_CONSTRUCT = annoClass("UsesReflectionToConstruct");
+ USES_REFLECTION_TO_ACCESS_METHOD = annoClass("UsesReflectionToAccessMethod");
+ USES_REFLECTION_TO_ACCESS_FIELD = annoClass("UsesReflectionToAccessField");
+ UNCONDITIONALLY_KEEP = annoClass("UnconditionallyKeep");
+ REPEATABLE_ANNOTATIONS =
+ ImmutableList.of(
+ USES_REFLECTION_TO_CONSTRUCT,
+ USES_REFLECTION_TO_ACCESS_METHOD,
+ USES_REFLECTION_TO_ACCESS_FIELD);
KEEP_ITEM_KIND = annoClass("KeepItemKind");
KIND_ONLY_CLASS = enumRef(KEEP_ITEM_KIND, "ONLY_CLASS");
KIND_ONLY_MEMBERS = enumRef(KEEP_ITEM_KIND, "ONLY_MEMBERS");
@@ -612,7 +662,18 @@
}
private void printAnnotationImports() {
- printImports(generateKotlin() ? KOTLIN_ANNOTATION_IMPORTS : ANNOTATION_IMPORTS);
+ printAnnotationImports(false);
+ }
+
+ private void printAnnotationImports(boolean incluceRepeatable) {
+ if (generateKotlin()) {
+ // Classes in kotlin.annotation does not need to be imported.
+ return;
+ }
+ if (incluceRepeatable) {
+ printImports(ImmutableList.of(Repeatable.class));
+ }
+ printImports(ANNOTATION_IMPORTS);
}
private void printOpenAnnotationClassTargettingAnnotations(String clazz) {
@@ -627,7 +688,7 @@
}
}
- private void printOpenAnnotationClassTargettingClassFieldlMethodCtor(String clazz) {
+ private void printOpenAnnotationClassTargetingClassFieldMethodCtor(String clazz) {
if (generateKotlin()) {
println("@Retention(AnnotationRetention.BINARY)");
println("@Target(");
@@ -1684,7 +1745,7 @@
"When a member is annotated, the member patterns cannot be used as the annotated"
+ " member itself fully defines the item to be kept (i.e., itself).")
.printDoc(this::println);
- printOpenAnnotationClassTargettingClassFieldlMethodCtor("KeepForApi");
+ printOpenAnnotationClassTargetingClassFieldMethodCtor("KeepForApi");
println();
withIndent(
() -> {
@@ -1773,7 +1834,7 @@
" // unreachable",
" }")
.printDoc(this::println);
- printOpenAnnotationClassTargettingClassFieldlMethodCtor(getUnqualifiedName(USES_REFLECTION));
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(getUnqualifiedName(USES_REFLECTION));
println();
withIndent(
() -> {
@@ -1786,6 +1847,267 @@
printCloseAnnotationClass();
}
+ private Group createAndroidXClassSelection(
+ Consumer<GroupMember> classConstantConsumer, Consumer<GroupMember> classNameConsumer) {
+ GroupMember classConstant = new GroupMember("classConstant").defaultUnspecifiedClass();
+ classConstantConsumer.accept(classConstant);
+ GroupMember className = new GroupMember("className").defaultEmptyString();
+ classNameConsumer.accept(className);
+ return new Group("class-selection")
+ .forAndroidX()
+ .addMember(classConstant)
+ .addMember(className);
+ }
+
+ private Group createAndroidXParameterSelection(
+ Consumer<GroupMember> paramsConsumer, Consumer<GroupMember> paramClassNamesConsumer) {
+ GroupMember params = new GroupMember("params").defaultUnspecifiedArray();
+ paramsConsumer.accept(params);
+ GroupMember paramClassNames =
+ new GroupMember("paramClassNames")
+ .defaultArrayValue(Reference.classFromClass(String.class), "\"\"");
+ paramClassNamesConsumer.accept(paramClassNames);
+ return new Group("constructor-parameters")
+ .forAndroidX()
+ .addMember(params)
+ .addMember(paramClassNames);
+ }
+
+ private Group createMethodNameSelection() {
+ return new Group("method-name")
+ .addMember(
+ new GroupMember("methodName")
+ .setDocTitle("Method name (or pattern) accessed by reflection.")
+ .requiredStringValue());
+ }
+
+ private Group createAndroidXReturnTypeSelection() {
+ return new Group("return-selection")
+ .forAndroidX()
+ .addMember(
+ new GroupMember("returnClass")
+ .setDocTitle("Return type of the method accessed by reflection.")
+ .addSection("Ignored if not specified.")
+ .defaultUnspecifiedClass())
+ .addMember(
+ new GroupMember("returnClassName")
+ .setDocTitle(
+ "Return type (or type pattern) of the method accessed by" + " reflection.")
+ .addSection("Ignored if not specified.")
+ .defaultEmptyString());
+ }
+
+ private Group createAndroidXFieldNameSelection() {
+ return new Group("field-name")
+ .addMember(
+ new GroupMember("fieldName")
+ .setDocTitle("Field name (or pattern) accessed by reflection.")
+ .requiredStringValue());
+ }
+
+ private Group createAndroidXFieldTypeSelection() {
+ return new Group("field-type-selection")
+ .forAndroidX()
+ .addMember(
+ new GroupMember("fieldClass")
+ .setDocTitle("Class of field accessed by reflection.")
+ .addSection("Ignored if not specified.")
+ .defaultUnspecifiedClass())
+ .addMember(
+ new GroupMember("fieldClassName")
+ .setDocTitle("Class (or class pattern) of field accessed by reflection.")
+ .addSection("Ignored if not specified.")
+ .defaultEmptyString());
+ }
+
+ private void generateUsesReflectionToConstruct() {
+ printCopyRight(2025);
+ printPackage();
+ printAnnotationImports(true);
+ if (generateKotlin()) {
+ println("import kotlin.reflect.KClass");
+ }
+ DocPrinter.printer()
+ .setDocTitle(
+ "The annotated code uses reflection to indirectly invoke a constructor of a"
+ + " class/interface, or its subclasses / interface implementers.")
+ .addSection(
+ "This annotation indicates to optimizers or shrinkers that the target constructor"
+ + " should be kept if the annotated code is reachable in the final application"
+ + " build.")
+ .addSection(
+ "`@UsesReflectionToConstruct()` is a convenience for"
+ + " `@UsesReflectionToAccessMethod(methodName = \"<init>\")`")
+ .addSection("@see UsesReflectionToAccessMethod")
+ .addSection("@see UsesReflectionToAccessField")
+ .printDoc(this::println);
+ println("@Repeatable");
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(
+ getUnqualifiedName(USES_REFLECTION_TO_CONSTRUCT));
+ println();
+ withIndent(
+ () -> {
+ createAndroidXClassSelection(
+ g -> g.setDocTitle("Class to be instantiated."),
+ g -> g.setDocTitle("Class to be instantiated."))
+ .generate(this);
+ println();
+ createAndroidXParameterSelection(
+ g ->
+ g.setDocTitle(
+ "Defines which constructor to keep by specifying the parameter list"
+ + " types.")
+ .addSection(
+ "If neither `param` nor `paramClassNames` is specified then"
+ + " constructors with all parameter lists are kept."),
+ g ->
+ g.setDocTitle(
+ "Defines which constructor to keep by specifying the parameter list"
+ + " types.")
+ .addSection(
+ "If neither `param` nor `paramClassNames` is specified then"
+ + " constructors with all parameter lists are kept."))
+ .generate(this);
+ });
+ printCloseAnnotationClass();
+ }
+
+ private void generateUsesReflectionToAccessMethod() {
+ printCopyRight(2025);
+ printPackage();
+ printAnnotationImports(true);
+ if (generateKotlin()) {
+ println("import kotlin.reflect.KClass");
+ }
+ DocPrinter.printer()
+ .setDocTitle(
+ "The annotated code uses reflection to indirectly access a method of the specified"
+ + " class/interface, or its subclasses / interface implementers.")
+ .addSection(
+ "This annotation indicates to optimizers or shrinkers that the target method should"
+ + " be preserved if the annotated code is reachable in the final application"
+ + " build.")
+ .addSection("@see UsesReflectionToConstruct")
+ .addSection("@see UsesReflectionToAccessField")
+ .printDoc(this::println);
+ println("@Repeatable");
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(
+ getUnqualifiedName(USES_REFLECTION_TO_ACCESS_METHOD));
+ println();
+ withIndent(
+ () -> {
+ createAndroidXClassSelection(
+ g -> g.setDocTitle("Class containing the method accessed by reflection."),
+ g ->
+ g.setDocTitle(
+ "Class name (or pattern) containing the method accessed by"
+ + " reflection."))
+ .generate(this);
+ println();
+ createMethodNameSelection().generate(this);
+ println();
+ createAndroidXParameterSelection(
+ g ->
+ g.setDocTitle(
+ "Defines which method to keep by specifying set of parameter"
+ + " classes passed.")
+ .addSection(
+ "If neither `param` nor `paramClassNames` is specified then"
+ + " methods with all parameter lists are kept."),
+ g ->
+ g.setDocTitle(
+ "Defines which method to keep by specifying set of parameter"
+ + " classes passed.")
+ .addSection(
+ "If neither `param` nor `paramClassNames` is specified then"
+ + " methods with all parameter lists are kept."))
+ .generate(this);
+ println();
+ createAndroidXReturnTypeSelection().generate(this);
+ });
+ printCloseAnnotationClass();
+ }
+
+ private void generateUsesReflectionToAccessField() {
+ printCopyRight(2025);
+ printPackage();
+ printAnnotationImports(true);
+ if (generateKotlin()) {
+ println("import kotlin.reflect.KClass");
+ }
+ DocPrinter.printer()
+ .setDocTitle(
+ "The annotated code uses reflection to indirectly access a field of the specified"
+ + " class/interface, or its subclasses / interface implementers.")
+ .addSection(
+ "This annotation indicates to optimizers or shrinkers that the target field should be"
+ + " preserved if the annotated code is reachable in the final application build.")
+ .addSection("@see UsesReflectionToConstruct", "@see UsesReflectionToAccessMethod")
+ .printDoc(this::println);
+ println("@Repeatable");
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(
+ getUnqualifiedName(USES_REFLECTION_TO_ACCESS_FIELD));
+ println();
+ withIndent(
+ () -> {
+ createAndroidXClassSelection(
+ g -> g.setDocTitle("Class containing the field accessed by reflection."),
+ g ->
+ g.setDocTitle(
+ "Class name (or pattern) containing the field accessed by"
+ + " reflection."))
+ .generate(this);
+ println();
+ createAndroidXFieldNameSelection().generate(this);
+ println();
+ createAndroidXFieldTypeSelection().generate(this);
+ });
+ printCloseAnnotationClass();
+ }
+
+ private void generateUnconditionallyKeep() {
+ printCopyRight(2025);
+ printPackage();
+ printAnnotationImports(true);
+ if (generateKotlin()) {
+ println("import kotlin.reflect.KClass");
+ }
+ DocPrinter.printer()
+ .setDocTitle(
+ "Indicates code which is accessed by references from outside of the application code"
+ + " directly, such as via JNI, reflection, or instantiation from platform"
+ + " framework code.")
+ .addSection(
+ "NOTE: This keep rule is unconditional, meaning that the annotated class, method, or"
+ + " field will always be preserved in the final application even if, for example,"
+ + " the surrounding code is never used.")
+ .addSection(
+ "If reflection or JNI access occurs inside the application, instead prefer the"
+ + " `@UsesReflection***` annotations, as they will keep conditionally, rather"
+ + " than unconditionally as this annotation does.")
+ .addSection(
+ "@see UsesReflectionToConstruct",
+ "@see UsesReflectionToAccessMethod",
+ "@see UsesReflectionToAccessField")
+ .printDoc(this::println);
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(
+ getUnqualifiedName(UNCONDITIONALLY_KEEP));
+ println();
+ withIndent(
+ () -> {
+ new Group("should-preserve-name")
+ .addMember(
+ new GroupMember("shouldPreserveName")
+ .setDocTitle("Should the name be preserved.")
+ .addSection(
+ "Generally this is true if the reference is external, but this can be"
+ + " disabled if the name isn't important.")
+ .defaultBooleanValue(true))
+ .generate(this);
+ });
+ printCloseAnnotationClass();
+ }
+
private void generateUsedByX(String annotationClassName, String doc) {
printCopyRight(2023);
printPackage();
@@ -1815,7 +2137,7 @@
"When a member is annotated, the member patterns cannot be used as the annotated"
+ " member itself fully defines the item to be kept (i.e., itself).")
.printDoc(this::println);
- printOpenAnnotationClassTargettingClassFieldlMethodCtor(annotationClassName);
+ printOpenAnnotationClassTargetingClassFieldMethodCtor(annotationClassName);
println();
withIndent(
() -> {
@@ -1901,6 +2223,10 @@
generateUsedByNativeConstants();
generateCheckRemovedConstants();
generateCheckOptimizedOutConstants();
+ // androidx annotations.
+ generateUsesReflectionToConstructConstants();
+ generateUsesReflectionToAccessMethodConstants();
+ generateUsesReflectionToAccessFieldConstants();
// Common item fields.
generateItemConstants();
// Inner annotation classes.
@@ -1933,10 +2259,20 @@
"Lcom/android/tools/r8/keepanno/annotations"
+ desc.substring(desc.lastIndexOf(DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR));
println("private static final String DESCRIPTOR = " + quote(desc) + ";");
+ if (REPEATABLE_ANNOTATIONS.contains(clazz)) {
+ String desc_container = desc.substring(0, desc.length() - 1) + "$Container;";
+ println(
+ "private static final String DESCRIPTOR_CONTAINER = " + quote(desc_container) + ";");
+ }
println("private static final String DESCRIPTOR_LEGACY = " + quote(desc_legacy) + ";");
println("public static boolean isDescriptor(String descriptor) {");
println(" return DESCRIPTOR.equals(descriptor) || DESCRIPTOR_LEGACY.equals(descriptor);");
println("}");
+ if (REPEATABLE_ANNOTATIONS.contains(clazz)) {
+ println("public static boolean isKotlinRepeatableContainerDescriptor(String descriptor) {");
+ println(" return DESCRIPTOR_CONTAINER.equals(descriptor);");
+ println("}");
+ }
println("public static String getDescriptor() {");
println(" return DESCRIPTOR;");
println("}");
@@ -2044,6 +2380,57 @@
println();
}
+ private void forEachUsesReflectionToConstructGroup(Consumer<Group> fn) {
+ fn.accept(createAndroidXClassSelection(g -> {}, g -> {}));
+ fn.accept(createAndroidXParameterSelection(g -> {}, g -> {}));
+ }
+
+ private void forEachUsesReflectionToAccessMethodGroup(Consumer<Group> fn) {
+ fn.accept(createAndroidXClassSelection(g -> {}, g -> {}));
+ fn.accept(createMethodNameSelection());
+ fn.accept(createAndroidXParameterSelection(g -> {}, g -> {}));
+ fn.accept(createAndroidXReturnTypeSelection());
+ }
+
+ private void forEachUsesReflectionToAccessFieldGroup(Consumer<Group> fn) {
+ fn.accept(createAndroidXClassSelection(g -> {}, g -> {}));
+ fn.accept(createAndroidXFieldNameSelection());
+ fn.accept(createAndroidXFieldTypeSelection());
+ }
+
+ private void generateUsesReflectionToConstructConstants() {
+ println("public static final class UsesReflectionToConstruct {");
+ withIndent(
+ () -> {
+ generateAnnotationConstants(USES_REFLECTION_TO_CONSTRUCT);
+ forEachUsesReflectionToConstructGroup(g -> g.generateConstants(this));
+ });
+ println("}");
+ println();
+ }
+
+ private void generateUsesReflectionToAccessMethodConstants() {
+ println("public static final class UsesReflectionToAccessMethod {");
+ withIndent(
+ () -> {
+ generateAnnotationConstants(USES_REFLECTION_TO_ACCESS_METHOD);
+ forEachUsesReflectionToConstructGroup(g -> g.generateConstants(this));
+ });
+ println("}");
+ println();
+ }
+
+ private void generateUsesReflectionToAccessFieldConstants() {
+ println("public static final class UsesReflectionToAccessField {");
+ withIndent(
+ () -> {
+ generateAnnotationConstants(USES_REFLECTION_TO_ACCESS_FIELD);
+ forEachUsesReflectionToConstructGroup(g -> g.generateConstants(this));
+ });
+ println("}");
+ println();
+ }
+
private List<Group> getItemGroups() {
return ImmutableList.of(
// Bindings.
@@ -2440,6 +2827,26 @@
generator.generateUsedByX("UsedByNative", "accessed from native code via JNI"),
write);
}
+ writeFile(
+ ANDROIDX_ANNO_PKG,
+ generator -> generator.USES_REFLECTION_TO_CONSTRUCT,
+ Generator::generateUsesReflectionToConstruct,
+ write);
+ writeFile(
+ ANDROIDX_ANNO_PKG,
+ generator -> generator.USES_REFLECTION_TO_ACCESS_METHOD,
+ Generator::generateUsesReflectionToAccessMethod,
+ write);
+ writeFile(
+ ANDROIDX_ANNO_PKG,
+ generator -> generator.USES_REFLECTION_TO_ACCESS_FIELD,
+ Generator::generateUsesReflectionToAccessField,
+ write);
+ writeFile(
+ ANDROIDX_ANNO_PKG,
+ generator -> generator.UNCONDITIONALLY_KEEP,
+ Generator::generateUnconditionallyKeep,
+ write);
}
}
}