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):