Merge commit '334da78fd9db078e556bf0e069c213ade19ea3da' into dev-release
Change-Id: Ie4e501631a7c3ce82289c969aa01d170e4648dc7
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
index 66bb6d5..eceec17 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
@@ -6,12 +6,14 @@
import static com.android.tools.r8.keepanno.ast.KeepTypePattern.fromDescriptor;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader.UserBindingsHelper;
+import com.android.tools.r8.keepanno.ast.KeepBindingReference;
+import com.android.tools.r8.keepanno.ast.KeepClassBindingReference;
import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
-import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
@@ -103,13 +105,14 @@
return metaInfoBuilder;
}
- public KeepItemPattern toItemPattern() {
+ public KeepBindingReference defineBindingReference(UserBindingsHelper bindingsHelper) {
KeepQualifiedClassNamePattern className =
KeepQualifiedClassNamePattern.exactFromDescriptor(getClassDescriptor());
KeepClassItemPattern classItem =
KeepClassItemPattern.builder().setClassNamePattern(className).build();
+ KeepClassBindingReference classBinding = bindingsHelper.defineFreshClassBinding(classItem);
if (isClassContext()) {
- return classItem;
+ return classBinding;
}
KeepMemberPattern memberPattern;
if (isMethodContext()) {
@@ -118,10 +121,12 @@
assert isFieldContext();
memberPattern = createFieldPatternFromDescriptor();
}
- return KeepMemberItemPattern.builder()
- .setClassReference(classItem.toClassItemReference())
- .setMemberPattern(memberPattern)
- .build();
+ KeepMemberItemPattern memberItem =
+ KeepMemberItemPattern.builder()
+ .setClassBindingReference(classBinding)
+ .setMemberPattern(memberPattern)
+ .build();
+ return bindingsHelper.defineFreshMemberBinding(memberItem);
}
private KeepFieldPattern createFieldPatternFromDescriptor() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index c6b6f64..641eb23 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
import com.android.tools.r8.keepanno.ast.KeepCheck;
import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
+import com.android.tools.r8.keepanno.ast.KeepClassBindingReference;
import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
import com.android.tools.r8.keepanno.ast.KeepClassItemReference;
import com.android.tools.r8.keepanno.ast.KeepCondition;
@@ -45,9 +46,9 @@
import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
import com.android.tools.r8.keepanno.ast.KeepInstanceOfPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
-import com.android.tools.r8.keepanno.ast.KeepItemReference;
import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern.BuilderBase;
+import com.android.tools.r8.keepanno.ast.KeepMemberBindingReference;
import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern;
@@ -76,6 +77,7 @@
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
@@ -216,13 +218,12 @@
});
}
- private static KeepClassItemReference classReferenceFromName(String className) {
- return KeepClassItemReference.fromClassNamePattern(
- KeepQualifiedClassNamePattern.exact(className));
- }
-
- private static KeepConstraints getClassConstraintsOrDefault(KeepConstraints constraints) {
- return constraints != null ? constraints : KeepConstraints.defaultConstraints();
+ private static KeepClassBindingReference classReferenceFromName(
+ String className, UserBindingsHelper bindingsHelper) {
+ return bindingsHelper.defineFreshClassBinding(
+ KeepClassItemPattern.builder()
+ .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
+ .build());
}
/** Internal copy of the user-facing KeepItemKind */
@@ -376,11 +377,14 @@
return new KeepEdgeVisitor(parsingContext, parent::accept, setContext);
}
if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
- KeepClassItemPattern classItem =
- KeepClassItemPattern.builder()
- .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
- .build();
- return new UsesReflectionVisitor(parsingContext, parent::accept, setContext, classItem);
+ return new UsesReflectionVisitor(
+ parsingContext,
+ parent::accept,
+ setContext,
+ bindingsHelper ->
+ KeepClassItemPattern.builder()
+ .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
+ .build());
}
if (descriptor.equals(ForApi.DESCRIPTOR)) {
return new ForApiClassVisitor(parsingContext, parent::accept, setContext, className);
@@ -450,7 +454,10 @@
}
private static KeepMemberItemPattern createMethodItemContext(
- String className, String methodName, String methodDescriptor) {
+ String className,
+ String methodName,
+ String methodDescriptor,
+ UserBindingsHelper bindingsHelper) {
String returnTypeDescriptor = Type.getReturnType(methodDescriptor).getDescriptor();
Type[] argumentTypes = Type.getArgumentTypes(methodDescriptor);
KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
@@ -462,8 +469,9 @@
? KeepMethodReturnTypePattern.voidType()
: KeepMethodReturnTypePattern.fromType(
KeepTypePattern.fromDescriptor(returnTypeDescriptor));
+
return KeepMemberItemPattern.builder()
- .setClassReference(classReferenceFromName(className))
+ .setClassBindingReference(classReferenceFromName(className, bindingsHelper))
.setMemberPattern(
KeepMethodPattern.builder()
.setNamePattern(KeepMethodNamePattern.exact(methodName))
@@ -519,14 +527,16 @@
parsingContext,
parent::accept,
setContext,
- createMethodItemContext(className, methodName, methodDescriptor));
+ bindingsHelper ->
+ createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper));
}
if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
return new ForApiMemberVisitor(
parsingContext,
parent::accept,
setContext,
- createMethodItemContext(className, methodName, methodDescriptor));
+ bindingsHelper ->
+ createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper));
}
if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
|| descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
@@ -534,14 +544,16 @@
parsingContext,
parent::accept,
setContext,
- createMethodItemContext(className, methodName, methodDescriptor));
+ bindingsHelper ->
+ createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper));
}
if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
return new CheckRemovedMemberVisitor(
parsingContext,
parent::accept,
setContext,
- createMethodItemContext(className, methodName, methodDescriptor),
+ bindingsHelper ->
+ createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper),
KeepCheckKind.REMOVED);
}
if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
@@ -549,7 +561,8 @@
parsingContext,
parent::accept,
setContext,
- createMethodItemContext(className, methodName, methodDescriptor),
+ bindingsHelper ->
+ createMethodItemContext(className, methodName, methodDescriptor, bindingsHelper),
KeepCheckKind.OPTIMIZED_OUT);
}
return null;
@@ -588,11 +601,14 @@
}
private static KeepMemberItemPattern createMemberItemContext(
- String className, String fieldName, String fieldTypeDescriptor) {
+ String className,
+ String fieldName,
+ String fieldTypeDescriptor,
+ UserBindingsHelper bindingsHelper) {
KeepFieldTypePattern typePattern =
KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(fieldTypeDescriptor));
return KeepMemberItemPattern.builder()
- .setClassReference(classReferenceFromName(className))
+ .setClassBindingReference(classReferenceFromName(className, bindingsHelper))
.setMemberPattern(
KeepFieldPattern.builder()
.setNamePattern(KeepFieldNamePattern.exact(fieldName))
@@ -648,14 +664,16 @@
parsingContext,
parent::accept,
setContext,
- createMemberItemContext(className, fieldName, fieldTypeDescriptor));
+ bindingsHelper ->
+ createMemberItemContext(className, fieldName, fieldTypeDescriptor, bindingsHelper));
}
if (descriptor.equals(ForApi.DESCRIPTOR)) {
return new ForApiMemberVisitor(
parsingContext,
parent::accept,
setContext,
- createMemberItemContext(className, fieldName, fieldTypeDescriptor));
+ bindingsHelper ->
+ createMemberItemContext(className, fieldName, fieldTypeDescriptor, bindingsHelper));
}
if (descriptor.equals(UsedByReflection.DESCRIPTOR)
|| descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
@@ -663,7 +681,8 @@
parsingContext,
parent::accept,
setContext,
- createMemberItemContext(className, fieldName, fieldTypeDescriptor));
+ bindingsHelper ->
+ createMemberItemContext(className, fieldName, fieldTypeDescriptor, bindingsHelper));
}
return null;
}
@@ -674,10 +693,14 @@
void accept(T result);
}
- private static class UserBindingsHelper {
+ public static class UserBindingsHelper {
private final KeepBindings.Builder builder = KeepBindings.builder();
private final Map<String, KeepBindingSymbol> userNames = new HashMap<>();
+ public boolean isUserBinding(KeepBindingSymbol symbol) {
+ return userNames.get(symbol.toString()) == symbol;
+ }
+
public KeepBindingSymbol resolveUserBinding(String name) {
return userNames.computeIfAbsent(name, builder::create);
}
@@ -686,14 +709,65 @@
builder.addBinding(resolveUserBinding(name), item);
}
- public KeepBindingSymbol defineFreshBinding(String name, KeepItemPattern item) {
+ public void replaceWithUserBinding(
+ String name, KeepBindingReference replacedBinding, ParsingContext parsingContext) {
+ if (isUserBinding(replacedBinding.getName())) {
+ // The language currently disallows aliasing bindings, thus a binding cannot directly be
+ // defined by a reference to another binding.
+ throw parsingContext.error(
+ "Invalid binding reference to '"
+ + replacedBinding
+ + "' in binding definition of '"
+ + name
+ + "'");
+ }
+ KeepItemPattern item = builder.deleteBinding(replacedBinding.getName());
+ defineUserBinding(name, item);
+ }
+
+ public KeepClassBindingReference defineFreshClassBinding(KeepClassItemPattern itemPattern) {
+ KeepBindingSymbol symbol = defineFreshBinding("CLASS", itemPattern);
+ return KeepClassBindingReference.forClass(symbol);
+ }
+
+ public KeepMemberBindingReference defineFreshMemberBinding(KeepMemberItemPattern itemPattern) {
+ KeepBindingSymbol symbol = defineFreshBinding("MEMBER", itemPattern);
+ return KeepMemberBindingReference.forMember(symbol);
+ }
+
+ public KeepBindingReference defineFreshItemBinding(String name, KeepItemPattern itemPattern) {
+ KeepBindingSymbol symbol = defineFreshBinding(name, itemPattern);
+ return KeepBindingReference.forItem(symbol, itemPattern);
+ }
+
+ private KeepBindingSymbol defineFreshBinding(String name, KeepItemPattern item) {
KeepBindingSymbol symbol = builder.generateFreshSymbol(name);
builder.addBinding(symbol, item);
return symbol;
}
- public KeepItemPattern getItemForBinding(KeepBindingReference bindingReference) {
- return builder.getItemForBinding(bindingReference.getName());
+ public KeepItemPattern getItem(KeepBindingReference bindingReference) {
+ return bindingReference.isClassType()
+ ? getClassItem(bindingReference.asClassBindingReference())
+ : getMemberItem(bindingReference.asMemberBindingReference());
+ }
+
+ public KeepClassItemPattern getClassItem(KeepClassBindingReference reference) {
+ KeepItemPattern item = builder.getItemForBinding(reference.getName());
+ assert item != null;
+ return item.asClassItemPattern();
+ }
+
+ public KeepMemberItemPattern getMemberItem(KeepMemberBindingReference reference) {
+ KeepItemPattern item = builder.getItemForBinding(reference.getName());
+ assert item != null;
+ return item.asMemberItemPattern();
+ }
+
+ public KeepClassItemPattern getClassItemForReference(KeepClassItemReference itemReference) {
+ return itemReference.isBindingReference()
+ ? getItem(itemReference.asBindingReference()).asClassItemPattern()
+ : itemReference.asClassItemPattern();
}
public KeepBindings build() {
@@ -829,15 +903,26 @@
"Invalid extracted annotation, must specify either an edge, a removed check, or an"
+ " optimized-out check.");
}
+ KeepEdgeMetaInfo metaInfo = context.applyToMetadata(KeepEdgeMetaInfo.builder()).build();
KeepCheckKind kind = isCheckRemoved ? KeepCheckKind.REMOVED : KeepCheckKind.OPTIMIZED_OUT;
- parent.accept(
- KeepCheck.builder()
- .setMetaInfo(context.applyToMetadata(KeepEdgeMetaInfo.builder()).build())
- .setKind(kind)
- .setItemPattern(context.toItemPattern())
- .build());
+ UserBindingsHelper bindingsHelper = new UserBindingsHelper();
+ KeepBindingReference contextBinding = context.defineBindingReference(bindingsHelper);
+ parent.accept(buildKeepCheckFromItem(metaInfo, kind, contextBinding, bindingsHelper));
super.visitEnd();
}
+
+ private static KeepCheck buildKeepCheckFromItem(
+ KeepEdgeMetaInfo metaInfo,
+ KeepCheckKind kind,
+ KeepBindingReference itemReference,
+ UserBindingsHelper bindingsHelper) {
+ return KeepCheck.builder()
+ .setMetaInfo(metaInfo)
+ .setKind(kind)
+ .setBindings(bindingsHelper.build())
+ .setItemBindingReference(itemReference)
+ .build();
+ }
}
private static class KeepEdgeVisitor extends AnnotationVisitorBase {
@@ -952,6 +1037,12 @@
}
@Override
+ public boolean useBindingForClassAndMembers() {
+ // KeepForApi targets should be disjunctive CLASS_AND_MEMBERS
+ return false;
+ }
+
+ @Override
public void visitEnd() {
if (!getKind().isOnlyClass() && isDefaultMemberDeclaration()) {
// If no member declarations have been made, set public & protected as the default.
@@ -964,15 +1055,14 @@
// Default constraints should retain the expected meta-data, such as signatures, annotations
// exception-throws etc.
KeepConstraints defaultForApiConstraints = KeepConstraints.all();
- Collection<KeepItemReference> items = getItemsWithoutBinding();
- for (KeepItemReference item : items) {
- if (item.isBindingReference()) {
- throw parsingContext.error("cannot reference bindings");
- }
+ Collection<KeepBindingReference> items = getItems();
+ for (KeepBindingReference bindingReference : items) {
+ KeepItemPattern item = bindingsHelper.getItem(bindingReference);
KeepClassItemPattern classItemPattern = item.asClassItemPattern();
if (classItemPattern == null) {
- assert item.isMemberItemReference();
- classItemPattern = item.asMemberItemPattern().getClassReference().asClassItemPattern();
+ assert item.isMemberItemPattern();
+ KeepClassItemReference classReference = item.asMemberItemPattern().getClassReference();
+ classItemPattern = bindingsHelper.getClassItemForReference(classReference);
}
String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
String itemDescriptor = classItemPattern.getClassNamePattern().getExactDescriptor();
@@ -987,7 +1077,7 @@
}
consequences.addTarget(
KeepTarget.builder()
- .setItemReference(item)
+ .setItemBindingReference(bindingReference)
.setConstraints(defaultForApiConstraints)
.build());
}
@@ -1018,25 +1108,39 @@
AnnotationParsingContext parsingContext,
Parent<KeepEdge> parent,
Consumer<KeepEdgeMetaInfo.Builder> addContext,
- KeepMemberItemPattern context) {
+ Function<UserBindingsHelper, KeepMemberItemPattern> contextBuilder) {
super(parsingContext);
this.parsingContext = parsingContext;
this.parent = parent;
addContext.accept(metaInfoBuilder);
- // Create a binding for the context such that the class and member are shared.
- KeepClassItemPattern classContext = context.getClassReference().asClassItemPattern();
- KeepBindingSymbol bindingSymbol = bindingsHelper.defineFreshBinding("CONTEXT", classContext);
- KeepClassItemReference classReference =
- KeepBindingReference.forClass(bindingSymbol).toClassItemReference();
- consequences.addTarget(
- KeepTarget.builder()
- .setItemPattern(
- KeepMemberItemPattern.builder()
- .copyFrom(context)
- .setClassReference(classReference)
- .build())
- .build());
- consequences.addTarget(KeepTarget.builder().setItemReference(classReference).build());
+ KeepMemberItemPattern context = contextBuilder.apply(bindingsHelper);
+ KeepClassItemReference classReference = context.getClassReference();
+ if (classReference.isBindingReference()) {
+ consequences.addTarget(
+ KeepTarget.builder()
+ .setItemBindingReference(bindingsHelper.defineFreshMemberBinding(context))
+ .build());
+ consequences.addTarget(
+ KeepTarget.builder()
+ .setItemBindingReference(classReference.asBindingReference())
+ .build());
+ } else {
+ // Create a binding for the context such that the class and member are shared.
+ KeepClassItemPattern classContext = classReference.asClassItemPattern();
+ KeepClassBindingReference classBindingReference =
+ bindingsHelper.defineFreshClassBinding(classContext);
+ consequences.addTarget(
+ KeepTarget.builder()
+ .setItemBindingReference(
+ bindingsHelper.defineFreshMemberBinding(
+ KeepMemberItemPattern.builder()
+ .copyFrom(context)
+ .setClassBindingReference(classBindingReference)
+ .build()))
+ .build());
+ consequences.addTarget(
+ KeepTarget.builder().setItemBindingReference(classBindingReference).build());
+ }
}
@Override
@@ -1200,34 +1304,24 @@
}
@Override
+ public boolean useBindingForClassAndMembers() {
+ // When annotating a class with UsedByReflection and including member patterns, don't
+ // require the member patterns to match to retain the class.
+ return false;
+ }
+
+ @Override
public void visitEnd() {
if (getKind() == null && !isDefaultMemberDeclaration()) {
// If no explict kind is set and member declarations have been made, keep the class too.
visitEnum(null, Kind.DESCRIPTOR, Kind.CLASS_AND_MEMBERS);
}
super.visitEnd();
- Collection<KeepItemReference> items = getItemsWithoutBinding();
- for (KeepItemReference item : items) {
- if (item.isBindingReference()) {
- // TODO(b/248408342): The edge can have preconditions so it should support bindings!
- throw parsingContext.error("cannot reference bindings");
- }
- KeepItemPattern itemPattern = item.asItemPattern();
- KeepClassItemPattern holderPattern =
- itemPattern.isClassItemPattern()
- ? itemPattern.asClassItemPattern()
- : itemPattern.asMemberItemPattern().getClassReference().asClassItemPattern();
- String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
- String itemDescriptor = holderPattern.getClassNamePattern().getExactDescriptor();
- if (!descriptor.equals(itemDescriptor)) {
- throw parsingContext.error("must reference its class context " + className);
- }
- if (!holderPattern.getInstanceOfPattern().isAny()) {
- throw parsingContext.error("cannot define an 'extends' pattern.");
- }
+ for (KeepBindingReference bindingReference : getItems()) {
+ verifyItemStructure(bindingReference);
consequences.addTarget(
KeepTarget.builder()
- .setItemPattern(itemPattern)
+ .setItemBindingReference(bindingReference)
.setConstraints(
constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()))
.build());
@@ -1239,6 +1333,23 @@
.setConsequences(consequences.build())
.build());
}
+
+ private void verifyItemStructure(KeepBindingReference bindingReference) {
+ KeepItemPattern itemPattern = bindingsHelper.getItem(bindingReference);
+ KeepClassItemPattern holderPattern =
+ itemPattern.isClassItemPattern()
+ ? itemPattern.asClassItemPattern()
+ : bindingsHelper.getClassItemForReference(
+ itemPattern.asMemberItemPattern().getClassReference());
+ String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
+ String itemDescriptor = holderPattern.getClassNamePattern().getExactDescriptor();
+ if (!descriptor.equals(itemDescriptor)) {
+ throw parsingContext.error("must reference its class context " + className);
+ }
+ if (!holderPattern.getInstanceOfPattern().isAny()) {
+ throw parsingContext.error("cannot define an 'extends' pattern.");
+ }
+ }
}
/**
@@ -1250,7 +1361,7 @@
private final ParsingContext parsingContext;
private final Parent<KeepEdge> parent;
- private final KeepItemPattern context;
+ private final KeepMemberItemPattern context;
private final KeepEdge.Builder builder = KeepEdge.builder();
private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
@@ -1262,11 +1373,11 @@
AnnotationParsingContext parsingContext,
Parent<KeepEdge> parent,
Consumer<KeepEdgeMetaInfo.Builder> addContext,
- KeepItemPattern context) {
+ Function<UserBindingsHelper, KeepMemberItemPattern> contextBuilder) {
super(parsingContext);
this.parsingContext = parsingContext;
this.parent = parent;
- this.context = context;
+ this.context = contextBuilder.apply(bindingsHelper);
addContext.accept(metaInfoBuilder);
constraintsParser = new ConstraintDeclarationParser(parsingContext);
}
@@ -1320,8 +1431,9 @@
if (kind.isOnlyClass()) {
throw parsingContext.error("kind must include its member");
}
- assert context.isMemberItemPattern();
KeepMemberItemPattern memberContext = context.asMemberItemPattern();
+ KeepMemberBindingReference contextBinding =
+ bindingsHelper.defineFreshMemberBinding(memberContext);
if (kind.includesClass()) {
consequences.addTarget(
KeepTarget.builder().setItemReference(memberContext.getClassReference()).build());
@@ -1331,7 +1443,7 @@
KeepTarget.builder()
.setConstraints(
constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()))
- .setItemPattern(context)
+ .setItemBindingReference(contextBinding)
.build());
parent.accept(
builder
@@ -1367,11 +1479,15 @@
AnnotationParsingContext parsingContext,
Parent<KeepEdge> parent,
Consumer<KeepEdgeMetaInfo.Builder> addContext,
- KeepItemPattern context) {
+ Function<UserBindingsHelper, KeepItemPattern> contextBuilder) {
super(parsingContext);
this.parsingContext = parsingContext;
this.parent = parent;
- preconditions.addCondition(KeepCondition.builder().setItemPattern(context).build());
+ KeepItemPattern context = contextBuilder.apply(bindingsHelper);
+ KeepBindingReference contextBinding =
+ bindingsHelper.defineFreshItemBinding("CONTEXT", context);
+ preconditions.addCondition(
+ KeepCondition.builder().setItemBindingReference(contextBinding).build());
addContext.accept(metaInfoBuilder);
}
@@ -1531,11 +1647,12 @@
@Override
public void visitEnd() {
+ UserBindingsHelper bindingsHelper = new UserBindingsHelper();
KeepItemVisitorBase itemVisitor =
new KeepItemVisitorBase(parsingContext) {
@Override
public UserBindingsHelper getBindingsHelper() {
- throw parsingContext.error("bindings not supported");
+ return bindingsHelper;
}
};
itemVisitor.visit(Item.className, className);
@@ -1544,7 +1661,8 @@
KeepCheck.builder()
.setMetaInfo(metaInfoBuilder.build())
.setKind(kind)
- .setItemPattern(itemVisitor.getItemReference().asItemPattern())
+ .setBindings(itemVisitor.getBindingsHelper().build())
+ .setItemBindingReference(itemVisitor.getItemReference())
.build());
}
}
@@ -1553,19 +1671,20 @@
private static class CheckRemovedMemberVisitor extends AnnotationVisitorBase {
private final Parent<KeepDeclaration> parent;
- private final KeepItemPattern context;
+ private final KeepMemberBindingReference context;
private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
private final KeepCheckKind kind;
+ private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
CheckRemovedMemberVisitor(
AnnotationParsingContext parsingContext,
Parent<KeepDeclaration> parent,
Consumer<KeepEdgeMetaInfo.Builder> addContext,
- KeepItemPattern context,
+ Function<UserBindingsHelper, KeepMemberItemPattern> contextBuilder,
KeepCheckKind kind) {
super(parsingContext);
this.parent = parent;
- this.context = context;
+ this.context = bindingsHelper.defineFreshMemberBinding(contextBuilder.apply(bindingsHelper));
this.kind = kind;
addContext.accept(metaInfoBuilder);
}
@@ -1583,20 +1702,17 @@
public void visitEnd() {
super.visitEnd();
parent.accept(
- KeepCheck.builder()
- .setMetaInfo(metaInfoBuilder.build())
- .setKind(kind)
- .setItemPattern(context)
- .build());
+ ExtractedAnnotationVisitor.buildKeepCheckFromItem(
+ metaInfoBuilder.build(), kind, context, bindingsHelper));
}
}
- private static class ClassDeclarationParser extends DeclarationParser<KeepClassItemReference> {
+ private static class ClassDeclarationParser extends DeclarationParser<KeepClassBindingReference> {
private final ParsingContext parsingContext;
private final Supplier<UserBindingsHelper> getBindingsHelper;
- private KeepClassItemReference boundClassItemReference = null;
+ private KeepClassBindingReference boundClassReference = null;
private final ClassNameParser classNameParser;
private final ClassNameParser annotatedByParser;
private final InstanceOfParser instanceOfParser;
@@ -1635,7 +1751,7 @@
}
private boolean isBindingReferenceDefined() {
- return boundClassItemReference != null;
+ return boundClassReference != null;
}
private boolean classPatternsAreDeclared() {
@@ -1656,37 +1772,41 @@
return isBindingReferenceDefined() || super.isDeclared();
}
- public KeepClassItemReference getValue() {
+ public KeepClassBindingReference getValue() {
checkAllowedDefinitions();
if (isBindingReferenceDefined()) {
- return boundClassItemReference;
+ return boundClassReference;
}
+ KeepClassItemPattern classItemPattern;
if (classPatternsAreDeclared()) {
- return KeepClassItemPattern.builder()
- .setClassNamePattern(
- classNameParser.getValueOrDefault(KeepQualifiedClassNamePattern.any()))
- .setAnnotatedByPattern(OptionalPattern.ofNullable(annotatedByParser.getValue()))
- .setInstanceOfPattern(instanceOfParser.getValueOrDefault(KeepInstanceOfPattern.any()))
- .build()
- .toClassItemReference();
+ classItemPattern =
+ KeepClassItemPattern.builder()
+ .setClassNamePattern(
+ classNameParser.getValueOrDefault(KeepQualifiedClassNamePattern.any()))
+ .setAnnotatedByPattern(OptionalPattern.ofNullable(annotatedByParser.getValue()))
+ .setInstanceOfPattern(
+ instanceOfParser.getValueOrDefault(KeepInstanceOfPattern.any()))
+ .build();
+ } else {
+ assert isDefault();
+ classItemPattern = KeepClassItemPattern.any();
}
- assert isDefault();
- return KeepClassItemPattern.any().toClassItemReference();
+ return getBindingsHelper.get().defineFreshClassBinding(classItemPattern);
}
- public void setBindingReference(KeepClassItemReference bindingReference) {
+ public void setBindingReference(KeepClassBindingReference bindingReference) {
if (isBindingReferenceDefined()) {
throw parsingContext.error(
"Cannot reference multiple class bindings for a single class item");
}
- this.boundClassItemReference = bindingReference;
+ this.boundClassReference = bindingReference;
}
@Override
public boolean tryParse(String name, Object value) {
if (name.equals(Item.classFromBinding) && value instanceof String) {
KeepBindingSymbol symbol = getBindingsHelper.get().resolveUserBinding((String) value);
- setBindingReference(KeepBindingReference.forClass(symbol).toClassItemReference());
+ setBindingReference(KeepBindingReference.forClass(symbol));
return true;
}
return super.tryParse(name, value);
@@ -1932,8 +2052,12 @@
public abstract UserBindingsHelper getBindingsHelper();
- // Constructed item available once visitEnd has been called.
- private KeepItemReference itemReference = null;
+ public boolean useBindingForClassAndMembers() {
+ return true;
+ }
+
+ // Constructed bindings available once visitEnd has been called.
+ private List<KeepBindingReference> itemReferences = null;
KeepItemVisitorBase(ParsingContext parsingContext) {
super(parsingContext);
@@ -1942,88 +2066,21 @@
memberDeclaration = new MemberDeclarationParser(parsingContext);
}
- public Collection<KeepItemReference> getItemsWithoutBinding() {
- if (itemReference == null) {
- throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
+ public Collection<KeepBindingReference> getItems() {
+ if (itemReferences == null || kind == null) {
+ throw parsingContext.error("Items not finalized. Missing call to visitEnd()");
}
- if (itemReference.isBindingReference()) {
- return Collections.singletonList(itemReference);
- }
- // Kind is only null if item is a "binding reference".
- if (kind == null) {
- throw parsingContext.error("Unexpected state: unknown kind for an item pattern");
- }
- if (kind.includesClassAndMembers()) {
- assert !itemReference.isBindingReference();
- KeepItemPattern itemPattern = itemReference.asItemPattern();
- KeepClassItemReference classReference;
- KeepMemberItemPattern memberPattern;
- if (itemPattern.isClassItemPattern()) {
- classReference = itemPattern.asClassItemPattern().toClassItemReference();
- memberPattern =
- KeepMemberItemPattern.builder()
- .setClassReference(classReference)
- .setMemberPattern(KeepMemberPattern.allMembers())
- .build();
- } else {
- memberPattern = itemPattern.asMemberItemPattern();
- classReference = memberPattern.getClassReference();
- }
- return ImmutableList.of(classReference, memberPattern.toItemReference());
- } else {
- return Collections.singletonList(itemReference);
- }
+ return itemReferences;
}
- public Collection<KeepItemReference> getItemsWithBinding() {
- if (itemReference == null) {
+ public KeepBindingReference getItemReference() {
+ if (itemReferences == null) {
throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
}
- if (itemReference.isBindingReference()) {
- return Collections.singletonList(itemReference);
+ if (itemReferences.size() > 1) {
+ throw parsingContext.error("Ambiguous item reference.");
}
- // Kind is only null if item is a "binding reference".
- if (kind == null) {
- throw parsingContext.error("Unexpected state: unknown kind for an item pattern");
- }
- if (kind.includesClassAndMembers()) {
- KeepItemPattern itemPattern = itemReference.asItemPattern();
- // Ensure we have a member item linked to the correct class.
- KeepMemberItemPattern memberItemPattern;
- if (itemPattern.isClassItemPattern()) {
- memberItemPattern =
- KeepMemberItemPattern.builder()
- .setClassReference(itemPattern.asClassItemPattern().toClassItemReference())
- .build();
- } else {
- memberItemPattern = itemPattern.asMemberItemPattern();
- }
- // If the class is not a binding, introduce the binding and rewrite the member.
- KeepClassItemReference classItemReference = memberItemPattern.getClassReference();
- if (classItemReference.isClassItemPattern()) {
- KeepClassItemPattern classItemPattern = classItemReference.asClassItemPattern();
- KeepBindingSymbol symbol =
- getBindingsHelper().defineFreshBinding("CLASS", classItemPattern);
- classItemReference = KeepBindingReference.forClass(symbol).toClassItemReference();
- memberItemPattern =
- KeepMemberItemPattern.builder()
- .copyFrom(memberItemPattern)
- .setClassReference(classItemReference)
- .build();
- }
- assert classItemReference.isBindingReference();
- assert memberItemPattern.getClassReference().equals(classItemReference);
- return ImmutableList.of(classItemReference, memberItemPattern.toItemReference());
- } else {
- return Collections.singletonList(itemReference);
- }
- }
-
- public KeepItemReference getItemReference() {
- if (itemReference == null) {
- throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
- }
- return itemReference;
+ return itemReferences.get(0);
}
public ItemKind getKind() {
@@ -2082,16 +2139,53 @@
return super.visitArray(name);
}
+ private void visitEndWithMemberBindingReference() {
+ if (classDeclaration.isDeclared() || memberDeclaration.isDeclared()) {
+ throw parsingContext.error(
+ "Cannot define an item explicitly and via a member-binding reference");
+ }
+ KeepBindingSymbol symbol = getBindingsHelper().resolveUserBinding(memberBindingReference);
+ KeepMemberBindingReference memberBinding = KeepBindingReference.forMember(symbol);
+
+ // If no explicit kind is set, the member binding implies it is just members.
+ if (kind == null) {
+ kind = ItemKind.ONLY_MEMBERS;
+ }
+
+ if (!kind.includesClass()) {
+ itemReferences = Collections.singletonList(memberBinding);
+ return;
+ }
+
+ KeepClassItemReference holderReference =
+ getBindingsHelper().getItem(memberBinding).asMemberItemPattern().getClassReference();
+ itemReferences =
+ ImmutableList.of(ensureCorrectBindingForMemberHolder(holderReference), memberBinding);
+ }
+
+ private KeepClassBindingReference ensureCorrectBindingForMemberHolder(
+ KeepClassItemReference holderReference) {
+ if (holderReference.isBindingReference()) {
+ KeepClassBindingReference bindingReference =
+ holderReference.asBindingReference().asClassBindingReference();
+ return ensureCorrectBindingForMemberHolder(bindingReference);
+ }
+ return getBindingsHelper().defineFreshClassBinding(holderReference.asClassItemPattern());
+ }
+
+ private KeepClassBindingReference ensureCorrectBindingForMemberHolder(
+ KeepClassBindingReference bindingReference) {
+ return useBindingForClassAndMembers()
+ ? bindingReference
+ : getBindingsHelper()
+ .defineFreshClassBinding(getBindingsHelper().getClassItem(bindingReference));
+ }
+
@Override
public void visitEnd() {
// Item defined by binding reference.
if (memberBindingReference != null) {
- if (classDeclaration.isDeclared() || memberDeclaration.isDeclared() || kind != null) {
- throw parsingContext.error(
- "Cannot define an item explicitly and via a member-binding reference");
- }
- KeepBindingSymbol symbol = getBindingsHelper().resolveUserBinding(memberBindingReference);
- itemReference = KeepBindingReference.forMember(symbol).toItemReference();
+ visitEndWithMemberBindingReference();
return;
}
@@ -2116,7 +2210,7 @@
if (memberDeclaration.isDeclared()) {
throw parsingContext.error("Item pattern for members is incompatible with kind " + kind);
}
- itemReference = classDeclaration.getValue();
+ itemReferences = Collections.singletonList(classDeclaration.getValue());
return;
}
@@ -2146,13 +2240,19 @@
}
}
- KeepClassItemReference classReference = classDeclaration.getValue();
- KeepItemPattern itemPattern =
- KeepMemberItemPattern.builder()
- .setClassReference(classReference)
- .setMemberPattern(memberPattern)
- .build();
- itemReference = itemPattern.toItemReference();
+ ImmutableList.Builder<KeepBindingReference> builder = ImmutableList.builder();
+ KeepClassBindingReference classReference = classDeclaration.getValue();
+ builder.add(
+ getBindingsHelper()
+ .defineFreshMemberBinding(
+ KeepMemberItemPattern.builder()
+ .setClassBindingReference(classReference)
+ .setMemberPattern(memberPattern)
+ .build()));
+ if (kind.includesClass()) {
+ builder.add(ensureCorrectBindingForMemberHolder(classReference));
+ }
+ itemReferences = builder.build();
}
}
@@ -2185,18 +2285,8 @@
@Override
public void visitEnd() {
super.visitEnd();
- KeepItemReference item = getItemReference();
- // The language currently disallows aliasing bindings, thus a binding cannot directly be
- // defined by a reference to another binding.
- if (item.isBindingReference()) {
- throw parsingContext.error(
- "Invalid binding reference to '"
- + item.asBindingReference()
- + "' in binding definition of '"
- + bindingName
- + "'");
- }
- helper.defineUserBinding(bindingName, item.asItemPattern());
+ KeepBindingReference bindingReference = getItemReference();
+ helper.replaceWithUserBinding(bindingName, bindingReference, parsingContext);
}
}
@@ -2243,8 +2333,8 @@
super.visitEnd();
builder.setConstraints(
constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()));
- for (KeepItemReference item : getItemsWithBinding()) {
- parent.accept(builder.setItemReference(item).build());
+ for (KeepBindingReference item : getItems()) {
+ parent.accept(builder.setItemBindingReference(item).build());
}
}
}
@@ -2271,7 +2361,7 @@
@Override
public void visitEnd() {
super.visitEnd();
- parent.accept(KeepCondition.builder().setItemReference(getItemReference()).build());
+ parent.accept(KeepCondition.builder().setItemBindingReference(getItemReference()).build());
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
index df6a224..46ee664 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.ast;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -57,6 +59,28 @@
.collect(Collectors.joining(", "));
}
+ public void verify(KeepItemReference... references) {
+ verify(Arrays.asList(references));
+ }
+
+ public void verify(Collection<KeepItemReference> references) {
+ for (KeepItemReference reference : references) {
+ if (reference.isBindingReference()) {
+ KeepBindingReference bindingReference = reference.asBindingReference();
+ if (!bindings.containsKey(bindingReference.getName())) {
+ throw new KeepEdgeException("Unbound reference to " + bindingReference);
+ }
+ } else {
+ KeepItemPattern itemPattern = reference.asItemPattern();
+ for (KeepBindingReference bindingReference : itemPattern.getBindingReferences()) {
+ if (!bindings.containsKey(bindingReference.getName())) {
+ throw new KeepEdgeException("Unbound reference to " + bindingReference);
+ }
+ }
+ }
+ }
+ }
+
/**
* A unique binding.
*
@@ -187,6 +211,14 @@
return this;
}
+ public KeepItemPattern deleteBinding(KeepBindingSymbol symbol) {
+ KeepItemPattern removed = bindings.remove(symbol);
+ if (removed == null) {
+ throw new KeepEdgeException("Invalid deletion of binding '" + symbol + "'");
+ }
+ return removed;
+ }
+
public KeepItemPattern getItemForBinding(KeepBindingSymbol symbol) {
return bindings.get(symbol);
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
index 1b2e42d..4c21cad 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
@@ -14,7 +14,8 @@
private KeepEdgeMetaInfo metaInfo = KeepEdgeMetaInfo.none();
private KeepCheckKind kind = KeepCheckKind.REMOVED;
- private KeepItemPattern itemPattern;
+ private KeepBindings bindings = KeepBindings.none();
+ private KeepItemReference itemReference;
public Builder setMetaInfo(KeepEdgeMetaInfo metaInfo) {
this.metaInfo = metaInfo;
@@ -26,16 +27,27 @@
return this;
}
- public Builder setItemPattern(KeepItemPattern itemPattern) {
- this.itemPattern = itemPattern;
+ public Builder setBindings(KeepBindings bindings) {
+ this.bindings = bindings;
+ return this;
+ }
+
+ public Builder setItemBindingReference(KeepBindingReference bindingReference) {
+ this.itemReference = bindingReference.toItemReference();
+ return this;
+ }
+
+ public Builder setItemReference(KeepItemReference itemReference) {
+ this.itemReference = itemReference;
return this;
}
public KeepCheck build() {
- if (itemPattern == null) {
+ if (itemReference == null) {
throw new KeepEdgeException("KeepCheck must have an item pattern.");
}
- return new KeepCheck(metaInfo, kind, itemPattern);
+ bindings.verify(itemReference);
+ return new KeepCheck(metaInfo, kind, bindings, itemReference);
}
}
@@ -45,12 +57,18 @@
private final KeepEdgeMetaInfo metaInfo;
private final KeepCheckKind kind;
- private final KeepItemPattern itemPattern;
+ private final KeepBindings bindings;
+ private final KeepItemReference itemReference;
- private KeepCheck(KeepEdgeMetaInfo metaInfo, KeepCheckKind kind, KeepItemPattern itemPattern) {
+ private KeepCheck(
+ KeepEdgeMetaInfo metaInfo,
+ KeepCheckKind kind,
+ KeepBindings bindings,
+ KeepItemReference itemReference) {
this.metaInfo = metaInfo;
this.kind = kind;
- this.itemPattern = itemPattern;
+ this.bindings = bindings;
+ this.itemReference = itemReference;
}
@Override
@@ -66,12 +84,23 @@
return kind;
}
+ public KeepBindings getBindings() {
+ return bindings;
+ }
+
+ public KeepItemReference getItemReference() {
+ return itemReference;
+ }
+
public KeepItemPattern getItemPattern() {
- return itemPattern;
+ if (itemReference.isBindingReference()) {
+ return bindings.get(itemReference.asBindingReference()).getItem();
+ }
+ return itemReference.asItemPattern();
}
@Override
public String toString() {
- return "KeepCheck{kind=" + kind + ", item=" + itemPattern + "}";
+ return "KeepCheck{kind=" + kind + ", item=" + itemReference + "}";
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java
index 2ecadbc..a051b62 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemPattern.java
@@ -77,15 +77,6 @@
}
@Override
- public KeepItemReference toItemReference() {
- return toClassItemReference();
- }
-
- public final KeepClassItemReference toClassItemReference() {
- return KeepClassItemReference.fromClassItemPattern(this);
- }
-
- @Override
public Collection<KeepBindingReference> getBindingReferences() {
return Collections.emptyList();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java
index 34a97b6..b912471 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepClassItemReference.java
@@ -15,16 +15,6 @@
return new ClassBinding(bindingReference);
}
- public static KeepClassItemReference fromClassItemPattern(KeepClassItemPattern classItemPattern) {
- return new ClassItem(classItemPattern);
- }
-
- public static KeepClassItemReference fromClassNamePattern(
- KeepQualifiedClassNamePattern classNamePattern) {
- return new ClassItem(
- KeepClassItemPattern.builder().setClassNamePattern(classNamePattern).build());
- }
-
@Override
public final KeepClassItemReference asClassItemReference() {
return this;
@@ -85,50 +75,4 @@
return bindingReference.toString();
}
}
-
- private static class ClassItem extends KeepClassItemReference {
- private final KeepClassItemPattern classItemPattern;
-
- private ClassItem(KeepClassItemPattern classItemPattern) {
- assert classItemPattern != null;
- this.classItemPattern = classItemPattern;
- }
-
- @Override
- public KeepItemPattern asItemPattern() {
- return classItemPattern;
- }
-
- @Override
- public KeepClassItemPattern asClassItemPattern() {
- return classItemPattern;
- }
-
- @Override
- public Collection<KeepBindingReference> getBindingReferences() {
- return Collections.emptyList();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof ClassItem)) {
- return false;
- }
- ClassItem someItem = (ClassItem) o;
- return classItemPattern.equals(someItem.classItemPattern);
- }
-
- @Override
- public int hashCode() {
- return classItemPattern.hashCode();
- }
-
- @Override
- public String toString() {
- return classItemPattern.toString();
- }
- }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
index e9f7be2..b4aa28a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
@@ -23,13 +23,14 @@
private Builder() {}
- public Builder setItemReference(KeepItemReference item) {
- this.item = item;
+ public Builder setItemBindingReference(KeepBindingReference item) {
+ this.item = item.toItemReference();
return this;
}
- public Builder setItemPattern(KeepItemPattern itemPattern) {
- return setItemReference(itemPattern.toItemReference());
+ public Builder setItemReference(KeepItemReference item) {
+ this.item = item;
+ return this;
}
public KeepCondition build() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
index a455344..f11578e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
@@ -19,14 +19,10 @@
*/
public abstract class KeepItemPattern {
- public static KeepItemPattern anyClass() {
+ public static KeepClassItemPattern anyClass() {
return KeepClassItemPattern.any();
}
- public static KeepItemPattern anyMember() {
- return KeepMemberItemPattern.any();
- }
-
public boolean isClassItemPattern() {
return asClassItemPattern() != null;
}
@@ -45,8 +41,6 @@
public abstract Collection<KeepBindingReference> getBindingReferences();
- public abstract KeepItemReference toItemReference();
-
public final <T> T apply(
Function<KeepClassItemPattern, T> onClass, Function<KeepMemberItemPattern, T> onMember) {
if (isClassItemPattern()) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java
index 443ea55..a79578f 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberBindingReference.java
@@ -22,6 +22,10 @@
return KeepMemberItemReference.fromBindingReference(this);
}
+ public KeepMemberItemReference toMemberItemReference() {
+ return KeepMemberItemReference.fromBindingReference(this);
+ }
+
@Override
public String toString() {
return "member-ref(" + super.toString() + ")";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java
index 5184528..3fbe013 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemPattern.java
@@ -9,18 +9,13 @@
public class KeepMemberItemPattern extends KeepItemPattern {
- public static KeepMemberItemPattern any() {
- return builder().build();
- }
-
public static Builder builder() {
return new Builder();
}
public static class Builder {
- private KeepClassItemReference classReference =
- KeepClassItemPattern.any().toClassItemReference();
+ private KeepClassItemReference classReference = null;
private KeepMemberPattern memberPattern = KeepMemberPattern.allMembers();
private Builder() {}
@@ -30,6 +25,11 @@
.setMemberPattern(pattern.getMemberPattern());
}
+ public Builder setClassBindingReference(KeepClassBindingReference classReference) {
+ this.classReference = classReference.toClassItemReference();
+ return this;
+ }
+
public Builder setClassReference(KeepClassItemReference classReference) {
this.classReference = classReference;
return this;
@@ -41,6 +41,10 @@
}
public KeepMemberItemPattern build() {
+ if (classReference == null) {
+ throw new KeepEdgeException(
+ "Invalid attempt to build a member pattern without a class reference");
+ }
return new KeepMemberItemPattern(classReference, memberPattern);
}
}
@@ -61,11 +65,6 @@
return this;
}
- @Override
- public KeepItemReference toItemReference() {
- return KeepMemberItemReference.fromMemberItemPattern(this);
- }
-
public KeepClassItemReference getClassReference() {
return classReference;
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java
index d2ab55d..aa71f27 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberItemReference.java
@@ -11,10 +11,6 @@
return new MemberBinding(bindingReference);
}
- public static KeepMemberItemReference fromMemberItemPattern(KeepMemberItemPattern itemPattern) {
- return new MemberItem(itemPattern);
- }
-
@Override
public final KeepMemberItemReference asMemberItemReference() {
return this;
@@ -38,28 +34,4 @@
return bindingReference.toString();
}
}
-
- private static final class MemberItem extends KeepMemberItemReference {
-
- private final KeepMemberItemPattern itemPattern;
-
- public MemberItem(KeepMemberItemPattern itemPattern) {
- this.itemPattern = itemPattern;
- }
-
- @Override
- public KeepItemPattern asItemPattern() {
- return itemPattern;
- }
-
- @Override
- public KeepMemberItemPattern asMemberItemPattern() {
- return itemPattern;
- }
-
- @Override
- public String toString() {
- return itemPattern.toString();
- }
- }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
index 8367dfe..cbf039e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
@@ -14,13 +14,14 @@
private Builder() {}
- public Builder setItemReference(KeepItemReference item) {
- this.item = item;
+ public Builder setItemBindingReference(KeepBindingReference reference) {
+ this.item = reference.toItemReference();
return this;
}
- public Builder setItemPattern(KeepItemPattern itemPattern) {
- return setItemReference(itemPattern.toItemReference());
+ public Builder setItemReference(KeepItemReference item) {
+ this.item = item;
+ return this;
}
public Builder setConstraints(KeepConstraints constraints) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index dba2375..3a618c9 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -6,10 +6,12 @@
import com.android.tools.r8.keepanno.ast.KeepAttribute;
import com.android.tools.r8.keepanno.ast.KeepBindingReference;
import com.android.tools.r8.keepanno.ast.KeepBindings;
+import com.android.tools.r8.keepanno.ast.KeepBindings.Binding;
import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
import com.android.tools.r8.keepanno.ast.KeepCheck;
import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
+import com.android.tools.r8.keepanno.ast.KeepClassItemReference;
import com.android.tools.r8.keepanno.ast.KeepCondition;
import com.android.tools.r8.keepanno.ast.KeepConstraints;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
@@ -91,15 +93,24 @@
Map<KeepBindingSymbol, KeepMemberPattern> memberPatterns;
List<KeepBindingSymbol> targetMembers;
KeepBindings.Builder builder = KeepBindings.builder();
- KeepBindingSymbol symbol = builder.generateFreshSymbol("CLASS");
+ KeepBindingSymbol symbol;
if (itemPattern.isClassItemPattern()) {
+ symbol = builder.generateFreshSymbol("CLASS");
builder.addBinding(symbol, itemPattern);
memberPatterns = Collections.emptyMap();
targetMembers = Collections.emptyList();
} else {
KeepMemberItemPattern memberItemPattern = itemPattern.asMemberItemPattern();
- assert memberItemPattern.getClassReference().isClassItemPattern();
- builder.addBinding(symbol, memberItemPattern.getClassReference().asClassItemPattern());
+ KeepClassItemReference classReference = memberItemPattern.getClassReference();
+ if (classReference.isClassItemPattern()) {
+ symbol = builder.generateFreshSymbol("CLASS");
+ builder.addBinding(symbol, classReference.asClassItemPattern());
+ } else {
+ KeepBindingReference bindingReference = classReference.asBindingReference();
+ Binding binding = check.getBindings().get(bindingReference);
+ symbol = bindingReference.getName();
+ builder.addBinding(symbol, binding.getItem());
+ }
KeepMemberPattern memberPattern = memberItemPattern.getMemberPattern();
// This does not actually allocate a binding as the mapping is maintained in 'memberPatterns'.
KeepBindingSymbol memberSymbol = new KeepBindingSymbol("MEMBERS");
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
index 58c1ae3..af5a909 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
@@ -22,6 +22,7 @@
notModeledTypes.add("androidx.annotation.RecentlyNonNull");
notModeledTypes.add("android.annotation.Nullable");
notModeledTypes.add("android.annotation.NonNull");
+ notModeledTypes.add("android.annotation.FlaggedApi");
return notModeledTypes;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 7bafe1b..a6f39aa 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -200,15 +200,6 @@
return operands;
}
- public boolean hasOperandThatMatches(Predicate<Value> predicate) {
- for (Value operand : operands) {
- if (predicate.test(operand)) {
- return true;
- }
- }
- return false;
- }
-
public void removeOperand(int index) {
removeOperand(index, null, alwaysFalse());
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index 45848ad..80cf637 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -7,8 +7,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
@@ -19,8 +17,6 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
@@ -30,6 +26,7 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
@@ -38,7 +35,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
@@ -48,7 +44,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
-import java.util.function.Predicate;
public class DefaultFieldValueJoiner {
@@ -74,9 +69,6 @@
// Find all the fields where we need to determine if each field read is guaranteed to be
// dominated by a write.
Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = getFieldsOfInterest();
- if (fieldsOfInterest.isEmpty()) {
- return Collections.emptyMap();
- }
// If constructor inlining is disabled, then we focus on whether each instance initializer
// definitely assigns the given field before it is read. We do the same for final and static
@@ -102,12 +94,12 @@
// constructor inlining, we find all new-instance instructions (including subtype allocations)
// and check if the field is written on each allocation before it is possibly read.
analyzeNewInstanceInstructions(
- fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add, executorService);
+ fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add);
return updateFlowGraphs(fieldsWithLiveDefaultValue, executorService);
}
- protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
+ private Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = new IdentityHashMap<>();
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramField(
@@ -248,101 +240,13 @@
private void analyzeNewInstanceInstructions(
Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
- Consumer<ProgramField> liveDefaultValueConsumer,
- ExecutorService executorService)
- throws ExecutionException {
- // To simplify the analysis, we currently bail out for non-final classes.
- // TODO(b/296030319): Handle non-final classes.
- MapUtils.removeIf(
- nonFinalInstanceFields,
- (holderType, fields) -> {
- assert !fields.isEmpty();
- DexProgramClass holder = fields.iterator().next().getHolder();
- // If the class is kept it could be instantiated directly, in which case all default field
- // values could be live.
- if (appView.getKeepInfo(holder).isPinned(appView.options())) {
- fields.forEach(liveDefaultValueConsumer);
- return true;
- }
- if (holder.isFinal() || !appView.appInfo().isInstantiatedIndirectly(holder)) {
- // When the class is not explicitly marked final, the class could in principle have
- // injected subclasses if it is pinned. However, none of the fields are pinned, so we
- // should be allowed to reason about the field assignments in the program.
- assert fields.stream()
- .allMatch(
- field -> appView.getKeepInfo(field).isValuePropagationAllowed(appView, field));
- return false;
- }
- fields.forEach(liveDefaultValueConsumer);
- return true;
- });
-
- // We analyze all allocations of the classes that declare one of the given fields.
- ThreadUtils.processMethods(
- appView,
- method ->
- analyzeNewInstanceInstructionsInMethod(
- nonFinalInstanceFields, liveDefaultValueConsumer, method),
- appView.options().getThreadingModule(),
- executorService);
- }
-
- private void analyzeNewInstanceInstructionsInMethod(
- Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
- Consumer<ProgramField> liveDefaultValueConsumer,
- ProgramMethod method) {
- if (!maybeHasNewInstanceThatMatches(method, nonFinalInstanceFields::containsKey)) {
- return;
+ Consumer<ProgramField> liveDefaultValueConsumer) {
+ // Conservatively treat all fields as maybe read before written.
+ // TODO(b/296030319): Implement analysis by building IR for all methods that instantiate the
+ // relevant classes and analyzing the puts to the newly created instances.
+ for (ProgramField field : IterableUtils.flatten(nonFinalInstanceFields.values())) {
+ liveDefaultValueConsumer.accept(field);
}
- IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
- for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) {
- ProgramFieldSet fieldsOfInterest = nonFinalInstanceFields.get(newInstance.getType());
- if (fieldsOfInterest == null) {
- continue;
- }
- FieldReadBeforeWriteDfsAnalysis analysis =
- new FieldReadBeforeWriteDfsAnalysis(appView, code, fieldsOfInterest, newInstance) {
-
- @Override
- public AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field) {
- // Remove this field from the `fieldsOfInterest`, so that we do not spend more time
- // analyzing it.
- if (fieldsOfInterest.remove(field)) {
- liveDefaultValueConsumer.accept(field);
- }
- return AnalysisContinuation.abortIf(fieldsOfInterest.isEmpty());
- }
- };
- analysis.run();
- if (fieldsOfInterest.isEmpty()) {
- nonFinalInstanceFields.remove(newInstance.getType());
- }
- }
- }
-
- private boolean maybeHasNewInstanceThatMatches(
- ProgramMethod method, Predicate<DexType> predicate) {
- Code code = method.getDefinition().getCode();
- if (code == null || code.isSharedCodeObject()) {
- return false;
- }
- if (code.isLirCode()) {
- return code.asLirCode()
- .hasConstantItemThatMatches(
- constant -> constant instanceof DexType && predicate.test((DexType) constant));
- }
- assert appView.isCfByteCodePassThrough(method);
- assert code.isCfCode();
- return method.registerCodeReferencesWithResult(
- new DefaultUseRegistryWithResult<>(appView, method, false) {
-
- @Override
- public void registerNewInstance(DexType type) {
- if (predicate.test(type)) {
- setResult(true);
- }
- }
- });
}
private Map<FlowGraph, Deque<FlowGraphNode>> updateFlowGraphs(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
deleted file mode 100644
index 1fdfb27..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
+++ /dev/null
@@ -1,387 +0,0 @@
-// Copyright (c) 2024, 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.optimize.argumentpropagation.propagation;
-
-import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
-import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
-import static com.android.tools.r8.ir.code.Opcodes.GOTO;
-import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.RETURN;
-import static com.android.tools.r8.ir.code.Opcodes.THROW;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockInstructionIterator;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.ConstString;
-import com.android.tools.r8.ir.code.Goto;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Phi;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Throw;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.ProgramFieldSet;
-
-/**
- * Analysis that is given an allocation site (a {@link NewInstance} instruction) and a set of fields
- * that belong to that newly allocated instance.
- *
- * <p>The analysis computes the subset of the given fields which are maybe read before they are
- * written. The default value of these fields is potentially read, whereas the default value of the
- * complement field set are guaranteed to never be read.
- *
- * <p>The analysis works by exploring all possible paths starting from the given allocation site to
- * the normal and the exceptional exits of the method, keeping track of which fields are definitely
- * written before they are read and which fields have maybe been read.
- */
-public abstract class FieldReadBeforeWriteDfsAnalysis extends FieldReadBeforeWriteDfsAnalysisState {
-
- private final AppView<AppInfoWithLiveness> appView;
- private final IRCode code;
- private final DexItemFactory dexItemFactory;
- // The set of fields to consider. Note that this is a concurrent set and that the caller may
- // concurrently remove fields from the set. This may happen if we concurrently find a
- // read-before-write of one of the fields.
- private final ProgramFieldSet fields;
- private final WorkList<WorkItem> worklist = WorkList.newIdentityWorkList();
-
- private final FieldReadBeforeWriteDfsAnalysis self = this;
-
- public FieldReadBeforeWriteDfsAnalysis(
- AppView<AppInfoWithLiveness> appView,
- IRCode code,
- ProgramFieldSet fields,
- NewInstance newInstance) {
- super(newInstance);
- this.appView = appView;
- this.code = code;
- this.dexItemFactory = appView.dexItemFactory();
- this.fields = fields;
- }
-
- // Returns ABORT if all fields of interest are now maybe-read-before-written.
- // Otherwise returns CONTINUE.
- public abstract AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field);
-
- public void run() {
- worklist.addIfNotSeen(new InitialWorkItem());
- worklist.run(WorkItem::process);
- }
-
- public enum AnalysisContinuation {
- // Signals to abort the analysis completely (i.e., to break out of the DFS). This is used when
- // we've reported all fields as being maybe read before written.
- ABORT,
- // Signals to continue the current DFS.
- CONTINUE,
- // Signals that all fields have been written before they are read on the current program path,
- // meaning that the algorithm does not need to explore any further. The algorithm should instead
- // backtrack and explore any other program paths.
- BREAK;
-
- static AnalysisContinuation abortIf(boolean condition) {
- if (condition) {
- return ABORT;
- }
- return CONTINUE;
- }
-
- boolean isAbort() {
- return this == ABORT;
- }
-
- boolean isAbortOrContinue() {
- return isAbort() || isContinue();
- }
-
- boolean isBreak() {
- return this == BREAK;
- }
-
- boolean isContinue() {
- return this == CONTINUE;
- }
-
- TraversalContinuation<?, ?> toTraversalContinuation() {
- assert isAbortOrContinue();
- return TraversalContinuation.breakIf(isAbort());
- }
- }
-
- abstract class WorkItem {
-
- abstract TraversalContinuation<?, ?> process();
-
- void applyPhis(BasicBlock block) {
- // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
- // backtracking of this (i.e., we should remove the out value from the object aliases again).
- for (Phi phi : block.getPhis()) {
- if (phi.hasOperandThatMatches(self::isMaybeInstance)) {
- addInstanceAlias(phi);
- }
- }
- }
-
- AnalysisContinuation applyInstructions(BasicBlockInstructionIterator instructionIterator) {
- while (instructionIterator.hasNext()) {
- Instruction instruction = instructionIterator.next();
- assert !instruction.hasOutValue() || !isMaybeInstance(instruction.outValue());
- AnalysisContinuation continuation;
- // TODO(b/339210038): Extend this to many other instructions, such as ConstClass,
- // InstanceOf, *Binop, etc.
- switch (instruction.opcode()) {
- case ASSUME:
- continuation = applyAssumeInstruction(instruction.asAssume());
- break;
- case CHECK_CAST:
- continuation = applyCheckCastInstruction(instruction.asCheckCast());
- break;
- case CONST_NUMBER:
- continuation = applyConstNumber(instruction.asConstNumber());
- break;
- case CONST_STRING:
- continuation = applyConstString(instruction.asConstString());
- break;
- case GOTO:
- continuation = applyGotoInstruction(instruction.asGoto());
- break;
- case INSTANCE_PUT:
- continuation = applyInstancePut(instruction.asInstancePut());
- break;
- case INVOKE_DIRECT:
- case INVOKE_INTERFACE:
- case INVOKE_STATIC:
- case INVOKE_SUPER:
- case INVOKE_VIRTUAL:
- continuation = applyInvokeMethodInstruction(instruction.asInvokeMethod());
- break;
- case RETURN:
- continuation = applyReturnInstruction(instruction.asReturn());
- break;
- case THROW:
- continuation = applyThrowInstruction(instruction.asThrow());
- break;
- default:
- continuation = applyUnhandledInstruction();
- break;
- }
- if (continuation.isAbort()) {
- return continuation;
- }
- if (continuation.isBreak()) {
- break;
- }
- }
- return AnalysisContinuation.CONTINUE;
- }
-
- // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
- // backtracking of this (i.e., we should remove the out value from the object aliases again).
- private AnalysisContinuation applyAssumeInstruction(Assume assume) {
- if (isMaybeInstance(assume.src())) {
- addInstanceAlias(assume.outValue());
- }
- return AnalysisContinuation.CONTINUE;
- }
-
- // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
- // backtracking of this (i.e., we should remove the out value from the object aliases again).
- private AnalysisContinuation applyCheckCastInstruction(CheckCast checkCast) {
- if (isMaybeInstance(checkCast.object())) {
- addInstanceAlias(checkCast.outValue());
- }
- // If the instance has escaped to the heap and this check-cast instruction throws, then it is
- // possible that the instance is retrieved from the heap and all fields are read.
- return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
- }
-
- private AnalysisContinuation applyConstNumber(ConstNumber unusedConstNumber) {
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyConstString(ConstString unusedConstString) {
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyGotoInstruction(Goto gotoInstruction) {
- BasicBlock targetBlock = gotoInstruction.getTarget();
- if (isBlockOnStack(targetBlock)) {
- // Bail out in case of cycles.
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- } else {
- // Continue exploration into the successor block.
- worklist.addIgnoringSeenSet(new ProcessBlockWorkItem(targetBlock));
- return AnalysisContinuation.CONTINUE;
- }
- }
-
- private AnalysisContinuation applyInstancePut(InstancePut instancePut) {
- // If the instance has escaped and this instance-put instruction can throw, then the program
- // can get the instance from the heap and read any field. Give up in this case.
- if (isEscaped() && instancePut.instructionInstanceCanThrow(appView, code.context())) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- // Record if this is a definite write to one of the fields of interest.
- if (isDefinitelyInstance(instancePut.object())) {
- ProgramField resolvedField =
- instancePut.resolveField(appView, code.context()).getProgramField();
- if (resolvedField != null && fields.contains(resolvedField)) {
- addWrittenBeforeRead(resolvedField);
- }
-
- // If all fields of interest are written before read, then stop the exploration of the
- // current program path (but continue to explore any program paths from previous unexplored
- // branches).
- if (fields.allMatch(self::isWrittenBeforeRead)) {
- return AnalysisContinuation.BREAK;
- }
- }
-
- // Record if the instance has escaped as a result of this instance-put.
- if (!isEscaped() && isMaybeInstance(instancePut.value())) {
- setEscaped(instancePut);
- }
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyInvokeMethodInstruction(InvokeMethod invoke) {
- // Allow calls to java.lang.Object.<init>().
- // TODO(b/339210038): Generalize this to other constructors.
- if (invoke.isInvokeConstructor(dexItemFactory)
- && isDefinitelyInstance(invoke.getFirstArgument())) {
- DexClassAndMethod resolvedMethod =
- invoke.resolveMethod(appView, code.context()).getResolutionPair();
- if (resolvedMethod != null
- && resolvedMethod
- .getReference()
- .isIdenticalTo(dexItemFactory.objectMembers.constructor)) {
- return AnalysisContinuation.CONTINUE;
- }
- }
-
- // Conservatively treat calls as reading any field if the receiver has escaped or is escaping.
- if (!isEscaped()
- && invoke.hasInValueThatMatches(self::isMaybeInstance)
- && invoke.instructionMayHaveSideEffects(appView, code.context())) {
- setEscaped(invoke);
- }
-
- if (isEscaped()) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- // Otherwise, this is a call to a method where none of the arguments is an alias of the
- // instance, and the instance has not escaped. Therefore, this call cannot read any of fields
- // from the instance.
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyReturnInstruction(Return unusedReturnInstruction) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- private AnalysisContinuation applyThrowInstruction(Throw unusedThrowInstruction) {
- return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
- }
-
- private AnalysisContinuation applyUnhandledInstruction() {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWritten() {
- for (ProgramField field : fields) {
- if (!isWrittenBeforeRead(field)) {
- AnalysisContinuation continuation = acceptFieldMaybeReadBeforeWrite(field);
- assert continuation.isAbortOrContinue();
- if (continuation.isAbort()) {
- return continuation;
- }
- }
- }
- // At this point we could also CONTINUE, but we check if the fields of interest have become
- // empty as a result of concurrent modification.
- return AnalysisContinuation.abortIf(fields.isEmpty());
- }
-
- AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped() {
- if (isEscaped()) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
- return AnalysisContinuation.CONTINUE;
- }
- }
-
- class InitialWorkItem extends WorkItem {
-
- @Override
- TraversalContinuation<?, ?> process() {
- // We start the analysis from the unique constructor invoke instead of from the NewInstance
- // instruction, since no instructions before the constructor call can read any fields from the
- // uninitialized this.
- // TODO(b/339210038): In principle it may be possible for the NewInstance value to flow into a
- // phi before the unique constructor invoke. If this happens we would not record the phi as
- // an alias when starting the analysis from the invoke-direct.
- InvokeDirect uniqueConstructorInvoke =
- getNewInstance().getUniqueConstructorInvoke(dexItemFactory);
- if (uniqueConstructorInvoke == null) {
- return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
- }
- BasicBlock block = uniqueConstructorInvoke.getBlock();
- // TODO(b/339210038): Maybe allow exceptional control flow.
- if (block.hasCatchHandlers()) {
- return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
- }
- addBlockToStack(block);
- addInstanceAlias(getNewInstance().outValue());
- BasicBlockInstructionIterator instructionIterator = block.iterator(uniqueConstructorInvoke);
- // Start the analysis from the invoke-direct instruction. This is important if we can tell
- // that the constructor definitely writes some fields.
- instructionIterator.previous();
- return applyInstructions(instructionIterator).toTraversalContinuation();
- }
- }
-
- class ProcessBlockWorkItem extends WorkItem {
-
- private final BasicBlock block;
-
- ProcessBlockWorkItem(BasicBlock block) {
- this.block = block;
- }
-
- @Override
- TraversalContinuation<?, ?> process() {
- // TODO(b/339210038): Maybe allow exceptional control flow.
- if (block.hasCatchHandlers()) {
- return TraversalContinuation.breakIf(
- markRemainingFieldsAsMaybeReadBeforeWritten().isAbort());
- }
- addBlockToStack(block);
- applyPhis(block);
- AnalysisContinuation continuation = applyInstructions(block.iterator());
- assert continuation.isAbortOrContinue();
- return TraversalContinuation.breakIf(continuation.isAbort());
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
deleted file mode 100644
index 7e24c03..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2024, 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.optimize.argumentpropagation.propagation;
-
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.collections.ProgramFieldSet;
-import com.google.common.collect.Sets;
-import java.util.Set;
-
-/**
- * The state we track during the field-maybe-read-before-write/field-never-read-before-written
- * analysis.
- */
-public class FieldReadBeforeWriteDfsAnalysisState {
-
- // The current allocation we are analyzing.
- private final NewInstance newInstance;
-
- // The first instruction on the current program path starting from the `newInstance` instruction
- // from which the `newInstance` value escapes.
- private Instruction escape = null;
-
- // The set of values that *may* be aliases of the `newInstance` value.
- private final Set<Value> instanceAliases = Sets.newIdentityHashSet();
-
- // The set of blocks on the current program path.
- private final Set<BasicBlock> stack = Sets.newIdentityHashSet();
-
- // The set of fields that are guaranteed to be written before they are read on the current program
- // path.
- private final ProgramFieldSet writtenBeforeRead = ProgramFieldSet.create();
-
- FieldReadBeforeWriteDfsAnalysisState(NewInstance newInstance) {
- this.newInstance = newInstance;
- }
-
- void addInstanceAlias(Value instanceAlias) {
- boolean changed = instanceAliases.add(instanceAlias);
- assert changed;
- }
-
- void addBlockToStack(BasicBlock block) {
- boolean changed = stack.add(block);
- assert changed;
- }
-
- void addWrittenBeforeRead(ProgramField field) {
- writtenBeforeRead.add(field);
- }
-
- NewInstance getNewInstance() {
- return newInstance;
- }
-
- boolean isBlockOnStack(BasicBlock block) {
- return stack.contains(block);
- }
-
- boolean isEscaped() {
- return escape != null;
- }
-
- boolean isDefinitelyInstance(Value value) {
- return value.getAliasedValue() == newInstance.outValue();
- }
-
- boolean isMaybeInstance(Value value) {
- return instanceAliases.contains(value);
- }
-
- boolean isWrittenBeforeRead(ProgramField field) {
- return writtenBeforeRead.contains(field);
- }
-
- void setEscaped(Instruction escape) {
- assert !isEscaped();
- this.escape = escape;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index 81e3424..d62328d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -46,7 +46,7 @@
final AppView<AppInfoWithLiveness> appView;
final Set<DexProgramClass> classesWithSingleCallerInlinedInstanceInitializers;
final IRConverter converter;
- protected final FieldStateCollection fieldStates;
+ final FieldStateCollection fieldStates;
final MethodStateCollectionByReference methodStates;
public InFlowPropagator(
@@ -113,15 +113,12 @@
private Map<FlowGraph, Deque<FlowGraphNode>> includeDefaultValuesInFieldStates(
List<FlowGraph> flowGraphs, ExecutorService executorService) throws ExecutionException {
- DefaultFieldValueJoiner joiner = createDefaultFieldValueJoiner(flowGraphs);
+ DefaultFieldValueJoiner joiner =
+ new DefaultFieldValueJoiner(
+ appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
return joiner.joinDefaultFieldValuesForFieldsWithReadBeforeWrite(executorService);
}
- protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(List<FlowGraph> flowGraphs) {
- return new DefaultFieldValueJoiner(
- appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
- }
-
private void processFlowGraphs(List<FlowGraph> flowGraphs, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
index 22ed377..8d7498c 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -34,8 +33,6 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.DefaultFieldValueJoiner;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.FlowGraph;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.IterableUtils;
@@ -45,8 +42,6 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -97,22 +92,7 @@
InFlowPropagator inFlowPropagator =
new InFlowPropagator(
- appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates()) {
-
- @Override
- protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(
- List<FlowGraph> flowGraphs) {
- return new DefaultFieldValueJoiner(appView, null, fieldStates, flowGraphs) {
-
- @Override
- protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
- // We do not rely on the optimization of any fields in the Composable optimization
- // pass.
- return Collections.emptyMap();
- }
- };
- }
- };
+ appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates());
inFlowPropagator.run(executorService);
ArgumentPropagatorOptimizationInfoPopulator optimizationInfoPopulator =
diff --git a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
index 19a0d8b..fb47d43 100644
--- a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.keepanno.ast.KeepAnnotationPattern;
import com.android.tools.r8.keepanno.ast.KeepBindingReference;
+import com.android.tools.r8.keepanno.ast.KeepBindings;
import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
import com.android.tools.r8.keepanno.ast.KeepCheck;
import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
@@ -34,6 +35,7 @@
import com.android.tools.r8.keepanno.ast.KeepConstraintVisitor;
import com.android.tools.r8.keepanno.ast.KeepConstraints;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepItemReference;
import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
@@ -355,8 +357,16 @@
}
private void continueWithNoClass(int classIndex) {
+ continueWithNoClassClearingMembers(classIndex, 0, null);
+ }
+
+ private void continueWithNoClassClearingMembers(
+ int classIndex, int memberInHolderIndex, IntList memberIndexTranslation) {
if (schema.isOptionalClass(classIndex)) {
assignment.setClass(classIndex, null);
+ for (int i = 0; i < memberInHolderIndex; i++) {
+ assignment.setMember(memberIndexTranslation.getInt(i), null);
+ }
findMatchingClass(classIndex + 1);
}
}
@@ -404,9 +414,9 @@
holder.forEachProgramMethodMatching(
m -> predicates.matchesMethod(m, methodPattern), continueWithMember));
if (didContinue.isFalse()) {
- // No match for the member pattern existed, continue with empty member.
- continueWithNoMember(
- memberIndex, memberInHolderIndex + 1, memberIndexTranslation, holder, nextClassIndex);
+ // No match for the member pattern existed, continue with next class.
+ continueWithNoClassClearingMembers(
+ nextClassIndex - 1, memberInHolderIndex, memberIndexTranslation);
}
}
@@ -429,18 +439,6 @@
definition.getContextClass(),
nextClassIndex);
}
-
- private void continueWithNoMember(
- int memberIndex,
- int nextMemberInHolderIndex,
- IntList memberIndexTranslation,
- DexProgramClass holder,
- int nextClassIndex) {
- if (schema.isOptionalMember(memberIndex, nextClassIndex - 1)) {
- assignment.setMember(memberIndex, null);
- findMatchingMember(nextMemberInHolderIndex, memberIndexTranslation, holder, nextClassIndex);
- }
- }
}
/**
@@ -456,7 +454,6 @@
final List<KeepClassItemPattern> classes = new ArrayList<>();
final List<KeepMemberItemPattern> members = new ArrayList<>();
final List<IntList> classMembers = new ArrayList<>();
- final IntList boundClasses = new IntArrayList();
final IntList preconditions = new IntArrayList();
final IntList consequences = new IntArrayList();
final List<KeepConstraints> constraints = new ArrayList<>();
@@ -480,16 +477,12 @@
}
private KeepItemPattern getItemForBinding(KeepBindingSymbol symbol) {
- assert declaration.isKeepEdge();
- return declaration.asKeepEdge().getBindings().get(symbol).getItem();
+ KeepBindings bindings = declaration.apply(KeepEdge::getBindings, KeepCheck::getBindings);
+ return bindings.get(symbol).getItem();
}
public boolean isOptionalClass(int classIndex) {
- return classIndex >= preconditionClassesCount && !boundClasses.contains(classIndex);
- }
-
- public boolean isOptionalMember(int memberIndex, int classIndex) {
- return memberIndex >= preconditionMembersCount && isOptionalClass(classIndex);
+ return classIndex >= preconditionClassesCount;
}
public static boolean isClassKeyReference(int keyRef) {
@@ -523,14 +516,7 @@
private int defineBindingReference(KeepBindingReference reference) {
return symbolToKey.computeIfAbsent(
- reference.getName(),
- symbol -> {
- int bindingId = defineItemPattern(getItemForBinding(symbol));
- if (isClassKeyReference(bindingId)) {
- boundClasses.add(bindingId);
- }
- return bindingId;
- });
+ reference.getName(), symbol -> defineItemPattern(getItemForBinding(symbol)));
}
private int defineItemPattern(KeepItemPattern item) {
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
index 891593a..c778df3 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
@@ -8,15 +8,14 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
-import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
import java.util.stream.Stream;
public class ProgramFieldSet implements Iterable<ProgramField> {
@@ -55,10 +54,6 @@
backing.putAll(fields.backing);
}
- public boolean allMatch(Predicate<? super ProgramField> predicate) {
- return Iterables.all(this, predicate);
- }
-
public boolean createAndAdd(DexProgramClass clazz, DexEncodedField definition) {
return add(new ProgramField(clazz, definition));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index ef3db54..1244c0f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -110,9 +110,9 @@
methodReferences.forEach(field -> numberOfMethods.increment())));
});
// These numbers will change when updating api-versions.xml
- assertEquals(5904, parsedApiClasses.size());
- assertEquals(30073, numberOfFields.get());
- assertEquals(46091, numberOfMethods.get());
+ assertEquals(5971, parsedApiClasses.size());
+ assertEquals(30341, numberOfFields.get());
+ assertEquals(46572, numberOfMethods.get());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
index fa46f4d..0d912b3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.classmerging.horizontal;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -36,9 +35,7 @@
.assertSuccessWithOutputLines("foo", "hello", "5", "foobar")
.inspect(
codeInspector -> {
- assertThat(
- codeInspector.clazz(C.class),
- isAbsentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(codeInspector.clazz(C.class), isPresent());
assertThat(codeInspector.clazz(D.class), isPresent());
assertThat(codeInspector.clazz(E.class), isPresent());
});
diff --git a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
index e6ee76a..d988c22 100644
--- a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
+++ b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
@@ -58,7 +58,7 @@
.setMinApi(AndroidApiLevel.K)
.compile()
.inspect(
- inspector -> assertEquals(backend.isDex() ? 1077 : 4, inspector.allClasses().size()));
+ inspector -> assertEquals(backend.isDex() ? 1091 : 4, inspector.allClasses().size()));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
index 142d565..f952fa9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.membervaluepropagation;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
@@ -42,11 +42,17 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isAbsent());
+ // TODO(b/339210038): Should always be absent.
+ assertThat(
+ aClassSubject,
+ isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
assertThat(mainMethodSubject, isPresent());
- assertTrue(mainMethodSubject.streamInstructions().anyMatch(i -> i.isConstNumber(42)));
+ // TODO(b/339210038): Should always contain 42.
+ assertEquals(
+ parameters.canInitNewInstanceUsingSuperclassConstructor(),
+ mainMethodSubject.streamInstructions().noneMatch(i -> i.isConstNumber(42)));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("42");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java
deleted file mode 100644
index 358227d..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2024, 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.ir.optimize.membervaluepropagation;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-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 LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest extends TestBase {
-
- @Parameter(0)
- public TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- @Test
- public void test() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(Main.class)
- .addKeepClassAndDefaultConstructor(BooleanBox.class)
- .enableInliningAnnotations()
- .setMinApi(parameters)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("false");
- }
-
- static class Main {
-
- public static void main(String[] args) throws Exception {
- Class<?> clazz = System.currentTimeMillis() > 0 ? BooleanBox.class : Object.class;
- BooleanBox box = (BooleanBox) clazz.getDeclaredConstructor().newInstance();
- System.out.println(box.value);
- box.set();
- }
- }
-
- static class BooleanBox {
-
- boolean value;
-
- BooleanBox() {}
-
- @NeverInline
- void set() {
- value = true;
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
index 862fbc9..fca4473 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -18,20 +18,21 @@
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 FieldInitializedByConstantArgumentMultipleConstructorsTest extends TestBase {
- @Parameter(0)
- public TestParameters parameters;
+ private final TestParameters parameters;
- @Parameters(name = "{0}")
+ @Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ public FieldInitializedByConstantArgumentMultipleConstructorsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -50,7 +51,10 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
- assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), isAbsent());
+ // TODO(b/280275115): Constructor inlining regresses instance field value analysis.
+ assertThat(
+ testClassSubject.uniqueMethodWithOriginalName("dead"),
+ isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMainViaBindingTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMainViaBindingTest.java
new file mode 100644
index 0000000..9246d9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepMainViaBindingTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.keepanno.annotations.KeepBinding;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class KeepMainViaBindingTest extends KeepAnnoTestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world!");
+
+ @Parameter public KeepAnnoParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<KeepAnnoParameters> data() {
+ return createParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForKeepAnno(parameters)
+ .addProgramClasses(getInputClasses())
+ .setExcludedOuterClass(getClass())
+ .run(TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class);
+ }
+
+ static class TestClass {
+
+ @KeepEdge(
+ bindings =
+ @KeepBinding(
+ bindingName = "MainMethod",
+ classConstant = TestClass.class,
+ methodName = "main"),
+ consequences =
+ @KeepTarget(kind = KeepItemKind.CLASS_AND_METHODS, memberFromBinding = "MainMethod"))
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/UsedByReflectionWithNonMatchingMemberPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/UsedByReflectionWithNonMatchingMemberPatternsTest.java
new file mode 100644
index 0000000..a77ec25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/UsedByReflectionWithNonMatchingMemberPatternsTest.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class UsedByReflectionWithNonMatchingMemberPatternsTest extends KeepAnnoTestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world!");
+
+ @Parameter public KeepAnnoParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<KeepAnnoParameters> data() {
+ return createParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForKeepAnno(parameters)
+ .addProgramClasses(getInputClasses())
+ .setExcludedOuterClass(getClass())
+ // The fields part of the UsedByReflection will be unused.
+ .allowUnusedProguardConfigurationRules()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class);
+ }
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_FIELDS)
+ static class TestClass {
+
+ @UsedByReflection
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index d2deccb..e0727be 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -40,15 +40,21 @@
@Test
public void testKeepAll() {
+ BindingsHelper helper = new BindingsHelper();
KeepEdge edge =
KeepEdge.builder()
.setConsequences(
KeepConsequences.builder()
.addTarget(
- KeepTarget.builder().setItemPattern(KeepItemPattern.anyClass()).build())
+ KeepTarget.builder()
+ .setItemBindingReference(helper.freshAnyClass())
+ .build())
.addTarget(
- KeepTarget.builder().setItemPattern(KeepItemPattern.anyMember()).build())
+ KeepTarget.builder()
+ .setItemBindingReference(helper.freshAnyMember())
+ .build())
.build())
+ .setBindings(helper.build())
.build();
assertEquals(
StringUtils.unixLines(
@@ -59,6 +65,7 @@
@Test
public void testSoftPinViaConstraints() {
+ BindingsHelper helper = new BindingsHelper();
KeepConstraints constraints =
KeepConstraints.builder()
.add(KeepConstraint.classInstantiate())
@@ -72,15 +79,16 @@
KeepConsequences.builder()
.addTarget(
KeepTarget.builder()
- .setItemPattern(KeepItemPattern.anyClass())
+ .setItemBindingReference(helper.freshAnyClass())
.setConstraints(constraints)
.build())
.addTarget(
KeepTarget.builder()
- .setItemPattern(KeepItemPattern.anyMember())
+ .setItemBindingReference(helper.freshAnyMember())
.setConstraints(constraints)
.build())
.build())
+ .setBindings(helper.build())
.build();
// Pinning just the use constraints points will issue the full inverse of the known options,
// e.g., 'allowaccessmodification'.
@@ -96,9 +104,11 @@
}
@Test
public void testKeepClass() {
- KeepTarget target = target(classItem(CLASS));
+ BindingsHelper helper = new BindingsHelper();
+ KeepTarget target = target(classItem(CLASS), helper);
KeepConsequences consequences = KeepConsequences.builder().addTarget(target).build();
- KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
+ KeepEdge edge =
+ KeepEdge.builder().setConsequences(consequences).setBindings(helper.build()).build();
assertEquals(
StringUtils.unixLines(
"-keep,allowaccessmodification class " + CLASS + " { void finalize(); }"),
@@ -107,20 +117,23 @@
@Test
public void testKeepInitIfReferenced() {
+ BindingsHelper helper = new BindingsHelper();
KeepEdge edge =
KeepEdge.builder()
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(KeepCondition.builder().setItemPattern(classItem(CLASS)).build())
+ .addCondition(condition(classItem(CLASS), helper))
.build())
.setConsequences(
KeepConsequences.builder()
.addTarget(
target(
- buildMemberItem(CLASS)
+ buildMemberItem(CLASS, helper)
.setMemberPattern(defaultInitializerPattern())
- .build()))
+ .build(),
+ helper))
.build())
+ .setBindings(helper.build())
.build();
assertEquals(
StringUtils.unixLines(
@@ -130,13 +143,19 @@
@Test
public void testKeepInstanceIfReferenced() {
+ BindingsHelper helper = new BindingsHelper();
KeepEdge edge =
KeepEdge.builder()
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(KeepCondition.builder().setItemPattern(classItem(CLASS)).build())
+ .addCondition(
+ KeepCondition.builder()
+ .setItemBindingReference(helper.freshClassBinding(classItem(CLASS)))
+ .build())
.build())
- .setConsequences(KeepConsequences.builder().addTarget(target(classItem(CLASS))).build())
+ .setConsequences(
+ KeepConsequences.builder().addTarget(target(classItem(CLASS), helper)).build())
+ .setBindings(helper.build())
.build();
assertEquals(
StringUtils.unixLines(
@@ -150,21 +169,24 @@
@Test
public void testKeepInstanceAndInitIfReferenced() {
+ BindingsHelper helper = new BindingsHelper();
KeepEdge edge =
KeepEdge.builder()
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(KeepCondition.builder().setItemPattern(classItem(CLASS)).build())
+ .addCondition(condition(classItem(CLASS), helper))
.build())
.setConsequences(
KeepConsequences.builder()
- .addTarget(target(classItem(CLASS)))
+ .addTarget(target(classItem(CLASS), helper))
.addTarget(
target(
- buildMemberItem(CLASS)
+ buildMemberItem(CLASS, helper)
.setMemberPattern(defaultInitializerPattern())
- .build()))
+ .build(),
+ helper))
.build())
+ .setBindings(helper.build())
.build();
assertEquals(
StringUtils.unixLines(
@@ -179,28 +201,26 @@
@Test
public void testKeepInstanceAndInitIfReferencedWithBinding() {
- KeepBindings.Builder bindings = KeepBindings.builder();
- KeepBindingSymbol classSymbol = bindings.create("CLASS");
+ BindingsHelper helper = new BindingsHelper();
+ KeepClassBindingReference clazz = helper.freshClassBinding(classItem(CLASS));
KeepEdge edge =
KeepEdge.builder()
- .setBindings(bindings.addBinding(classSymbol, classItem(CLASS)).build())
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(
- KeepCondition.builder()
- .setItemReference(classItemBinding(classSymbol))
- .build())
+ .addCondition(KeepCondition.builder().setItemBindingReference(clazz).build())
.build())
.setConsequences(
KeepConsequences.builder()
- .addTarget(target(classItemBinding(classSymbol)))
+ .addTarget(target(clazz))
.addTarget(
target(
KeepMemberItemPattern.builder()
- .setClassReference(classItemBinding(classSymbol))
+ .setClassBindingReference(clazz)
.setMemberPattern(defaultInitializerPattern())
- .build()))
+ .build(),
+ helper))
.build())
+ .setBindings(helper.build())
.build();
assertEquals(
StringUtils.unixLines(
@@ -212,19 +232,27 @@
extract(edge));
}
- private KeepClassItemReference classItemBinding(KeepBindingSymbol bindingName) {
- return KeepBindingReference.forClass(bindingName).toClassItemReference();
+ private KeepClassBindingReference classItemBinding(KeepBindingSymbol bindingName) {
+ return KeepBindingReference.forClass(bindingName);
}
- private KeepTarget target(KeepItemPattern item) {
- return KeepTarget.builder().setItemPattern(item).build();
+ private KeepTarget target(KeepItemPattern item, BindingsHelper helper) {
+ return target(helper.freshItemBinding(item));
}
- private KeepTarget target(KeepItemReference item) {
- return KeepTarget.builder().setItemReference(item).build();
+ private KeepTarget target(KeepBindingReference item) {
+ return KeepTarget.builder().setItemBindingReference(item).build();
}
- private KeepItemPattern classItem(String typeName) {
+ private KeepCondition condition(KeepItemPattern item, BindingsHelper helper) {
+ return condition(helper.freshItemBinding(item));
+ }
+
+ private KeepCondition condition(KeepBindingReference item) {
+ return KeepCondition.builder().setItemBindingReference(item).build();
+ }
+
+ private KeepClassItemPattern classItem(String typeName) {
return buildClassItem(typeName).build();
}
@@ -233,9 +261,14 @@
.setClassNamePattern(KeepQualifiedClassNamePattern.exact(typeName));
}
- private KeepMemberItemPattern.Builder buildMemberItem(String typeName) {
+ private KeepMemberItemPattern.Builder buildMemberItem(String holderType, BindingsHelper helper) {
+ return buildMemberItem(helper.freshClassBinding(buildClassItem(holderType).build()));
+ }
+
+ private static KeepMemberItemPattern.Builder buildMemberItem(KeepClassBindingReference holder) {
return KeepMemberItemPattern.builder()
- .setClassReference(buildClassItem(typeName).build().toClassItemReference());
+ .setClassBindingReference(holder)
+ .setMemberPattern(KeepMemberPattern.allMembers());
}
private KeepMemberPattern defaultInitializerPattern() {
@@ -245,4 +278,37 @@
.setReturnTypeVoid()
.build();
}
+
+ private static class BindingsHelper {
+ private final KeepBindings.Builder builder = KeepBindings.builder();
+
+ public KeepClassBindingReference freshClassBinding(KeepClassItemPattern pattern) {
+ KeepBindingSymbol symbol = builder.generateFreshSymbol("CLASS");
+ builder.addBinding(symbol, pattern);
+ return KeepClassBindingReference.forClass(symbol);
+ }
+
+ public KeepMemberBindingReference freshMemberBinding(KeepMemberItemPattern pattern) {
+ KeepBindingSymbol symbol = builder.generateFreshSymbol("MEMBER");
+ builder.addBinding(symbol, pattern);
+ return KeepMemberBindingReference.forMember(symbol);
+ }
+
+ public KeepBindingReference freshItemBinding(KeepItemPattern pattern) {
+ return pattern.apply(this::freshClassBinding, this::freshMemberBinding);
+ }
+
+ public KeepClassBindingReference freshAnyClass() {
+ return freshClassBinding(KeepItemPattern.anyClass());
+ }
+
+ public KeepMemberBindingReference freshAnyMember() {
+ return freshMemberBinding(
+ buildMemberItem(freshClassBinding(KeepItemPattern.anyClass())).build());
+ }
+
+ public KeepBindings build() {
+ return builder.build();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
index 933d9e2..32e72b6 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
@@ -91,7 +91,7 @@
@Override
protected void configureR8(R8FullTestBuilder builder) {
boolean referencesNotNull =
- !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_6_0)
+ !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_4_20)
&& !kotlinStdlibAsLibrary;
builder.applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull"));
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
index 44d3eca..6f190cd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
@@ -62,7 +62,7 @@
@Override
protected void configureR8(R8FullTestBuilder builder) {
boolean referencesNotNull =
- !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_6_0)
+ !kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_4_20)
&& !kotlinStdlibAsLibrary
&& !useJvmAssertions;
builder
diff --git a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
index c548593..8b7b4dd 100644
--- a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_9_21;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MAX_SUPPORTED_VERSION;
import static com.android.tools.r8.ToolHelper.isWindows;
import static com.google.common.io.Files.getNameWithoutExtension;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -26,7 +26,6 @@
import com.google.common.hash.Hasher;
import java.io.File;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -36,7 +35,9 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
+import java.util.jar.Manifest;
import java.util.stream.Collectors;
+import java.util.zip.ZipFile;
import org.junit.rules.TemporaryFolder;
public class KotlinCompilerTool {
@@ -216,6 +217,21 @@
return annotationJar;
}
+ public void checkClasspath() throws IOException {
+ try (ZipFile zipFile = new ZipFile(compiler.toFile(), UTF_8)) {
+ Manifest manifest =
+ new Manifest(zipFile.getInputStream(zipFile.getEntry("META-INF/MANIFEST.MF")));
+ String classPath = manifest.getMainAttributes().getValue("Class-Path");
+ String[] classPathEntries = classPath.split(" ");
+ // Expect all classpath entries to be present ajacent to the compiler jar.
+ for (String classPathEntry : classPathEntries) {
+ assert !classPathEntry.contains("/");
+ assert Files.exists(compiler.getParent().resolve(classPathEntry))
+ : "Kotlin compiler missing classpath: " + classPathEntry;
+ }
+ }
+ }
+
@Override
public String toString() {
return name;
@@ -439,6 +455,7 @@
private CommandLineAndHasherConsumers buildCommandLineAndHasherConsumers(Path output)
throws IOException {
+ compiler.checkClasspath();
CommandLineAndHasherConsumers commandLineAndHasherConsumers =
new CommandLineAndHasherConsumers();
List<String> cmdline = commandLineAndHasherConsumers.cmdline;
@@ -460,7 +477,7 @@
// Until now this is just command line files, no inputs, hash existing command
String noneFileCommandLineArguments = StringUtils.join("", cmdline);
commandLineAndHasherConsumers.hasherConsumers.add(
- hasher -> hasher.putString(noneFileCommandLineArguments, StandardCharsets.UTF_8));
+ hasher -> hasher.putString(noneFileCommandLineArguments, UTF_8));
for (Path source : sources) {
cmdline.add(source.toString());
@@ -478,14 +495,14 @@
for (Path path : classpath) {
commandLineAndHasherConsumers.hasherConsumers.add(
hasher -> {
- hasher.putString("--cp", StandardCharsets.UTF_8);
+ hasher.putString("--cp", UTF_8);
hasher.putBytes(Files.readAllBytes(path));
});
}
}
cmdline.addAll(additionalArguments);
commandLineAndHasherConsumers.hasherConsumers.add(
- hasher -> additionalArguments.forEach(s -> hasher.putString(s, StandardCharsets.UTF_8)));
+ hasher -> additionalArguments.forEach(s -> hasher.putString(s, UTF_8)));
return commandLineAndHasherConsumers;
}
}
diff --git a/third_party/android_jar/lib-v35.tar.gz.sha1 b/third_party/android_jar/lib-v35.tar.gz.sha1
index f1f5b61..3543c83 100644
--- a/third_party/android_jar/lib-v35.tar.gz.sha1
+++ b/third_party/android_jar/lib-v35.tar.gz.sha1
@@ -1 +1 @@
-c9ad2358cc165f07fd68ef2151741ba12b26dcbc
\ No newline at end of file
+9ac3858ee6859c45500fa7c5254592516c5e73b7
\ No newline at end of file
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index a3e3b5e..0d2c06c 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-6fcae4d91999ef50dfbd2367c02270bc0ab9d2de
\ No newline at end of file
+b043b19332f3f7cd2578874b6d051462b55ac56a
\ No newline at end of file
diff --git a/tools/download_kotlin_dev.py b/tools/download_kotlin_dev.py
index aa73ba8..7359f1d 100755
--- a/tools/download_kotlin_dev.py
+++ b/tools/download_kotlin_dev.py
@@ -14,10 +14,14 @@
url_request = urllib
import os
import sys
+import xml.etree.ElementTree
+
JETBRAINS_KOTLIN_MAVEN_URL = "https://maven.pkg.jetbrains.space/kotlin/p/" \
"kotlin/bootstrap/org/jetbrains/kotlin/"
KOTLIN_RELEASE_URL = JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-compiler/"
+KOTLINC_LIB = os.path.join(utils.THIRD_PARTY, "kotlin",
+ "kotlin-compiler-dev", "kotlinc", "lib")
def download_newest():
@@ -55,12 +59,12 @@
raise Exception('Url: %s \n returned %s' %
(KOTLIN_RELEASE_URL, response.getcode()))
+ # Check POM for expected dependencies.
+ check_pom(top_most_version_and_build)
+
# We can now download all files related to the kotlin compiler version.
print("Downloading version: " + top_most_version_and_build)
- kotlinc_lib = os.path.join(utils.THIRD_PARTY, "kotlin",
- "kotlin-compiler-dev", "kotlinc", "lib")
-
utils.DownloadFromGoogleCloudStorage(
os.path.join(utils.THIRD_PARTY, "kotlin",
"kotlin-compiler-dev.tar.gz.sha1"))
@@ -68,20 +72,61 @@
download_and_save(
JETBRAINS_KOTLIN_MAVEN_URL +
"kotlin-compiler/{0}/kotlin-compiler-{0}.jar".format(
- top_most_version_and_build), kotlinc_lib, "kotlin-compiler.jar")
+ top_most_version_and_build), KOTLINC_LIB, "kotlin-compiler.jar")
download_and_save(
JETBRAINS_KOTLIN_MAVEN_URL +
"kotlin-stdlib/{0}/kotlin-stdlib-{0}.jar".format(
- top_most_version_and_build), kotlinc_lib, "kotlin-stdlib.jar")
+ top_most_version_and_build), KOTLINC_LIB, "kotlin-stdlib.jar")
+ download_and_save(
+ JETBRAINS_KOTLIN_MAVEN_URL +
+ "kotlin-stdlib-jdk8/{0}/kotlin-stdlib-jdk8-{0}.jar".format(
+ top_most_version_and_build), KOTLINC_LIB, "kotlin-stdlib-jdk8.jar")
+ # POM file has dependency on version 1.6.10 - download latest anyway.
download_and_save(
JETBRAINS_KOTLIN_MAVEN_URL +
"kotlin-reflect/{0}/kotlin-reflect-{0}.jar".format(
- top_most_version_and_build), kotlinc_lib, "kotlin-reflect.jar")
+ top_most_version_and_build), KOTLINC_LIB, "kotlin-reflect.jar")
download_and_save(
JETBRAINS_KOTLIN_MAVEN_URL +
"kotlin-script-runtime/{0}/kotlin-script-runtime-{0}.jar".format(
- top_most_version_and_build), kotlinc_lib,
+ top_most_version_and_build), KOTLINC_LIB,
"kotlin-script-runtime.jar")
+ download_and_save(
+ "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar", KOTLINC_LIB, "kotlinx-coroutines-core-jvm.jar")
+ download_and_save(
+ "https://repo1.maven.org/maven2/org/jetbrains/intellij/deps/trove4j/1.0.20200330/trove4j-1.0.20200330.jar", KOTLINC_LIB, "trove4j.jar")
+
+
+def check_pom(top_most_version_and_build):
+ # Download POM, and check the expected dependencies.
+ download_and_save(
+ JETBRAINS_KOTLIN_MAVEN_URL +
+ "kotlin-compiler/{0}/kotlin-compiler-{0}.pom".format(
+ top_most_version_and_build), KOTLINC_LIB, "kotlin-compiler.pom")
+ pom_file = os.path.join(KOTLINC_LIB, "kotlin-compiler.pom")
+ ns = "http://maven.apache.org/POM/4.0.0"
+ xml.etree.ElementTree.register_namespace('', ns)
+ tree = xml.etree.ElementTree.ElementTree()
+ tree.parse(pom_file)
+ #return tree.getroot().find("{%s}dependencies" % ns).text
+ for dependency in tree.getroot().find("{%s}dependencies" % ns):
+ groupId = dependency.find("{%s}groupId" % ns).text
+ artifactId = dependency.find("{%s}artifactId" % ns).text
+ version = dependency.find("{%s}version" % ns).text
+ coordinates = (
+ '{groupId}:{artifactId}:{version}'
+ .format(groupId=groupId, artifactId=artifactId, version=version))
+ print('Dependecy: ' + coordinates)
+ expected_dependencies = set()
+ for artifactId in ("kotlin-stdlib", "kotlin-stdlib-jdk8", "kotlin-script-runtime"):
+ expected_dependencies.add(
+ 'org.jetbrains.kotlin:{artifactId}:{version}'
+ .format(artifactId=artifactId, version=top_most_version_and_build))
+ expected_dependencies.add('org.jetbrains.kotlin:kotlin-reflect:1.6.10')
+ expected_dependencies.add('org.jetbrains.intellij.deps:trove4j:1.0.20200330')
+ expected_dependencies.add('org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4')
+ if not coordinates in expected_dependencies:
+ raise Exception('Unexpected dependency: ' + coordinates)
def download_and_save(url, path, name):