Merge commit '34e27f5ef1e7849efd311fd649791a0024ef686a' into dev-release
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index cc80809..7837f26 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -47,10 +47,26 @@ """ % FMT_CMD)) return results +def CheckDeterministicDebuggingChanged(input_api, output_api): + for f in input_api.AffectedFiles(): + path = f.LocalPath() + if not path.endswith('InternalOptions.java'): + continue + branch = ( + check_output(['git', 'cl', 'upstream']) + .strip() + .replace('refs/heads/', '')) + diff = check_output( + ['git', 'diff', '--no-prefix', '-U0', branch, '--', path]) + if 'DETERMINISTIC_DEBUGGING' in diff: + return [output_api.PresubmitError(diff)] + return [] + def CheckChange(input_api, output_api): results = [] results.extend(CheckFormatting(input_api, output_api)) results.extend(CheckDoNotMerge(input_api, output_api)) + results.extend(CheckDeterministicDebuggingChanged(input_api, output_api)) return results def CheckChangeOnCommit(input_api, output_api):
diff --git a/build.gradle b/build.gradle index fd47c97..77b5865 100644 --- a/build.gradle +++ b/build.gradle
@@ -35,7 +35,7 @@ ext { androidSupportVersion = '25.4.0' - asmVersion = '7.1' + asmVersion = '7.2' espressoVersion = '3.0.0' fastutilVersion = '7.2.0' guavaVersion = '23.0'
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java index a6d704b..bf55656 100644 --- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java +++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -132,7 +132,7 @@ List<DexEncodedMethod> directMethods = new ArrayList<>(); List<DexEncodedMethod> virtualMethods = new ArrayList<>(); for (DexEncodedMethod method : methods) { - assert method.method.holder == clazz.type; + assert method.holder() == clazz.type; CfCode code = null; if (!method.accessFlags.isAbstract() /*&& !method.accessFlags.isNative()*/) { code = buildEmptyThrowingCfCode(method.method);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java index 4b310aa..51b6ed7 100644 --- a/src/main/java/com/android/tools/r8/PrintUses.java +++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -203,7 +203,7 @@ isStatic ? appInfo.lookupStaticTarget(field.holder, field) : appInfo.lookupInstanceTarget(field.holder, field); - if (baseField != null && baseField.field.holder != field.holder) { + if (baseField != null && baseField.holder() != field.holder) { field = baseField.field; } addType(field.holder); @@ -214,7 +214,7 @@ noObfuscationTypes.add(field.holder); } if (baseField.accessFlags.isVisibilityDependingOnPackage()) { - keepPackageNames.add(baseField.field.holder.getPackageName()); + keepPackageNames.add(baseField.holder().getPackageName()); } typeFields.add(field); } @@ -234,7 +234,7 @@ noObfuscationTypes.add(method.holder); } if (encodedMethod.accessFlags.isVisibilityDependingOnPackage()) { - keepPackageNames.add(encodedMethod.method.holder.getPackageName()); + keepPackageNames.add(encodedMethod.holder().getPackageName()); } typeMethods.add(method); } @@ -247,7 +247,7 @@ private void registerMethod(DexEncodedMethod method) { DexEncodedMethod superTarget = appInfo - .resolveMethod(method.method.holder, method.method) + .resolveMethod(method.holder(), method.method) .lookupInvokeSpecialTarget(context, appInfo); if (superTarget != null) { addMethod(superTarget.method); @@ -477,7 +477,7 @@ if (encodedMethod.accessFlags.isStatic()) { append("<clinit>"); } else { - String holderName = encodedMethod.method.holder.toSourceString(); + String holderName = encodedMethod.holder().toSourceString(); String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1); append(constructorName); }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 5bca79b1..15af102 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -34,7 +34,9 @@ import com.android.tools.r8.inspector.internal.InspectorImpl; import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker; import com.android.tools.r8.ir.conversion.IRConverter; +import com.android.tools.r8.ir.desugar.BackportedMethodRewriter; import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter; +import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter; import com.android.tools.r8.ir.desugar.NestedPrivateMethodLense; import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring; import com.android.tools.r8.ir.optimize.AssertionsRewriter; @@ -45,6 +47,7 @@ import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.UninstantiatedTypeOptimizationGraphLense; import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector; import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector.UnusedArgumentsGraphLense; +import com.android.tools.r8.ir.optimize.enums.EnumUnboxingCfMethods; import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.jar.CfApplicationWriter; @@ -280,6 +283,11 @@ if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) { DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView); } + InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options); + BackportedMethodRewriter.registerAssumedLibraryTypes(options); + if (options.enableEnumUnboxing) { + EnumUnboxingCfMethods.registerSynthesizedCodeReferences(options.itemFactory); + } List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>(); timing.begin("Strip unused code"); @@ -364,21 +372,22 @@ application = pruner.run(application); // Recompute the subtyping information. + Set<DexType> removedClasses = pruner.getRemovedClasses(); appView.setAppInfo( appView .appInfo() .withLiveness() .prunedCopyFrom( application, - pruner.getRemovedClasses(), + removedClasses, pruner.getMethodsToKeepForConfigurationDebugging())); - appView.setAppServices(appView.appServices().prunedCopy(pruner.getRemovedClasses())); + appView.setAppServices(appView.appServices().prunedCopy(removedClasses)); new AbstractMethodRemover(appView.appInfo().withLiveness()).run(); AnnotationRemover annotationRemover = annotationRemoverBuilder .computeClassesToRetainInnerClassAttributeFor(appViewWithLiveness) - .build(appViewWithLiveness); + .build(appViewWithLiveness, removedClasses); annotationRemover.ensureValid().run(); classesToRetainInnerClassAttributeFor = annotationRemover.getClassesToRetainInnerClassAttributeFor(); @@ -659,6 +668,7 @@ TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration); application = pruner.run(application); + Set<DexType> removedClasses = pruner.getRemovedClasses(); if (options.usageInformationConsumer != null) { ExceptionUtils.withFinishedResourceHandler( @@ -669,9 +679,9 @@ .appInfo() .prunedCopyFrom( application, - CollectionUtils.mergeSets(prunedTypes, pruner.getRemovedClasses()), + CollectionUtils.mergeSets(prunedTypes, removedClasses), pruner.getMethodsToKeepForConfigurationDebugging())); - appView.setAppServices(appView.appServices().prunedCopy(pruner.getRemovedClasses())); + appView.setAppServices(appView.appServices().prunedCopy(removedClasses)); // TODO(b/130721661): Enable this assert. // assert Inliner.verifyNoMethodsInlinedDueToSingleCallSite(appView); @@ -694,7 +704,7 @@ assert classesToRetainInnerClassAttributeFor != null; AnnotationRemover.builder() .setClassesToRetainInnerClassAttributeFor(classesToRetainInnerClassAttributeFor) - .build(appView.withLiveness()) + .build(appView.withLiveness(), removedClasses) .run(); if (!mainDexClasses.isEmpty()) { // Remove types that no longer exists from the computed main dex list.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index fea04c7..3e9ae7e 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8; +import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING; + import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation; import com.android.tools.r8.ProgramResource.Kind; import com.android.tools.r8.errors.DexFileOverflowDiagnostic; @@ -854,6 +856,7 @@ internal.enableVerticalClassMerging = false; internal.enableClassStaticizer = false; internal.outline.enabled = false; + internal.enableEnumUnboxing = false; } // Amend the proguard-map consumer with options from the proguard configuration. @@ -925,8 +928,10 @@ internal.synthesizedClassPrefix = synthesizedClassPrefix; internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; - assert internal.threadCount == ThreadUtils.NOT_SPECIFIED; - internal.threadCount = getThreadCount(); + if (!DETERMINISTIC_DEBUGGING) { + assert internal.threadCount == ThreadUtils.NOT_SPECIFIED; + internal.threadCount = getThreadCount(); + } return internal; }
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java index 93ee213..21a5392 100644 --- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java +++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -75,6 +75,7 @@ private Set<String> imports = new HashSet<>(); private List<String> methods = new ArrayList<>(); private Set<String> methodNames = new HashSet<>(); + private Set<String> synthesizedTypes = new HashSet<>(); // Per method structures. @@ -261,7 +262,8 @@ if (field != null) { return "options.itemFactory." + field; } - return "options.itemFactory.createSynthesizedType(" + quote(descriptor) + ")"; + synthesizedTypes.add(descriptor); + return "options.itemFactory.createType(" + quote(descriptor) + ")"; } private String dexProto(DexProto proto) { @@ -523,4 +525,8 @@ public void print(CfConstMethodType type) { throw new Unimplemented(type.getClass().getSimpleName()); } + + public Set<String> getSynthesizedTypes() { + return synthesizedTypes; + } }
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java index 5b4d9f2..93973c3 100644 --- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java +++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -238,8 +238,8 @@ if (argumentIndex < 0) { argumentType = code.method.isInstanceInitializer() - ? new ThisInstanceInfo(instruction.asArgument(), code.method.method.holder) - : createInitializedType(code.method.method.holder); + ? new ThisInstanceInfo(instruction.asArgument(), code.method.holder()) + : createInitializedType(code.method.holder()); } else { argumentType = createInitializedType(code.method.method.proto.parameters.values[argumentIndex]);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java index a7c3ec0..bd4c6da 100644 --- a/src/main/java/com/android/tools/r8/dex/FileWriter.java +++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -351,10 +351,10 @@ String originalClassName; if (proguardMap != null) { signature = proguardMap.originalSignatureOf(method.method); - originalClassName = proguardMap.originalNameOf(method.method.holder); + originalClassName = proguardMap.originalNameOf(method.holder()); } else { signature = MethodSignature.fromDexMethod(method.method); - originalClassName = method.method.holder.toSourceString(); + originalClassName = method.holder().toSourceString(); } codeToSignatureMap.put(code, originalClassName + signature); }
diff --git a/src/main/java/com/android/tools/r8/errors/InvalidLibrarySuperclassDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InvalidLibrarySuperclassDiagnostic.java new file mode 100644 index 0000000..2392f0a --- /dev/null +++ b/src/main/java/com/android/tools/r8/errors/InvalidLibrarySuperclassDiagnostic.java
@@ -0,0 +1,71 @@ +// Copyright (c) 2020, 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.errors; + +import com.android.tools.r8.Keep; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.position.Position; +import com.android.tools.r8.references.ClassReference; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.utils.StringUtils; +import java.util.List; + +/** + * Diagnostic for super types of library classes which are not library classes but required for + * desugaring. + */ +@Keep +public class InvalidLibrarySuperclassDiagnostic implements DesugarDiagnostic { + + private final Origin origin; + private final List<MethodReference> methods; + private final ClassReference libraryType; + private final ClassReference invalidSuperType; + private final String message; + + public InvalidLibrarySuperclassDiagnostic( + Origin origin, + ClassReference libraryType, + ClassReference invalidSuperType, + String message, + List<MethodReference> methods) { + assert origin != null; + assert libraryType != null; + assert invalidSuperType != null; + assert message != null; + this.origin = origin; + this.libraryType = libraryType; + this.invalidSuperType = invalidSuperType; + this.message = message; + this.methods = methods; + } + + @Override + public Origin getOrigin() { + return origin; + } + + @Override + public Position getPosition() { + return Position.UNKNOWN; + } + + @Override + public String getDiagnosticMessage() { + StringBuilder builder = + new StringBuilder() + .append("Superclass `") + .append(invalidSuperType.getTypeName()) + .append("` of library class `") + .append(libraryType.getTypeName()) + .append("` is ") + .append(message) + .append( + ". A superclass of a library class should be a library class. This is required for" + + " the desugaring of "); + StringUtils.append(builder, methods, ", ", StringUtils.BraceType.NONE); + return builder.toString(); + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java index a41e119..8b63cbf 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfo.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -141,6 +141,10 @@ @Override public DexClass definitionFor(DexType type) { + return definitionForWithoutExistenceAssert(type); + } + + public final DexClass definitionForWithoutExistenceAssert(DexType type) { assert checkIfObsolete(); DexProgramClass cached = synthesizedClasses.get(type); if (cached != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java index 58611d4..4edc070 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -357,7 +357,7 @@ return true; } - public boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) { + public boolean isInstantiatedInterface(DexProgramClass clazz) { assert checkIfObsolete(); return true; // Don't know, there might be. }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java index 0c4267c..897763a 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -400,7 +400,7 @@ @Override public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { for (CfInstruction instruction : instructions) { - instruction.registerUse(registry, method.method.holder); + instruction.registerUse(registry, method.holder()); } for (CfTryCatch tryCatch : tryCatchRanges) { for (DexType guard : tryCatch.guards) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java index 85810ed..9dd0398 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -332,7 +332,7 @@ } private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) { - assert field.field.holder == type + assert field.holder() == type : "Expected field `" + field.field.toSourceString() + "` to have holder `" @@ -435,8 +435,7 @@ } private boolean isSignaturePolymorphicMethod(DexEncodedMethod method, DexItemFactory factory) { - assert method.method.holder == factory.methodHandleType - || method.method.holder == factory.varHandleType; + assert method.holder() == factory.methodHandleType || method.holder() == factory.varHandleType; return method.accessFlags.isVarargs() && method.accessFlags.isNative() && method.method.proto.parameters.size() == 1
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java index 37f8853..6c05f12 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -12,7 +12,7 @@ private final DexEncodedMethod method; DexClassAndMethod(DexClass holder, DexEncodedMethod method) { - assert holder.type == method.method.holder; + assert holder.type == method.holder(); this.holder = holder; this.method = method; }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java index 49611c6..e73c267 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -73,7 +73,7 @@ int argumentRegister = code.registerSize - code.incomingRegisterSize; if (!method.accessFlags.isStatic()) { DexString name = factory.thisName; - DexType type = method.method.holder; + DexType type = method.holder(); startArgument(argumentRegister, name, type); argumentRegister += ValueType.fromDexType(type).requiredRegisters(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java index 10427a3..0d5c50d 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -159,6 +159,10 @@ return isStatic(); } + public boolean isVolatile() { + return accessFlags.isVolatile(); + } + public boolean hasAnnotation() { return !annotations().isEmpty(); } @@ -206,7 +210,7 @@ && singleValue.asSingleFieldValue().getField() == field) { return null; } - if (singleValue.isMaterializableInContext(appView, code.method.method.holder)) { + if (singleValue.isMaterializableInContext(appView, code.method.holder())) { TypeElement type = TypeElement.fromDexType(field.type, maybeNull(), appView); return singleValue.createMaterializingInstruction( appView, code, TypeAndLocalInfoSupplier.create(type, local));
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index 01b0e84..bb1ee15 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -450,7 +450,7 @@ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { checkIfObsolete(); return isInliningCandidate( - container.method.holder, inliningReason, appInfo, whyAreYouNotInliningReporter); + container.holder(), inliningReason, appInfo, whyAreYouNotInliningReporter); } public boolean isInliningCandidate(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java index 30d9369..b0a86c6 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -26,6 +26,8 @@ import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring; +import com.android.tools.r8.ir.optimize.enums.EnumUnboxingRewriter; import com.android.tools.r8.kotlin.Kotlin; import com.android.tools.r8.utils.ArrayUtils; import com.android.tools.r8.utils.LRUCacheTable; @@ -156,6 +158,7 @@ public final DexString isEmptyMethodName = createString("isEmpty"); public final DexString lengthMethodName = createString("length"); + public final DexString concatMethodName = createString("concat"); public final DexString containsMethodName = createString("contains"); public final DexString startsWithMethodName = createString("startsWith"); public final DexString endsWithMethodName = createString("endsWith"); @@ -254,6 +257,8 @@ public final DexString throwableDescriptor = createString(throwableDescriptorString); public final DexString illegalAccessErrorDescriptor = createString("Ljava/lang/IllegalAccessError;"); + public final DexString illegalArgumentExceptionDescriptor = + createString("Ljava/lang/IllegalArgumentException;"); public final DexString icceDescriptor = createString("Ljava/lang/IncompatibleClassChangeError;"); public final DexString exceptionInInitializerErrorDescriptor = createString("Ljava/lang/ExceptionInInitializerError;"); @@ -381,6 +386,8 @@ public final DexType throwableType = createStaticallyKnownType(throwableDescriptor); public final DexType illegalAccessErrorType = createStaticallyKnownType(illegalAccessErrorDescriptor); + public final DexType illegalArgumentExceptionType = + createStaticallyKnownType(illegalArgumentExceptionDescriptor); public final DexType icceType = createStaticallyKnownType(icceDescriptor); public final DexType exceptionInInitializerErrorType = createStaticallyKnownType(exceptionInInitializerErrorDescriptor); @@ -398,6 +405,15 @@ public final DexType androidOsBuildVersionType = createStaticallyKnownType("Landroid/os/Build$VERSION;"); + public final DexString nestConstructorDescriptor = + createString("L" + NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME + ";"); + public final DexType nestConstructorType = createStaticallyKnownType(nestConstructorDescriptor); + + public final DexString enumUnboxingUtilityDescriptor = + createString("L" + EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_CLASS_NAME + ";"); + public final DexType enumUnboxingUtilityType = + createStaticallyKnownType(enumUnboxingUtilityDescriptor); + public final StringBuildingMethods stringBuilderMethods = new StringBuildingMethods(stringBuilderType); public final StringBuildingMethods stringBufferMethods = @@ -415,6 +431,8 @@ public final ConstructorMethods constructorMethods = new ConstructorMethods(); public final EnumMethods enumMethods = new EnumMethods(); public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods(); + public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods = + new IllegalArgumentExceptionMethods(); public final PrimitiveTypesBoxedTypeFields primitiveTypesBoxedTypeFields = new PrimitiveTypesBoxedTypeFields(); public final AtomicFieldUpdaterMethods atomicFieldUpdaterMethods = @@ -965,6 +983,13 @@ createMethod(npeType, createProto(voidType, stringType), constructorMethodName); } + public class IllegalArgumentExceptionMethods { + + public final DexMethod initWithMessage = + createMethod( + illegalArgumentExceptionType, createProto(voidType, stringType), initMethodName); + } + /** * All boxed types (Boolean, Byte, ...) have a field named TYPE which contains the Class object * for the primitive type. @@ -1051,6 +1076,7 @@ public final DexMethod isEmpty; public final DexMethod length; + public final DexMethod concat; public final DexMethod contains; public final DexMethod startsWith; public final DexMethod endsWith; @@ -1083,6 +1109,7 @@ DexString[] needsOneObject = { objectDescriptor }; DexString[] needsOneInt = { intDescriptor }; + concat = createMethod(stringDescriptor, concatMethodName, stringDescriptor, needsOneString); contains = createMethod( stringDescriptor, containsMethodName, booleanDescriptor, needsOneCharSequence); startsWith = createMethod( @@ -1414,6 +1441,12 @@ return type; } + // Registration of a type that is only dynamically known (eg, in the desugared lib spec), but + // will be referenced during desugaring. + public void registerTypeNeededForDesugaring(DexType type) { + addPossiblySynthesizedType(type); + } + private void addPossiblySynthesizedType(DexType type) { if (type.isArrayType()) { type = type.toBaseType(this);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java index 80605e5..5236191 100644 --- a/src/main/java/com/android/tools/r8/graph/DexMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -6,6 +6,11 @@ import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.references.TypeReference; +import java.util.ArrayList; +import java.util.List; public class DexMethod extends DexMember<DexEncodedMethod, DexMethod> { @@ -28,6 +33,22 @@ return "Method " + holder + "." + name + " " + proto.toString(); } + public MethodReference asMethodReference(AppView<?> appView) { + List<TypeReference> parameters = new ArrayList<>(); + for (DexType value : proto.parameters.values) { + parameters.add(Reference.typeFromDescriptor(value.toDescriptorString())); + } + TypeReference returnType = + proto.returnType == appView.dexItemFactory().voidType + ? null + : Reference.typeFromDescriptor(proto.returnType.toDescriptorString()); + return Reference.method( + Reference.classFromDescriptor(holder.toDescriptorString()), + name.toString(), + parameters, + returnType); + } + public int getArity() { return proto.parameters.size(); }
diff --git a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java index 4fd96f7..23bfb99 100644 --- a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java +++ b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
@@ -7,6 +7,7 @@ import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; public class EnumValueInfoMapCollection { @@ -94,6 +95,10 @@ return map.get(field); } + public void forEach(BiConsumer<DexField, EnumValueInfo> consumer) { + map.forEach(consumer); + } + EnumValueInfoMap rewrittenWithLens(GraphLense lens) { ImmutableMap.Builder<DexField, EnumValueInfo> builder = ImmutableMap.builder(); map.forEach(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java index 049cb7a..97d49a5 100644 --- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java +++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -96,6 +96,8 @@ */ public class GenericSignature { + private static final List<FormalTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of(); + interface DexDefinitionSignature<T extends DexDefinition> { default boolean isClassSignature() { return false; @@ -122,19 +124,51 @@ } } + public static class FormalTypeParameter { + + final String name; + final FieldTypeSignature classBound; + final List<FieldTypeSignature> interfaceBounds; + + FormalTypeParameter( + String name, FieldTypeSignature classBound, List<FieldTypeSignature> interfaceBounds) { + this.name = name; + this.classBound = classBound; + this.interfaceBounds = interfaceBounds; + } + + public String getName() { + return name; + } + + public FieldTypeSignature getClassBound() { + return classBound; + } + + public List<FieldTypeSignature> getInterfaceBounds() { + return interfaceBounds; + } + } + public static class ClassSignature implements DexDefinitionSignature<DexClass> { static final ClassSignature UNKNOWN_CLASS_SIGNATURE = - new ClassSignature(ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE, ImmutableList.of()); + new ClassSignature( + ImmutableList.of(), + ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE, + ImmutableList.of()); - // TODO(b/129925954): encoding formal type parameters + final List<FormalTypeParameter> formalTypeParameters; final ClassTypeSignature superClassSignature; final List<ClassTypeSignature> superInterfaceSignatures; ClassSignature( + List<FormalTypeParameter> formalTypeParameters, ClassTypeSignature superClassSignature, List<ClassTypeSignature> superInterfaceSignatures) { + assert formalTypeParameters != null; assert superClassSignature != null; assert superInterfaceSignatures != null; + this.formalTypeParameters = formalTypeParameters; this.superClassSignature = superClassSignature; this.superInterfaceSignatures = superInterfaceSignatures; } @@ -221,6 +255,10 @@ public TypeVariableSignature asTypeVariableSignature() { return null; } + + public boolean isUnknown() { + return this == ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE; + } } public static class ClassTypeSignature extends FieldTypeSignature { @@ -331,7 +369,7 @@ public static class TypeVariableSignature extends FieldTypeSignature { final String typeVariable; - TypeVariableSignature(String typeVariable) { + private TypeVariableSignature(String typeVariable) { assert typeVariable != null; this.typeVariable = typeVariable; } @@ -350,6 +388,10 @@ public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) { return new ArrayTypeSignature(this); } + + public String getTypeVariable() { + return typeVariable; + } } // TODO(b/129925954): Canonicalization? @@ -400,20 +442,24 @@ public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> { static final MethodTypeSignature UNKNOWN_METHOD_TYPE_SIGNATURE = - new MethodTypeSignature(ImmutableList.of(), ReturnType.VOID, ImmutableList.of()); + new MethodTypeSignature( + ImmutableList.of(), ImmutableList.of(), ReturnType.VOID, ImmutableList.of()); - // TODO(b/129925954): encoding formal type parameters + final List<FormalTypeParameter> formalTypeParameters; final List<TypeSignature> typeSignatures; final ReturnType returnType; final List<TypeSignature> throwsSignatures; MethodTypeSignature( + final List<FormalTypeParameter> formalTypeParameters, List<TypeSignature> typeSignatures, ReturnType returnType, List<TypeSignature> throwsSignatures) { + assert formalTypeParameters != null; assert typeSignatures != null; assert returnType != null; assert throwsSignatures != null; + this.formalTypeParameters = formalTypeParameters; this.typeSignatures = typeSignatures; this.returnType = returnType; this.throwsSignatures = throwsSignatures; @@ -443,6 +489,10 @@ public MethodTypeSignature asMethodTypeSignature() { return this; } + + public List<FormalTypeParameter> getFormalTypeParameters() { + return formalTypeParameters; + } } enum Kind { @@ -518,7 +568,7 @@ public static FieldTypeSignature toFieldTypeSignature( DexEncodedField field, AppView<AppInfoWithLiveness> appView) { - DexClass currentClassContext = appView.definitionFor(field.field.holder); + DexClass currentClassContext = appView.definitionFor(field.holder()); DexDefinitionSignature<?> signature = toGenericSignature(currentClassContext, field, appView); if (signature != null) { @@ -530,7 +580,7 @@ public static MethodTypeSignature toMethodTypeSignature( DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) { - DexClass currentClassContext = appView.definitionFor(method.method.holder); + DexClass currentClassContext = appView.definitionFor(method.holder()); DexDefinitionSignature<?> signature = toGenericSignature(currentClassContext, method, appView); if (signature != null) { @@ -688,7 +738,7 @@ private ClassSignature parseClassSignature() { // ClassSignature ::= FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*. - parseOptFormalTypeParameters(); + List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters(); // SuperclassSignature ::= ClassTypeSignature. ClassTypeSignature superClassSignature = @@ -700,42 +750,53 @@ builder.add(parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION)); } - return new ClassSignature(superClassSignature, builder.build()); + return new ClassSignature(formalTypeParameters, superClassSignature, builder.build()); } - private void parseOptFormalTypeParameters() { + private List<FormalTypeParameter> parseOptFormalTypeParameters() { // FormalTypeParameters ::= "<" FormalTypeParameter+ ">". - - if (symbol == '<') { - scanSymbol(); - - updateFormalTypeParameter(); - - while ((symbol != '>') && (symbol > 0)) { - updateFormalTypeParameter(); - } - - expect('>'); + if (symbol != '<') { + return EMPTY_TYPE_PARAMS; } + scanSymbol(); + + ImmutableList.Builder<FormalTypeParameter> builder = ImmutableList.builder(); + while ((symbol != '>') && (symbol > 0)) { + builder.add(updateFormalTypeParameter()); + } + expect('>'); + return builder.build(); } - private void updateFormalTypeParameter() { + private FormalTypeParameter updateFormalTypeParameter() { // FormalTypeParameter ::= Identifier ClassBound InterfaceBound*. scanIdentifier(); assert identifier != null; + String typeParameterIdentifier = identifier; + // ClassBound ::= ":" FieldTypeSignature?. expect(':'); + FieldTypeSignature classBound = ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE; if (symbol == 'L' || symbol == '[' || symbol == 'T') { - parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); + classBound = parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); } + // Only build the interfacebound builder, which is uncommon, if we actually see an interface. + ImmutableList.Builder<FieldTypeSignature> builder = null; while (symbol == ':') { // InterfaceBound ::= ":" FieldTypeSignature. + if (builder == null) { + builder = ImmutableList.builder(); + } scanSymbol(); - parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION); + builder.add(parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION)); } + if (builder == null) { + return new FormalTypeParameter(typeParameterIdentifier, classBound, null); + } + return new FormalTypeParameter(typeParameterIdentifier, classBound, builder.build()); } private FieldTypeSignature parseFieldTypeSignature(ParserPosition parserPosition) { @@ -862,7 +923,7 @@ private MethodTypeSignature parseMethodTypeSignature() { // MethodTypeSignature ::= // FormalTypeParameters? "(" TypeSignature* ")" ReturnType ThrowsSignature*. - parseOptFormalTypeParameters(); + List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters(); expect('('); @@ -890,7 +951,10 @@ } return new MethodTypeSignature( - parameterSignatureBuilder.build(), returnType, throwsSignatureBuilder.build()); + formalTypeParameters, + parameterSignatureBuilder.build(), + returnType, + throwsSignatureBuilder.build()); } private ReturnType updateReturnType() {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java index 7d2d844..e5de7ff 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLense.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -15,7 +15,6 @@ import java.util.ArrayDeque; import java.util.Collections; import java.util.Deque; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; @@ -710,7 +709,7 @@ @Override public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) { - Set<DexMethod> result = new HashSet<>(); + Set<DexMethod> result = Sets.newIdentityHashSet(); for (DexMethod previous : previousLense.lookupMethodInAllContexts(method)) { result.add(methodMap.getOrDefault(previous, previous)); }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java index 269b36b..6fecde6 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java +++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -228,7 +228,7 @@ } private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) { - assert method.method.holder == holder.type + assert method.holder() == holder.type : "Expected method `" + method.method.toSourceString() + "` to have holder `"
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java index 2ed505e..546bf90 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -20,6 +20,12 @@ boolean isInstantiatedDirectly(DexProgramClass clazz); + boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz); + + boolean isInterfaceWithUnknownSubtypeHierarchy(DexProgramClass clazz); + + boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass clazz); + ObjectAllocationInfoCollection rewrittenWithLens( DexDefinitionSupplier definitions, GraphLense lens); }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java index 3869d23..5999bbf 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -21,28 +21,101 @@ import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Predicate; -/** Stores the set of instantiated classes along with their allocation sites. */ -public class ObjectAllocationInfoCollectionImpl implements ObjectAllocationInfoCollection { +/** + * Provides information about all possibly instantiated classes and lambdas, their allocation sites, + * if known, as well as the full subtyping hierarchy of types above them. + */ +public abstract class ObjectAllocationInfoCollectionImpl implements ObjectAllocationInfoCollection { - private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking; - private final Set<DexProgramClass> classesWithoutAllocationSiteTracking; + /** Instantiated classes with the contexts of the instantiations. */ + final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking = + new IdentityHashMap<>(); - private ObjectAllocationInfoCollectionImpl( - Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking, - Set<DexProgramClass> classesWithoutAllocationSiteTracking) { - this.classesWithAllocationSiteTracking = classesWithAllocationSiteTracking; - this.classesWithoutAllocationSiteTracking = classesWithoutAllocationSiteTracking; + /** Instantiated classes without contexts. */ + final Set<DexProgramClass> classesWithoutAllocationSiteTracking = Sets.newIdentityHashSet(); + + /** + * Set of interface types for which the subtype hierarchy is unknown from that type. + * + * <p>E.g., the type is kept thus there could be instantiations of subtypes. + * + * <p>TODO(b/145344105): Generalize this to typesWithUnknownSubtypeHierarchy. + */ + final Set<DexProgramClass> interfacesWithUnknownSubtypeHierarchy = Sets.newIdentityHashSet(); + + /** Map of types directly implemented by lambdas to those lambdas. */ + final Map<DexType, List<LambdaDescriptor>> instantiatedLambdas = new IdentityHashMap<>(); + + /** + * Hierarchy for instantiated types mapping a type to the set of immediate subtypes for which some + * subtype is either an instantiated class, kept interface or is implemented by an instantiated + * lambda. + */ + Map<DexType, Set<DexClass>> instantiatedHierarchy = new IdentityHashMap<>(); + + private ObjectAllocationInfoCollectionImpl() { + // Only builder can allocate an instance. } public static Builder builder(boolean trackAllocationSites, GraphReporter reporter) { return new Builder(trackAllocationSites, reporter); } - public void markNoLongerInstantiated(DexProgramClass clazz) { - classesWithAllocationSiteTracking.remove(clazz); - classesWithoutAllocationSiteTracking.remove(clazz); + public abstract void mutate(Consumer<Builder> mutator, AppInfo appInfo); + + /** + * True if a class type might be instantiated directly at the given type. + * + * <p>Should not be called on interface types. + * + * <p>TODO(b/145344105): Extend this to not be called on any abstract types. + */ + @Override + public boolean isInstantiatedDirectly(DexProgramClass clazz) { + if (clazz.isInterface()) { + return false; + } + if (classesWithAllocationSiteTracking.containsKey(clazz)) { + assert !classesWithAllocationSiteTracking.get(clazz).isEmpty(); + return true; + } + return classesWithoutAllocationSiteTracking.contains(clazz); + } + + /** True if the type or subtype of it might be instantiated. */ + @Override + public boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz) { + return (!clazz.isInterface() && isInstantiatedDirectly(clazz)) + || hasInstantiatedStrictSubtype(clazz); + } + + /** True if there might exist an instantiated (strict) subtype of the given type. */ + public boolean hasInstantiatedStrictSubtype(DexProgramClass clazz) { + if (instantiatedHierarchy.get(clazz.type) != null) { + return true; + } + if (!clazz.isInterface()) { + return false; + } + return interfacesWithUnknownSubtypeHierarchy.contains(clazz) + || isImmediateInterfaceOfInstantiatedLambda(clazz); + } + + /** True if the type is an interface that has unknown instantiations, eg, by being kept. */ + @Override + public boolean isInterfaceWithUnknownSubtypeHierarchy(DexProgramClass clazz) { + return clazz.isInterface() && interfacesWithUnknownSubtypeHierarchy.contains(clazz); + } + + /** Returns true if the type is an immediate interface of an instantiated lambda. */ + @Override + public boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass iface) { + return iface.isInterface() && instantiatedLambdas.get(iface.type) != null; + } + + public Set<DexClass> getImmediateSubtypesInInstantiatedHierarchy(DexType type) { + return instantiatedHierarchy.get(type); } @Override @@ -57,238 +130,15 @@ } @Override - public boolean isInstantiatedDirectly(DexProgramClass clazz) { - if (classesWithAllocationSiteTracking.containsKey(clazz)) { - assert !classesWithAllocationSiteTracking.get(clazz).isEmpty(); - return true; - } - return classesWithoutAllocationSiteTracking.contains(clazz); - } - - @Override public ObjectAllocationInfoCollectionImpl rewrittenWithLens( DexDefinitionSupplier definitions, GraphLense lens) { - return builder(true, null).rewrittenWithLens(this, definitions, lens).build(); + return builder(true, null).rewrittenWithLens(this, definitions, lens).build(definitions); } - public static class Builder { - - private final boolean trackAllocationSites; - - /** Instantiated classes with the contexts of the instantiations. */ - private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking = - new IdentityHashMap<>(); - - /** Instantiated classes without contexts. */ - private final Set<DexProgramClass> classesWithoutAllocationSiteTracking = - Sets.newIdentityHashSet(); - - /** Set of types directly implemented by a lambda. */ - private final Map<DexType, List<LambdaDescriptor>> instantiatedLambdas = - new IdentityHashMap<>(); - - /** - * Hierarchy for instantiated types mapping a type to the set of immediate subtypes for which - * some subtype is either instantiated or is implemented by an instantiated lambda. - */ - private final Map<DexType, Set<DexClass>> instantiatedHierarchy = new IdentityHashMap<>(); - - /** - * Set of interface types for which there may be instantiations, such as lambda expressions or - * explicit keep rules. - */ - private final Set<DexProgramClass> instantiatedInterfaceTypes = Sets.newIdentityHashSet(); - - /** Subset of the above that are marked instantiated by usages that are not lambdas. */ - public final Set<DexProgramClass> unknownInstantiatedInterfaceTypes = Sets.newIdentityHashSet(); - - private GraphReporter reporter; - - private Builder(boolean trackAllocationSites, GraphReporter reporter) { - this.trackAllocationSites = trackAllocationSites; - this.reporter = reporter; - } - - private boolean shouldTrackAllocationSitesForClass( - DexProgramClass clazz, InstantiationReason instantiationReason) { - if (!trackAllocationSites) { - return false; - } - if (instantiationReason != InstantiationReason.NEW_INSTANCE_INSTRUCTION) { - // There is an allocation site which is not a new-instance instruction. - return false; - } - if (classesWithoutAllocationSiteTracking.contains(clazz)) { - // We already gave up on tracking the allocation sites for `clazz` previously. - return false; - } - // We currently only use allocation site information for instance field value propagation. - return !clazz.instanceFields().isEmpty(); - } - - public boolean isInstantiatedDirectlyOrIsInstantiationLeaf(DexProgramClass clazz) { - if (clazz.isInterface()) { - return instantiatedInterfaceTypes.contains(clazz); - } - return isInstantiatedDirectly(clazz); - } - - public boolean isInstantiatedDirectly(DexProgramClass clazz) { - assert !clazz.isInterface(); - if (classesWithAllocationSiteTracking.containsKey(clazz)) { - assert !classesWithAllocationSiteTracking.get(clazz).isEmpty(); - return true; - } - return classesWithoutAllocationSiteTracking.contains(clazz); - } - - public boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz) { - return isInstantiatedDirectlyOrIsInstantiationLeaf(clazz) - || instantiatedHierarchy.containsKey(clazz.type); - } - - public void forEachInstantiatedSubType( - DexType type, - Consumer<DexProgramClass> onClass, - Consumer<LambdaDescriptor> onLambda, - AppInfo appInfo) { - internalForEachInstantiatedSubType( - type, - onClass, - onLambda, - instantiatedHierarchy, - instantiatedLambdas, - this::isInstantiatedDirectlyOrIsInstantiationLeaf, - appInfo); - } - - public Set<DexClass> getImmediateSubtypesInInstantiatedHierarchy(DexType type) { - return instantiatedHierarchy.get(type); - } - - /** - * Records that {@param clazz} is instantiated in {@param context}. - * - * @return true if {@param clazz} was not instantiated before. - */ - public boolean recordDirectAllocationSite( - DexProgramClass clazz, - DexEncodedMethod context, - InstantiationReason instantiationReason, - KeepReason keepReason, - AppInfo appInfo) { - assert !clazz.isInterface(); - if (reporter != null) { - reporter.registerClass(clazz, keepReason); - } - populateInstantiatedHierarchy(appInfo, clazz); - if (shouldTrackAllocationSitesForClass(clazz, instantiationReason)) { - assert context != null; - Set<DexEncodedMethod> allocationSitesForClass = - classesWithAllocationSiteTracking.computeIfAbsent( - clazz, ignore -> Sets.newIdentityHashSet()); - allocationSitesForClass.add(context); - return allocationSitesForClass.size() == 1; - } - if (classesWithoutAllocationSiteTracking.add(clazz)) { - Set<DexEncodedMethod> allocationSitesForClass = - classesWithAllocationSiteTracking.remove(clazz); - return allocationSitesForClass == null; - } - return false; - } - - public boolean recordInstantiatedInterface(DexProgramClass iface) { - assert iface.isInterface(); - assert !iface.isAnnotation(); - unknownInstantiatedInterfaceTypes.add(iface); - return instantiatedInterfaceTypes.add(iface); - } - - public void recordInstantiatedLambdaInterface( - DexType iface, LambdaDescriptor lambda, AppInfo appInfo) { - instantiatedLambdas.computeIfAbsent(iface, key -> new ArrayList<>()).add(lambda); - populateInstantiatedHierarchy(appInfo, iface); - } - - private void populateInstantiatedHierarchy(AppInfo appInfo, DexType type) { - DexClass clazz = appInfo.definitionFor(type); - if (clazz != null) { - populateInstantiatedHierarchy(appInfo, clazz); - } - } - - private void populateInstantiatedHierarchy(AppInfo appInfo, DexClass clazz) { - if (clazz.superType != null) { - populateInstantiatedHierarchy(appInfo, clazz.superType, clazz); - } - for (DexType iface : clazz.interfaces.values) { - populateInstantiatedHierarchy(appInfo, iface, clazz); - } - } - - private void populateInstantiatedHierarchy(AppInfo appInfo, DexType type, DexClass subtype) { - if (type == appInfo.dexItemFactory().objectType) { - return; - } - Set<DexClass> subtypes = instantiatedHierarchy.get(type); - if (subtypes != null) { - subtypes.add(subtype); - return; - } - // This is the first time an instantiation appears below 'type', recursively populate. - subtypes = Sets.newIdentityHashSet(); - subtypes.add(subtype); - instantiatedHierarchy.put(type, subtypes); - populateInstantiatedHierarchy(appInfo, type); - } - - Builder rewrittenWithLens( - ObjectAllocationInfoCollectionImpl objectAllocationInfos, - DexDefinitionSupplier definitions, - GraphLense lens) { - objectAllocationInfos.classesWithAllocationSiteTracking.forEach( - (clazz, allocationSitesForClass) -> { - DexType type = lens.lookupType(clazz.type); - if (type.isPrimitiveType()) { - return; - } - DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type)); - assert rewrittenClass != null; - assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass); - classesWithAllocationSiteTracking.put( - rewrittenClass, - LensUtils.rewrittenWithRenamedSignature( - allocationSitesForClass, definitions, lens)); - }); - objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach( - clazz -> { - DexType type = lens.lookupType(clazz.type); - if (type.isPrimitiveType()) { - return; - } - DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type)); - assert rewrittenClass != null; - assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass); - assert !classesWithoutAllocationSiteTracking.contains(rewrittenClass); - classesWithoutAllocationSiteTracking.add(rewrittenClass); - }); - return this; - } - - public ObjectAllocationInfoCollectionImpl build() { - return new ObjectAllocationInfoCollectionImpl( - classesWithAllocationSiteTracking, classesWithoutAllocationSiteTracking); - } - } - - private static void internalForEachInstantiatedSubType( + public void forEachInstantiatedSubType( DexType type, - Consumer<DexProgramClass> subTypeConsumer, - Consumer<LambdaDescriptor> lambdaConsumer, - Map<DexType, Set<DexClass>> instantiatedHierarchy, - Map<DexType, List<LambdaDescriptor>> instantiatedLambdas, - Predicate<DexProgramClass> isInstantiatedDirectly, + Consumer<DexProgramClass> onClass, + Consumer<LambdaDescriptor> onLambda, AppInfo appInfo) { WorkList<DexClass> worklist = WorkList.newIdentityWorkList(); if (type == appInfo.dexItemFactory().objectType) { @@ -307,7 +157,7 @@ // If no definition for the type is found, populate the worklist with any // instantiated subtypes and callback with any lambda instance. worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(type, Collections.emptySet())); - instantiatedLambdas.getOrDefault(type, Collections.emptyList()).forEach(lambdaConsumer); + instantiatedLambdas.getOrDefault(type, Collections.emptyList()).forEach(onLambda); } else { worklist.addIfNotSeen(initialClass); } @@ -317,12 +167,287 @@ DexClass clazz = worklist.next(); if (clazz.isProgramClass()) { DexProgramClass programClass = clazz.asProgramClass(); - if (isInstantiatedDirectly.test(programClass)) { - subTypeConsumer.accept(programClass); + if (isInstantiatedDirectly(programClass) + || isInterfaceWithUnknownSubtypeHierarchy(programClass)) { + onClass.accept(programClass); } } worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(clazz.type, Collections.emptySet())); - instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList()).forEach(lambdaConsumer); + instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList()).forEach(onLambda); + } + } + + public static class Builder extends ObjectAllocationInfoCollectionImpl { + + private static class Data { + + private final boolean trackAllocationSites; + private final GraphReporter reporter; + + private Data(boolean trackAllocationSites, GraphReporter reporter) { + this.trackAllocationSites = trackAllocationSites; + this.reporter = reporter; + } + } + + // Pointer to data valid during the duration of the builder. + private Data data; + + private Builder(boolean trackAllocationSites, GraphReporter reporter) { + data = new Data(trackAllocationSites, reporter); + } + + public ObjectAllocationInfoCollectionImpl build(DexDefinitionSupplier definitions) { + assert data != null; + if (instantiatedHierarchy == null) { + repopulateInstantiatedHierarchy(definitions); + } + assert validate(definitions); + data = null; + return this; + } + + // Consider a mutation interface that has just the mutation methods. + @Override + public void mutate(Consumer<Builder> mutator, AppInfo appInfo) { + mutator.accept(this); + repopulateInstantiatedHierarchy(appInfo); + } + + private boolean shouldTrackAllocationSitesForClass( + DexProgramClass clazz, InstantiationReason instantiationReason) { + if (!data.trackAllocationSites) { + return false; + } + if (instantiationReason != InstantiationReason.NEW_INSTANCE_INSTRUCTION) { + // There is an allocation site which is not a new-instance instruction. + return false; + } + if (classesWithoutAllocationSiteTracking.contains(clazz)) { + // We already gave up on tracking the allocation sites for `clazz` previously. + return false; + } + // We currently only use allocation site information for instance field value propagation. + return !clazz.instanceFields().isEmpty(); + } + + /** + * Records that {@param clazz} is instantiated in {@param context}. + * + * @return true if {@param clazz} was not instantiated before. + */ + public boolean recordDirectAllocationSite( + DexProgramClass clazz, + DexEncodedMethod context, + InstantiationReason instantiationReason, + KeepReason keepReason, + AppInfo appInfo) { + assert !clazz.isInterface(); + if (data.reporter != null) { + data.reporter.registerClass(clazz, keepReason); + } + populateInstantiatedHierarchy(appInfo, clazz); + if (shouldTrackAllocationSitesForClass(clazz, instantiationReason)) { + assert context != null; + Set<DexEncodedMethod> allocationSitesForClass = + classesWithAllocationSiteTracking.computeIfAbsent( + clazz, ignore -> Sets.newIdentityHashSet()); + allocationSitesForClass.add(context); + return allocationSitesForClass.size() == 1; + } + if (classesWithoutAllocationSiteTracking.add(clazz)) { + Set<DexEncodedMethod> allocationSitesForClass = + classesWithAllocationSiteTracking.remove(clazz); + return allocationSitesForClass == null; + } + return false; + } + + public boolean recordInstantiatedInterface(DexProgramClass iface, AppInfo appInfo) { + assert iface.isInterface(); + assert !iface.isAnnotation(); + if (interfacesWithUnknownSubtypeHierarchy.add(iface)) { + populateInstantiatedHierarchy(appInfo, iface); + return true; + } + return false; + } + + public void recordInstantiatedLambdaInterface( + DexType iface, LambdaDescriptor lambda, AppInfo appInfo) { + instantiatedLambdas.computeIfAbsent(iface, key -> new ArrayList<>()).add(lambda); + populateInstantiatedHierarchy(appInfo, iface); + } + + private void repopulateInstantiatedHierarchy(DexDefinitionSupplier definitions) { + instantiatedHierarchy = new IdentityHashMap<>(); + classesWithAllocationSiteTracking + .keySet() + .forEach(clazz -> populateInstantiatedHierarchy(definitions, clazz)); + classesWithoutAllocationSiteTracking.forEach( + clazz -> populateInstantiatedHierarchy(definitions, clazz)); + interfacesWithUnknownSubtypeHierarchy.forEach( + clazz -> populateInstantiatedHierarchy(definitions, clazz)); + instantiatedLambdas + .keySet() + .forEach(type -> populateInstantiatedHierarchy(definitions, type)); + } + + private void populateInstantiatedHierarchy(DexDefinitionSupplier definitions, DexType type) { + DexClass clazz = definitions.definitionFor(type); + if (clazz != null) { + populateInstantiatedHierarchy(definitions, clazz); + } + } + + private void populateInstantiatedHierarchy(DexDefinitionSupplier definitions, DexClass clazz) { + if (clazz.superType != null) { + populateInstantiatedHierarchy(definitions, clazz.superType, clazz); + } + for (DexType iface : clazz.interfaces.values) { + populateInstantiatedHierarchy(definitions, iface, clazz); + } + } + + private void populateInstantiatedHierarchy( + DexDefinitionSupplier definitions, DexType type, DexClass subtype) { + if (type == definitions.dexItemFactory().objectType) { + return; + } + Set<DexClass> subtypes = instantiatedHierarchy.get(type); + if (subtypes != null) { + subtypes.add(subtype); + return; + } + // This is the first time an instantiation appears below 'type', recursively populate. + subtypes = Sets.newIdentityHashSet(); + subtypes.add(subtype); + instantiatedHierarchy.put(type, subtypes); + populateInstantiatedHierarchy(definitions, type); + } + + public void markNoLongerInstantiated(DexProgramClass clazz) { + classesWithAllocationSiteTracking.remove(clazz); + classesWithoutAllocationSiteTracking.remove(clazz); + instantiatedHierarchy = null; + } + + Builder rewrittenWithLens( + ObjectAllocationInfoCollectionImpl objectAllocationInfos, + DexDefinitionSupplier definitions, + GraphLense lens) { + instantiatedHierarchy = null; + objectAllocationInfos.classesWithAllocationSiteTracking.forEach( + (clazz, allocationSitesForClass) -> { + DexType type = lens.lookupType(clazz.type); + if (type.isPrimitiveType()) { + assert !objectAllocationInfos.hasInstantiatedStrictSubtype(clazz); + return; + } + DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type)); + assert rewrittenClass != null; + assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass); + classesWithAllocationSiteTracking.put( + rewrittenClass, + LensUtils.rewrittenWithRenamedSignature( + allocationSitesForClass, definitions, lens)); + }); + objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach( + clazz -> { + DexType type = lens.lookupType(clazz.type); + if (type.isPrimitiveType()) { + assert !objectAllocationInfos.hasInstantiatedStrictSubtype(clazz); + return; + } + DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type)); + assert rewrittenClass != null; + assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass); + assert !classesWithoutAllocationSiteTracking.contains(rewrittenClass); + classesWithoutAllocationSiteTracking.add(rewrittenClass); + }); + for (DexProgramClass abstractType : + objectAllocationInfos.interfacesWithUnknownSubtypeHierarchy) { + DexType type = lens.lookupType(abstractType.type); + if (type.isPrimitiveType()) { + assert false; + continue; + } + DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type)); + assert rewrittenClass != null; + assert !interfacesWithUnknownSubtypeHierarchy.contains(rewrittenClass); + interfacesWithUnknownSubtypeHierarchy.add(rewrittenClass); + } + objectAllocationInfos.instantiatedLambdas.forEach( + (iface, lambdas) -> { + DexType type = lens.lookupType(iface); + if (type.isPrimitiveType()) { + assert false; + return; + } + assert !instantiatedLambdas.containsKey(type); + // TODO(b/150277553): Rewrite lambda descriptor. + instantiatedLambdas.put(type, lambdas); + }); + return this; + } + + // Validation that all types are linked in the instantiated hierarchy map. + boolean validate(DexDefinitionSupplier definitions) { + classesWithAllocationSiteTracking.forEach( + (clazz, contexts) -> { + assert !clazz.isInterface(); + assert !classesWithoutAllocationSiteTracking.contains(clazz); + assert verifyAllSuperTypesAreInHierarchy(definitions, clazz.allImmediateSupertypes()); + }); + classesWithoutAllocationSiteTracking.forEach( + clazz -> { + assert !clazz.isInterface(); + assert !classesWithAllocationSiteTracking.containsKey(clazz); + assert verifyAllSuperTypesAreInHierarchy(definitions, clazz.allImmediateSupertypes()); + }); + instantiatedLambdas.forEach( + (iface, lambdas) -> { + assert !lambdas.isEmpty(); + DexClass definition = definitions.definitionFor(iface); + if (definition != null) { + assert definition.isInterface(); + assert verifyAllSuperTypesAreInHierarchy( + definitions, definition.allImmediateSupertypes()); + } + }); + for (DexProgramClass iface : interfacesWithUnknownSubtypeHierarchy) { + verifyAllSuperTypesAreInHierarchy(definitions, iface.allImmediateSupertypes()); + } + instantiatedHierarchy.forEach( + (type, subtypes) -> { + assert !subtypes.isEmpty(); + for (DexClass subtype : subtypes) { + assert isImmediateSuperType(type, subtype); + } + }); + return true; + } + + private boolean verifyAllSuperTypesAreInHierarchy( + DexDefinitionSupplier definitions, Iterable<DexType> dexTypes) { + for (DexType supertype : dexTypes) { + assert typeIsInHierarchy(definitions, supertype); + } + return true; + } + + private boolean typeIsInHierarchy(DexDefinitionSupplier definitions, DexType type) { + return type == definitions.dexItemFactory().objectType + || instantiatedHierarchy.containsKey(type); + } + + private boolean isImmediateSuperType(DexType type, DexClass subtype) { + for (DexType supertype : subtype.allImmediateSupertypes()) { + if (type == supertype) { + return true; + } + } + return false; } } }
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java index e8cae8d..283777f 100644 --- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -45,6 +45,10 @@ return false; } + public boolean isIncompatibleClassChangeErrorResult() { + return false; + } + /** Returns non-null if isFailedResolution() is true, otherwise null. */ public FailedResolutionResult asFailedResolution() { return null; @@ -101,7 +105,7 @@ InstantiatedObject instance, AppInfoWithClassHierarchy appInfo); public abstract DexClassAndMethod lookupVirtualDispatchTarget( - DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo); + DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo); public abstract LookupTarget lookupVirtualDispatchTarget( LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo); @@ -119,12 +123,12 @@ assert initialResolutionHolder != null; assert resolvedHolder != null; assert resolvedMethod != null; - assert resolvedHolder.type == resolvedMethod.method.holder; + assert resolvedHolder.type == resolvedMethod.holder(); this.resolvedHolder = resolvedHolder; this.resolvedMethod = resolvedMethod; this.initialResolutionHolder = initialResolutionHolder; assert !resolvedMethod.isPrivateMethod() - || initialResolutionHolder.type == resolvedMethod.method.holder; + || initialResolutionHolder.type == resolvedMethod.holder(); } public DexClass getResolvedHolder() { @@ -324,7 +328,7 @@ // It appears as if this check is also in place for non-initializer methods too. // See NestInvokeSpecialMethodAccessWithIntermediateTest. if ((target.isInstanceInitializer() || target.isPrivateMethod()) - && target.method.holder != symbolicReference.type) { + && target.holder() != symbolicReference.type) { return null; } // Runtime exceptions: @@ -440,7 +444,7 @@ Consumer<DexProgramClass> lambdaInstantiatedConsumer = subType -> { subTypeConsumer.accept(subType); - if (appInfo.hasAnyInstantiatedLambdas(subType)) { + if (appInfo.isInstantiatedInterface(subType)) { hasInstantiatedLambdas.set(true); } }; @@ -516,7 +520,7 @@ @Override public DexClassAndMethod lookupVirtualDispatchTarget( - DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { + DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { return lookupVirtualDispatchTarget(dynamicInstance, appInfo, initialResolutionHolder.type); } @@ -542,9 +546,7 @@ } private DexClassAndMethod lookupVirtualDispatchTarget( - DexProgramClass dynamicInstance, - AppInfoWithClassHierarchy appInfo, - DexType resolutionHolder) { + DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo, DexType resolutionHolder) { assert appInfo.isSubtype(dynamicInstance.type, resolutionHolder) : dynamicInstance.type + " is not a subtype of " + resolutionHolder; // TODO(b/148591377): Enable this assertion. @@ -567,7 +569,7 @@ } if (candidate == null || candidate == DexEncodedMethod.SENTINEL) { // We cannot find a target above the resolved method. - if (current.type == overrideTarget.method.holder) { + if (current.type == overrideTarget.holder()) { return null; } current = current.superType == null ? null : appInfo.definitionFor(current.superType); @@ -584,7 +586,7 @@ } private DexClassAndMethod lookupMaximallySpecificDispatchTarget( - DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { + DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { return appInfo.lookupMaximallySpecificMethod(dynamicInstance, resolvedMethod.method); } @@ -646,7 +648,7 @@ } // For package private methods, a valid override has to be inside the package. assert resolvedMethod.accessFlags.isPackagePrivate(); - return resolvedMethod.method.holder.isSamePackage(candidate.method.holder); + return resolvedMethod.holder().isSamePackage(candidate.holder()); } } @@ -702,7 +704,7 @@ @Override public DexClassAndMethod lookupVirtualDispatchTarget( - DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { + DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { return null; } @@ -808,6 +810,11 @@ ? INSTANCE : new IncompatibleClassResult(methodsCausingError); } + + @Override + public boolean isIncompatibleClassChangeErrorResult() { + return true; + } } public static class NoSuchMethodResult extends FailedResolutionResult {
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java index fdb4dc1..061cbcf 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -127,7 +127,7 @@ } private boolean hasKotlincClinitAssertionCode(DexEncodedMethod method) { - if (method.method.holder == dexItemFactory.kotlin.kotlinAssertions) { + if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) { CfCode code = method.getCode().asCfCode(); for (int i = 1; i < code.instructions.size(); i++) { CfInstruction instruction = code.instructions.get(i - 1);
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java index f32143d..7ecd527 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -74,7 +74,7 @@ // Record that the enclosing class is guaranteed to be initialized at the allocation site. AppInfoWithSubtyping appInfo = appView.appInfo(); - DexType guaranteedToBeInitialized = context.method.holder; + DexType guaranteedToBeInitialized = context.holder(); DexType existingGuaranteedToBeInitialized = mapping.getOrDefault(key, guaranteedToBeInitialized); mapping.put(
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java index 237f9ab..944d680 100644 --- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java +++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -39,6 +39,10 @@ return mergedClasses.containsKey(type); } + public boolean isTarget(DexType type) { + return !getSourcesFor(type).isEmpty(); + } + @Override public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) { for (List<DexType> sourcesForTarget : sources.values()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java index 7167e72..94ee9cf 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -97,7 +97,7 @@ } public boolean isClassDefinitelyLoadedBeforeInstruction(DexType type, Instruction instruction) { - DexType context = code.method.method.holder; + DexType context = code.method.holder(); BasicBlock block = instruction.getBlock(); // Visit the instructions in `block` prior to `instruction`. @@ -335,7 +335,7 @@ if (!resolutionResult.isSingleResolution()) { return false; } - DexType holder = resolutionResult.getSingleTarget().method.holder; + DexType holder = resolutionResult.getSingleTarget().holder(); return appView.isSubtype(holder, type).isTrue(); } @@ -394,7 +394,7 @@ if (!resolutionResult.isSingleResolution()) { return false; } - DexType holder = resolutionResult.getSingleTarget().method.holder; + DexType holder = resolutionResult.getSingleTarget().holder(); return appView.isSubtype(holder, type).isTrue(); } @@ -430,7 +430,7 @@ if (!resolutionResult.isSingleResolution()) { return false; } - DexType holder = resolutionResult.getSingleTarget().method.holder; + DexType holder = resolutionResult.getSingleTarget().holder(); return appView.isSubtype(holder, type).isTrue(); } @@ -505,11 +505,11 @@ enqueue(clazz.type, visited, worklist); } else if (definition.isDexEncodedField()) { DexEncodedField field = definition.asDexEncodedField(); - enqueue(field.field.holder, visited, worklist); + enqueue(field.holder(), visited, worklist); } else if (definition.isDexEncodedMethod()) { assert instruction.isInvokeMethod(); DexEncodedMethod method = definition.asDexEncodedMethod(); - enqueue(method.method.holder, visited, worklist); + enqueue(method.holder(), visited, worklist); enqueueInitializedClassesOnNormalExit(method, instruction.inValues(), visited, worklist); } else { assert false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java index 0fc8bd0..9ae6c86 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
@@ -37,7 +37,7 @@ } if (instr.isInvokeMethod()) { DexEncodedMethod target = - instr.asInvokeMethod().lookupSingleTarget(appView, code.method.method.holder); + instr.asInvokeMethod().lookupSingleTarget(appView, code.method.holder()); if (target != null && target.getOptimizationInfo().returnValueOnlyDependsOnArguments()) { continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java index f5cd48d..406c9e6 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -36,7 +36,7 @@ public static Set<DexType> computeInitializedClassesOnNormalExit( AppView<AppInfoWithLiveness> appView, IRCode code) { DominatorTree dominatorTree = new DominatorTree(code, Assumption.MAY_HAVE_UNREACHABLE_BLOCKS); - Visitor visitor = new Visitor(appView, code.method.method.holder); + Visitor visitor = new Visitor(appView, code.method.holder()); for (BasicBlock dominator : dominatorTree.normalExitDominatorBlocks()) { if (dominator.hasCatchHandlers()) { // When determining which classes that are guaranteed to be initialized from a given @@ -115,8 +115,8 @@ public Void handleFieldInstruction(FieldInstruction instruction) { DexEncodedField field = appView.appInfo().resolveField(instruction.getField()); if (field != null) { - if (field.field.holder.isClassType()) { - markInitializedOnNormalExit(field.field.holder); + if (field.holder().isClassType()) { + markInitializedOnNormalExit(field.holder()); } else { assert false : "Expected holder of field type to be a class type"; } @@ -132,7 +132,7 @@ if (method.holder.isClassType()) { DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context); if (singleTarget != null) { - markInitializedOnNormalExit(singleTarget.method.holder); + markInitializedOnNormalExit(singleTarget.holder()); markInitializedOnNormalExit( singleTarget.getOptimizationInfo().getInitializedClassesOnNormalExit()); } else {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java index 9fcee4a..05416df 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -84,7 +84,7 @@ public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) { this.appView = appView; this.code = code; - this.context = code.method.method.holder; + this.context = code.method.holder(); } public boolean valueMayDependOnEnvironment(Value value) { @@ -433,7 +433,7 @@ if (definition.isStaticGet()) { StaticGet staticGet = definition.asStaticGet(); DexEncodedField field = appView.appInfo().resolveField(staticGet.getField()); - if (field != null && field.field.holder == context) { + if (field != null && field.holder() == context) { List<StaticPut> finalFieldPuts = computeFinalFieldPuts().get(field); if (finalFieldPuts == null || finalFieldPuts.size() != 1) { return false; @@ -452,7 +452,7 @@ finalFieldPuts = new IdentityHashMap<>(); for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) { DexEncodedField field = appView.appInfo().resolveField(staticPut.getField()); - if (field != null && field.field.holder == context && field.isFinal()) { + if (field != null && field.holder() == context && field.isFinal()) { finalFieldPuts.computeIfAbsent(field, ignore -> new ArrayList<>()).add(staticPut); } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java index d5bd10a..75d5423 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -148,7 +148,7 @@ return; } - DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.holder()); if (singleTarget == null) { // We just lost track. abstractInstanceFieldValues.remove(clazz); @@ -173,7 +173,7 @@ initializationInfo.asArgumentInitializationInfo(); Value argument = invoke.arguments().get(argumentInitializationInfo.getArgumentIndex()); AbstractValue abstractValue = - entry.getValue().join(argument.getAbstractValue(appView, context.method.holder)); + entry.getValue().join(argument.getAbstractValue(appView, context.holder())); assert !abstractValue.isBottom(); if (!abstractValue.isUnknown()) { entry.setValue(abstractValue); @@ -294,7 +294,7 @@ if (abstractValue.isUnknown()) { return true; } - assert abstractValue == value.getAbstractValue(appView, context.method.holder); + assert abstractValue == value.getAbstractValue(appView, context.holder()); return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java index af990b1..08989c0 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -49,7 +49,7 @@ DexProgramClass clazz, DexEncodedMethod method) { assert clazz != null; - assert clazz.type == method.method.holder; + assert clazz.type == method.holder(); this.appView = appView; this.clazz = clazz; this.code = code; @@ -129,7 +129,7 @@ // Then check if any of the instructions that precede the given instruction in the current block // may read the field. - DexType context = method.method.holder; + DexType context = method.holder(); InstructionIterator instructionIterator = block.iterator(); while (instructionIterator.hasNext()) { Instruction current = instructionIterator.next(); @@ -164,7 +164,7 @@ * and its transitive predecessors. */ private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() { - DexType context = method.method.holder; + DexType context = method.holder(); Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>(); Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock()); while (!worklist.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java index 3aec803..58b4194 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -86,7 +86,7 @@ assert appView.enableWholeProgramOptimizations(); assert method.isInstanceInitializer(); - DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass(); + DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass(); if (!appView.options().enableValuePropagationForInstanceFields) { return EmptyInstanceFieldInitializationInfoCollection.getInstance(); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java index 4d57a5e..e2ab677 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -53,7 +53,7 @@ assert appView.enableWholeProgramOptimizations(); assert method.isClassInitializer(); timing.begin("Analyze class initializer"); - DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass(); + DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass(); new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method) .computeFieldOptimizationInfo(classInitializerDefaultsResult); timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java index 2a70440..08b29ce 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -130,7 +130,7 @@ */ public void rewriteCode(DexEncodedMethod method, IRCode code) { if (method.isClassInitializer() - && classesWithRemovedExtensionFields.contains(method.method.holder) + && classesWithRemovedExtensionFields.contains(method.holder()) && code.metadata().mayHaveStaticPut()) { rewriteClassInitializer(code); } @@ -204,7 +204,7 @@ return false; } - DexClass clazz = appView.definitionFor(encodedField.field.holder); + DexClass clazz = appView.definitionFor(encodedField.holder()); if (clazz == null || !clazz.isProgramClass()) { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java index feca06c..0801f16 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -36,6 +36,7 @@ import com.android.tools.r8.ir.optimize.inliner.FixedInliningReasonStrategy; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.Enqueuer; import com.android.tools.r8.utils.PredicateSet; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; @@ -64,11 +65,16 @@ /** Returns true if an action was deferred. */ public boolean deferDeadProtoBuilders( - DexProgramClass clazz, DexEncodedMethod context, BooleanSupplier register) { + DexProgramClass clazz, + DexEncodedMethod context, + BooleanSupplier register, + Enqueuer enqueuer) { if (references.isDynamicMethod(context) && references.isGeneratedMessageLiteBuilder(clazz)) { if (register.getAsBoolean()) { - assert builders.getOrDefault(clazz, context) == context; - builders.put(clazz, context); + if (enqueuer.getMode().isFinalTreeShaking()) { + assert builders.getOrDefault(clazz, context) == context; + builders.put(clazz, context); + } return true; } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java index 390f2b2..b2d813a 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -99,7 +99,7 @@ * newMessageInfo is still pending. */ private void rewriteDynamicMethod(DexEncodedMethod method, IRCode code) { - DexClass context = appView.definitionFor(method.method.holder); + DexClass context = appView.definitionFor(method.holder()); if (context == null || !context.isProgramClass()) { return; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java index 71750f1..79dea35 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -43,6 +43,7 @@ @Override public boolean registerConstClass(DexType type) { if (references.isDynamicMethod(getContextMethod())) { + enqueuer.addDeadProtoTypeCandidate(type); return false; } return super.registerConstClass(type); @@ -58,6 +59,7 @@ @Override public boolean registerStaticFieldRead(DexField field) { if (references.isDynamicMethod(getContextMethod())) { + enqueuer.addDeadProtoTypeCandidate(field.holder); return false; } return super.registerStaticFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java index de37f38..1321984 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
@@ -36,7 +36,7 @@ @Override public Reason computeInliningReason( InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { - DexProgramClass enclosingClass = appView.definitionFor(context.method.holder).asProgramClass(); + DexProgramClass enclosingClass = appView.definitionFor(context.holder()).asProgramClass(); if (references.isAbstractGeneratedMessageLiteBuilder(enclosingClass) && invoke.isInvokeSuper()) { // Aggressively inline invoke-super calls inside the GeneratedMessageLite builders. Such
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java index e414f23..d6d2990 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -135,7 +135,7 @@ return; } - DexType holder = encodedMethod.method.holder; + DexType holder = encodedMethod.holder(); if (seenButNotLiveProtos.containsKey(holder)) { // The proto is now live instead of dead. liveProtos.put(holder, seenButNotLiveProtos.remove(holder)); @@ -150,7 +150,7 @@ private void createProtoMessageInfoFromDynamicMethod( DexEncodedMethod dynamicMethod, Map<DexType, ProtoMessageInfo> protos) { - DexType holder = dynamicMethod.method.holder; + DexType holder = dynamicMethod.holder(); assert !protos.containsKey(holder); DexClass context = appView.definitionFor(holder); @@ -254,7 +254,7 @@ for (DexEncodedMethod findLiteExtensionByNumberMethod : findLiteExtensionByNumberMethods) { IRCode code = findLiteExtensionByNumberMethod.buildIR( - appView, appView.appInfo().originFor(findLiteExtensionByNumberMethod.method.holder)); + appView, appView.appInfo().originFor(findLiteExtensionByNumberMethod.holder())); for (BasicBlock block : code.blocks(BasicBlock::isReturnBlock)) { Value returnValue = block.exit().asReturn().returnValue().getAliasedValue(); if (returnValue.isPhi()) { @@ -275,7 +275,7 @@ continue; } - DexProgramClass holder = asProgramClassOrNull(appView.definitionFor(field.field.holder)); + DexProgramClass holder = asProgramClassOrNull(appView.definitionFor(field.holder())); if (holder == null) { assert false; continue; @@ -365,7 +365,7 @@ } DexEncodedMethod dynamicMethod = protoMessageInfo.getDynamicMethod(); - DexProgramClass clazz = appView.definitionFor(dynamicMethod.method.holder).asProgramClass(); + DexProgramClass clazz = appView.definitionFor(dynamicMethod.holder()).asProgramClass(); for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) { DexEncodedField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo); @@ -526,7 +526,7 @@ return; } - DexClass clazz = appView.definitionFor(encodedOneOfCaseField.field.holder); + DexClass clazz = appView.definitionFor(encodedOneOfCaseField.holder()); if (clazz == null || !clazz.isProgramClass()) { assert false; return; @@ -555,7 +555,7 @@ return; } - if (encodedOneOfField.field.holder != encodedOneOfCaseField.field.holder) { + if (encodedOneOfField.holder() != encodedOneOfCaseField.holder()) { assert false; return; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java index ac88305..f026295 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
@@ -220,7 +220,7 @@ } public DexType getType() { - return dynamicMethod.method.holder; + return dynamicMethod.holder(); } public boolean hasFields() {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java index 3449c30..0d500e9 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -39,7 +39,7 @@ */ public static ClassInitializerSideEffect classInitializerCanBePostponed( AppView<?> appView, IRCode code) { - DexType context = code.method.method.holder; + DexType context = code.method.holder(); OptionalBool controlFlowMayDependOnEnvironment = OptionalBool.unknown(); boolean mayHaveSideEffects = false; @@ -113,7 +113,7 @@ StaticPut staticPut = instruction.asStaticPut(); DexEncodedField field = appView.appInfo().resolveField(staticPut.getField()); if (field == null - || field.field.holder != context + || field.holder() != context || environmentAnalysis.valueMayDependOnEnvironment(staticPut.value()) || instruction.instructionInstanceCanThrow(appView, context).isThrowing()) { return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java index 34a83d1..3f5640d 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -72,7 +72,7 @@ Value returnedValue = code.createValue(classClassType(appView, definitelyNotNull()), debugLocalInfo); ConstClass instruction = new ConstClass(returnedValue, type); - assert !instruction.instructionMayHaveSideEffects(appView, code.method.method.holder); + assert !instruction.instructionMayHaveSideEffects(appView, code.method.holder()); return instruction; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java index ef38879..855694a 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -85,7 +85,7 @@ public boolean isMaterializableInContext(AppView<?> appView, DexType context) { DexEncodedField encodedField = appView.appInfo().resolveField(field); return isMemberVisibleFromOriginalContext( - appView, context, encodedField.field.holder, encodedField.accessFlags); + appView, context, encodedField.holder(), encodedField.accessFlags); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java index dc0b133..0994dc0 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -90,7 +90,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java index f4a8a14..cd29ec9 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -192,7 +192,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java index 6611bc7..9fb9f27 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Assume.java +++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -279,7 +279,7 @@ assert super.verifyTypes(appView); TypeElement inType = src().getType(); - TypeElement outType = outValue().getType(); + TypeElement outType = getOutType(); if (isAssumeNone() || isAssumeDynamicType()) { assert inType.isReferenceType() : inType; assert outType.equals(inType)
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java index 37d9b9e..52aed30 100644 --- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java +++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -540,6 +540,10 @@ return phis; } + public boolean isEntry() { + return getPredecessors().isEmpty(); + } + public boolean isFilled() { return filled; } @@ -1750,7 +1754,7 @@ if (hasMoveException) { // Remove the move-exception instruction. move = entry().asMoveException(); - exceptionTypeLattice = move.outValue().getType(); + exceptionTypeLattice = move.getOutType(); exceptionType = move.getExceptionType(); assert move.getDebugValues().isEmpty(); getInstructions().remove(0);
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java index 4e5c923..1537f85 100644 --- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java +++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -228,7 +228,7 @@ throw new IllegalStateException(); } - assert !current.hasOutValue() || current.outValue().getType().isInt(); + assert !current.hasOutValue() || current.getOutType().isInt(); // Replace the instruction by const-number. ConstNumber constNumber = code.createIntConstant(value, current.getLocalInfo()); @@ -250,7 +250,7 @@ // Replace the instruction by static-get. TypeElement newType = TypeElement.fromDexType(field.type, maybeNull(), appView); - TypeElement oldType = current.hasOutValue() ? current.outValue().getType() : null; + TypeElement oldType = current.getOutType(); Value value = code.createValue(newType, current.getLocalInfo()); StaticGet staticGet = new StaticGet(value, field); for (Value inValue : current.inValues()) { @@ -495,8 +495,8 @@ Set<BasicBlock> blocksToRemove, DexType downcast) { assert blocksToRemove != null; - DexType codeHolder = code.method.method.holder; - DexType inlineeHolder = inlinee.method.method.holder; + DexType codeHolder = code.method.holder(); + DexType inlineeHolder = inlinee.method.holder(); if (codeHolder != inlineeHolder && inlinee.method.isOnlyInlinedIntoNestMembers()) { // Should rewrite private calls to virtual calls. assert NestUtils.sameNest(codeHolder, inlineeHolder, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java index 2d7e409..da952bd 100644 --- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java +++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -171,7 +171,7 @@ assert inType.isPreciseType(); - TypeElement outType = outValue().getType(); + TypeElement outType = getOutType(); TypeElement castType = TypeElement.fromDexType(getType(), inType.nullability(), appView); if (inType.lessThanOrEqual(castType, appView)) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java index e77f315..bb5788b 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -44,10 +44,7 @@ public static ConstClass copyOf(IRCode code, ConstClass original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); } @@ -142,7 +139,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java index 996bf38..a390b3f 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -38,10 +38,7 @@ public static ConstMethodHandle copyOf(IRCode code, ConstMethodHandle original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java index 6cd4d0b..774475d 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -38,10 +38,7 @@ public static ConstMethodType copyOf(IRCode code, ConstMethodType original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java index 2683b66..06aef0a 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -55,10 +55,7 @@ public static ConstNumber copyOf(IRCode code, ConstNumber original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); } @@ -251,7 +248,7 @@ @Override public String toString() { if (outValue != null) { - return super.toString() + " " + value + " (" + outValue().getType() + ")"; + return super.toString() + " " + value + " (" + getOutType() + ")"; } else { return super.toString() + " " + value + " (dead)"; } @@ -316,13 +313,13 @@ @Override public TypeElement evaluate(AppView<?> appView) { - return outValue().getType(); + return getOutType(); } @Override public boolean verifyTypes(AppView<?> appView) { assert super.verifyTypes(appView); - assert !isZero() || outValue().getType().isPrimitiveType() || outValue().getType().isNullType(); + assert !isZero() || getOutType().isPrimitiveType() || getOutType().isNullType(); return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java index c98d8b7..a90a67e 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -42,10 +42,7 @@ public static ConstString copyOf(IRCode code, ConstString original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java index 5884660..f8a36db 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java +++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -79,7 +79,7 @@ @Override public boolean verifyTypes(AppView<?> appView) { super.verifyTypes(appView); - assert src().getType().lessThanOrEqual(outValue().getType(), appView); + assert src().getType().lessThanOrEqual(getOutType(), appView); return true; } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java index 70ebd80..bc29749 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -48,10 +48,7 @@ public static DexItemBasedConstString copyOf(IRCode code, DexItemBasedConstString original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java index d7e0067..18f8326 100644 --- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java +++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -232,7 +232,7 @@ assert isFieldGet(); DexEncodedField field = appView.appInfo().resolveField(getField()); if (field != null) { - DexClass holder = appView.definitionFor(field.field.holder); + DexClass holder = appView.definitionFor(field.holder()); if (holder != null && holder.isLibraryClass() && field.isStatic() && field.isFinal()) { return appView.abstractValueFactory().createSingleFieldValue(field.field); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java index 5c488c4..e46af2c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -116,6 +116,10 @@ return result; } + public boolean mayHaveInitClass() { + return get(Opcodes.INIT_CLASS); + } + public boolean mayHaveInstanceGet() { return get(Opcodes.INSTANCE_GET); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java index da0592d..42068de 100644 --- a/src/main/java/com/android/tools/r8/ir/code/If.java +++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -199,14 +199,14 @@ public BasicBlock targetFromCondition(ConstNumber value) { assert isZeroTest(); - assert verifyTypeCompatible(value.outValue().getType(), type); + assert verifyTypeCompatible(value.getOutType(), type); return targetFromCondition(Long.signum(value.getRawValue())); } public BasicBlock targetFromCondition(ConstNumber left, ConstNumber right) { assert !isZeroTest(); assert left.outType() == right.outType(); - assert verifyTypeCompatible(left.outValue().getType(), type); + assert verifyTypeCompatible(left.getOutType(), type); return targetFromCondition(Long.signum(left.getRawValue() - right.getRawValue())); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java index 6edc6da..0740db4 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -127,7 +127,7 @@ // * IncompatibleClassChangeError (instance-* instruction for static fields) // * IllegalAccessError (not visible from the access context) // * NullPointerException (null receiver) - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java index 116a3e6..58bcd28 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java +++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -142,7 +142,7 @@ // * IllegalAccessError (not visible from the access context) // * NullPointerException (null receiver) // * not read at all - boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.method.holder); + boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.holder()); assert appView.enableWholeProgramOptimizations() || haveSideEffects : "Expected instance-put instruction to have side effects in D8"; return !haveSideEffects;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java index 654c2e8..b280728 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java +++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -593,7 +593,7 @@ /** Returns true is this instruction can be treated as dead code if its outputs are not used. */ public boolean canBeDeadCode(AppView<?> appView, IRCode code) { // TODO(b/129530569): instructions with fine-grained side effect analysis may use: - // return !instructionMayHaveSideEffects(appView, code.method.method.holder); + // return !instructionMayHaveSideEffects(appView, code.method.holder()); return !instructionInstanceCanThrow(); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java index ddc8952..d6aaf36 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -190,12 +190,12 @@ // Verify that the target method is accessible in the current context. if (!isMemberVisibleFromOriginalContext( - appView, context, target.method.holder, target.accessFlags)) { + appView, context, target.holder(), target.accessFlags)) { return true; } // Verify that the target method does not have side-effects. - DexClass clazz = appView.definitionFor(target.method.holder); + DexClass clazz = appView.definitionFor(target.holder()); if (clazz == null) { assert false : "Expected to be able to find the enclosing class of a method definition"; return true; @@ -218,7 +218,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { DexEncodedMethod method = code.method; - if (instructionMayHaveSideEffects(appView, method.method.holder)) { + if (instructionMayHaveSideEffects(appView, method.holder())) { return false; } @@ -236,7 +236,7 @@ if (appView.dexItemFactory().isConstructor(invoke.getInvokedMethod()) && invoke.getReceiver() == getReceiver()) { // If another constructor call than `this` is found, then it must not have side effects. - if (invoke.instructionMayHaveSideEffects(appView, method.method.holder)) { + if (invoke.instructionMayHaveSideEffects(appView, method.holder())) { return false; } if (otherInitCalls == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java index 9b708bc..63f6a89 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -118,9 +118,7 @@ methodTarget -> { DexEncodedMethod target = methodTarget.getMethod(); if (target == refinedTarget - || appView - .isSubtype(target.method.holder, refinedReceiverType) - .isPossiblyTrue()) { + || appView.isSubtype(target.holder(), refinedReceiverType).isPossiblyTrue()) { result.add(target); } },
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java index a71d185..41fac8c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -184,7 +184,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java index 7992a1c..b285dd6 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -197,7 +197,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java index 2d29972..01a6a76 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -183,7 +183,7 @@ // Verify that the target method is accessible in the current context. if (!isMemberVisibleFromOriginalContext( - appView, context, target.method.holder, target.accessFlags)) { + appView, context, target.holder(), target.accessFlags)) { return true; } @@ -200,12 +200,14 @@ return false; } - return target.method.holder.classInitializationMayHaveSideEffects( - appView, - // Types that are a super type of `context` are guaranteed to be initialized - // already. - type -> appView.isSubtype(context, type).isTrue(), - Sets.newIdentityHashSet()); + return target + .holder() + .classInitializationMayHaveSideEffects( + appView, + // Types that are a super type of `context` are guaranteed to be initialized + // already. + type -> appView.isSubtype(context, type).isTrue(), + Sets.newIdentityHashSet()); } return true; @@ -213,6 +215,6 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java index 711c911..c30a8cb 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -180,7 +180,7 @@ // Verify that the target method is accessible in the current context. if (!isMemberVisibleFromOriginalContext( - appView, context, target.method.holder, target.accessFlags)) { + appView, context, target.holder(), target.accessFlags)) { return true; } @@ -200,6 +200,6 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java index adcc848..24e831c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Move.java +++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -73,7 +73,7 @@ @Override public String toString() { - return super.toString() + " (" + outValue().getType() + ")"; + return super.toString() + " (" + getOutType() + ")"; } @Override @@ -128,7 +128,7 @@ super.verifyTypes(appView); // DebugLocalWrite defines it's own verification of types but should be allowed to call super. if (!this.isDebugLocalWrite()) { - assert src().getType().equals(outValue().getType()); + assert src().getType().equals(getOutType()); } return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java index 2211a59..87643df 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -151,6 +151,6 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java index d8acd0b..1090554 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -196,7 +196,7 @@ @Override public boolean canBeDeadCode(AppView<?> appView, IRCode code) { - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } public void markNoSpilling() { @@ -226,7 +226,7 @@ @Override public boolean verifyTypes(AppView<?> appView) { - TypeElement type = outValue().getType(); + TypeElement type = getOutType(); assert type.isClassType(); assert type.asClassType().getClassType() == clazz || appView.options().testing.allowTypeErrors; assert type.isDefinitelyNotNull();
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java index 2c246f92..84a86f2 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -38,10 +38,7 @@ public static StaticGet copyOf(IRCode code, StaticGet original) { Value newValue = - new Value( - code.valueNumberGenerator.next(), - original.outValue().getType(), - original.getLocalInfo()); + new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo()); return copyOf(newValue, original); } @@ -156,7 +153,7 @@ // * IncompatibleClassChangeError (static-* instruction for instance fields) // * IllegalAccessError (not visible from the access context) // * side-effects in <clinit> - return !instructionMayHaveSideEffects(appView, code.method.method.holder); + return !instructionMayHaveSideEffects(appView, code.method.holder()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java index 16c102b..7249d94 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -139,7 +139,7 @@ // * IllegalAccessError (not visible from the access context) // * side-effects in <clinit> // * not read _globally_ - boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.method.holder); + boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.holder()); assert appView.enableWholeProgramOptimizations() || haveSideEffects : "Expected static-put instruction to have side effects in D8"; return !haveSideEffects;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java index e84f64a..57c923c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Value.java +++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -826,7 +826,7 @@ builder.append("("); if (isConstant && definition.asConstNumber().outValue != null) { ConstNumber constNumber = definition.asConstNumber(); - if (constNumber.outValue().getType().isSinglePrimitive()) { + if (constNumber.getOutType().isSinglePrimitive()) { builder.append((int) constNumber.getRawValue()); } else { builder.append(constNumber.getRawValue());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java index 6ff7e2f..18b62e6 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -178,7 +178,7 @@ if (singleTarget != null) { assert !source.accessFlags.isBridge() || singleTarget != currentMethod.method; DexProgramClass clazz = - asProgramClassOrNull(appView.definitionFor(singleTarget.method.holder)); + asProgramClassOrNull(appView.definitionFor(singleTarget.holder())); if (clazz != null) { // For static invokes, the class could be initialized. if (type == Invoke.Type.STATIC) { @@ -253,8 +253,7 @@ return; } - DexProgramClass clazz = - asProgramClassOrNull(appView.definitionFor(encodedField.field.holder)); + DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(encodedField.holder())); if (clazz == null) { return; }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java index ae3e7cd..74f24c8 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -328,7 +328,7 @@ } } return new CfCode( - method.method.holder, + method.holder(), stackHeightTracker.maxHeight, registerAllocator.registersUsed(), instructions, @@ -386,7 +386,7 @@ || constNumber == null || add == null || store == null - || constNumber.outValue().getType() != TypeElement.getInt()) { + || constNumber.getOutType() != TypeElement.getInt()) { it.next(); continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java index 1f1d2e5..f13653a 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -427,7 +427,7 @@ private void buildMethodEnterSynchronization(IRBuilder builder) { assert needsGeneratedMethodSynchronization; currentlyGeneratingMethodSynchronization = true; - DexType type = method.method.holder; + DexType type = method.holder(); int monitorRegister; if (isStatic()) { monitorRegister = state.push(type).register; @@ -633,7 +633,7 @@ return ((CfNew) instruction).getType(); } if (type.isUninitializedThis()) { - return method.method.holder; + return method.holder(); } assert type.isTop(); return null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java index a4ca85a..6e47281 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -1404,7 +1404,7 @@ @Override public void addInstructions(DexBuilder builder, List<Instruction> instructions) { Move move = getMove(); - TypeElement moveType = move.outValue().getType(); + TypeElement moveType = move.getOutType(); int src = srcRegister(builder); int dest = destRegister(builder); Instruction instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java index 993a69c..35250d1 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -530,7 +530,7 @@ int argumentIndex = 0; if (!method.isStatic()) { - writeCallback.accept(register, method.method.holder); + writeCallback.accept(register, method.holder()); addThisArgument(register); argumentIndex++; register++; @@ -932,7 +932,7 @@ void addThisArgument(int register) { boolean receiverCouldBeNull = context != null && context != method; Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull(); - TypeElement receiverType = TypeElement.fromDexType(method.method.holder, nullability, appView); + TypeElement receiverType = TypeElement.fromDexType(method.holder(), nullability, appView); addThisArgument(register, receiverType); } @@ -1463,14 +1463,14 @@ // therefore we use an invoke-direct instead. We need to do this as the Android Runtime // will not allow invoke-virtual of a private method. DexMethod invocationMethod = (DexMethod) item; - DexType holderType = method.method.holder; + DexType holderType = method.holder(); if (invocationMethod.holder == holderType) { DexClass holderClass = appView.definitionFor(holderType); assert holderClass != null && holderClass.isProgramClass(); if (holderClass != null) { DexEncodedMethod directTarget = holderClass.lookupDirectMethod(invocationMethod); if (directTarget != null && !directTarget.isStatic()) { - assert invocationMethod.holder == directTarget.method.holder; + assert invocationMethod.holder == directTarget.holder(); type = Type.DIRECT; } } @@ -2332,7 +2332,7 @@ private void addInstruction(Instruction ir, Position position) { assert verifyOutValueType(ir); - hasImpreciseValues |= ir.outValue() != null && !ir.outValue().getType().isPreciseType(); + hasImpreciseValues |= ir.outValue() != null && !ir.getOutType().isPreciseType(); ir.setPosition(position); attachLocalValues(ir); currentBlock.add(ir, metadata); @@ -2363,13 +2363,11 @@ } private boolean verifyOutValueType(Instruction ir) { - assert ir.outValue() == null - || ir.isArrayGet() - || ir.evaluate(appView) == ir.outValue().getType(); + assert ir.outValue() == null || ir.isArrayGet() || ir.evaluate(appView) == ir.getOutType(); assert ir.outValue() == null || !ir.isArrayGet() - || ir.evaluate(appView) == ir.outValue().getType() - || (ir.outValue().getType().isBottom() && ir.evaluate(appView).isReferenceType()); + || ir.evaluate(appView) == ir.getOutType() + || (ir.getOutType().isBottom() && ir.evaluate(appView).isReferenceType()); return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java index cae0c84..e29a1c1 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -94,6 +94,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis; import com.android.tools.r8.shaking.MainDexClasses; +import com.android.tools.r8.utils.Action; import com.android.tools.r8.utils.CfgPrinter; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.ExceptionUtils; @@ -181,7 +182,7 @@ OptimizationFeedbackSimple.getInstance(); private DexString highestSortingString; - private List<com.android.tools.r8.utils.Action> onWaveDoneActions = null; + private List<Action> onWaveDoneActions = null; private final List<DexString> neverMergePrefixes; boolean seenNotNeverMergePrefix = false; @@ -609,10 +610,10 @@ if (appView.options().enableNeverMergePrefixes) { for (DexString neverMergePrefix : neverMergePrefixes) { // Synthetic classes will always be merged. - if (method.method.holder.isD8R8SynthesizedClassType()) { + if (method.holder().isD8R8SynthesizedClassType()) { continue; } - if (method.method.holder.descriptor.startsWith(neverMergePrefix)) { + if (method.holder().descriptor.startsWith(neverMergePrefix)) { seenNeverMergePrefix = true; } else { seenNotNeverMergePrefix = true; @@ -904,7 +905,7 @@ ThreadUtils.processItems( methods, method -> { - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder)); + IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); assert code != null; assert !method.getCode().isOutlineCode(); // Instead of repeating all the optimizations of rewriteCode(), only run the @@ -928,7 +929,7 @@ } private void forEachSynthesizedServiceLoaderMethod(DexEncodedMethod method) { - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder)); + IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); assert code != null; codeRewriter.rewriteMoveResult(code); removeDeadCodeAndFinalizeIR( @@ -1006,7 +1007,7 @@ count++; result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name)); - } while (appView.definitionFor(result) != null); + } while (appView.appInfo().definitionForWithoutExistenceAssert(result) != null); return result; } @@ -1085,7 +1086,7 @@ private Timing rewriteCode( DexEncodedMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) { - Origin origin = appView.appInfo().originFor(method.method.holder); + Origin origin = appView.appInfo().originFor(method.holder()); return ExceptionUtils.withOriginAttachmentHandler( origin, new MethodPosition(method.method), @@ -1147,6 +1148,10 @@ codeRewriter.simplifyDebugLocals(code); } + if (enumUnboxer != null && methodProcessor.isPost()) { + enumUnboxer.rewriteCode(code); + } + if (appView.graphLense().hasCodeRewritings()) { assert lensCodeRewriter != null; timing.begin("Lens rewrite"); @@ -1154,10 +1159,6 @@ timing.end(); } - if (enumUnboxer != null && methodProcessor.isPost()) { - enumUnboxer.rewriteCode(code); - } - if (method.isProcessed()) { assert !appView.enableWholeProgramOptimizations() || !appView.appInfo().withLiveness().neverReprocess.contains(method.method); @@ -1215,7 +1216,7 @@ if (memberValuePropagation != null) { timing.begin("Propagate member values"); - memberValuePropagation.rewriteWithConstantValues(code, method.method.holder); + memberValuePropagation.rewriteWithConstantValues(code, method.holder()); timing.end(); } @@ -1284,7 +1285,7 @@ if (devirtualizer != null) { assert code.verifyTypes(appView); timing.begin("Devirtualize invoke interface"); - devirtualizer.devirtualizeInvokeInterface(code, method.method.holder); + devirtualizer.devirtualizeInvokeInterface(code, method.holder()); timing.end(); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java index bb541cf..7daa4a1 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -7,6 +7,27 @@ import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY; import static com.android.tools.r8.ir.code.Invoke.Type.STATIC; import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL; +import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST; +import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS; +import static com.android.tools.r8.ir.code.Opcodes.CONST_METHOD_HANDLE; +import static com.android.tools.r8.ir.code.Opcodes.INIT_CLASS; +import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET; +import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_OF; +import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM; +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_MULTI_NEW_ARRAY; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_POLYMORPHIC; +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.MOVE_EXCEPTION; +import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY; +import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE; +import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET; +import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppInfoWithSubtyping; @@ -86,7 +107,7 @@ private Value makeOutValue(Instruction insn, IRCode code) { if (insn.outValue() != null) { - TypeElement oldType = insn.outValue().getType(); + TypeElement oldType = insn.getOutType(); TypeElement newType = oldType.fixupClassTypeReferences(appView.graphLense()::lookupType, appView); return code.createValue(newType, insn.getLocalInfo()); @@ -114,291 +135,370 @@ InstructionListIterator iterator = block.listIterator(code); while (iterator.hasNext()) { Instruction current = iterator.next(); - if (current.isInvokeCustom()) { - InvokeCustom invokeCustom = current.asInvokeCustom(); - DexCallSite callSite = invokeCustom.getCallSite(); - DexCallSite newCallSite = rewriteCallSite(callSite, method); - if (newCallSite != callSite) { - Value newOutValue = makeOutValue(invokeCustom, code); - InvokeCustom newInvokeCustom = - new InvokeCustom(newCallSite, newOutValue, invokeCustom.inValues()); - iterator.replaceCurrentInstruction(newInvokeCustom); - if (newOutValue != null && newOutValue.getType() != invokeCustom.outValue().getType()) { - affectedPhis.addAll(newOutValue.uniquePhiUsers()); - } - } - } else if (current.isConstMethodHandle()) { - DexMethodHandle handle = current.asConstMethodHandle().getValue(); - DexMethodHandle newHandle = rewriteDexMethodHandle( - handle, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY); - if (newHandle != handle) { - Value newOutValue = makeOutValue(current, code); - iterator.replaceCurrentInstruction(new ConstMethodHandle(newOutValue, newHandle)); - if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) { - affectedPhis.addAll(newOutValue.uniquePhiUsers()); - } - } - } else if (current.isInitClass()) { - InitClass initClass = current.asInitClass(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - initClass.getClassValue(), (t, v) -> new InitClass(v, t)); - } else if (current.isInvokeMethod()) { - InvokeMethod invoke = current.asInvokeMethod(); - DexMethod invokedMethod = invoke.getInvokedMethod(); - DexType invokedHolder = invokedMethod.holder; - if (invokedHolder.isArrayType()) { - DexType baseType = invokedHolder.toBaseType(factory); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - baseType, - (t, v) -> { - DexType mappedHolder = invokedHolder.replaceBaseType(t, factory); - // Just reuse proto and name, as no methods on array types cant be renamed nor - // change signature. - DexMethod actualTarget = - factory.createMethod( - mappedHolder, invokedMethod.proto, invokedMethod.name); - return Invoke.create(VIRTUAL, actualTarget, null, v, invoke.inValues()); - }); - continue; - } - if (!invokedHolder.isClassType()) { - assert false; - continue; - } - if (invoke.isInvokeDirect()) { - checkInvokeDirect(method.method, invoke.asInvokeDirect()); - } - GraphLenseLookupResult lenseLookup = - graphLense.lookupMethod(invokedMethod, method.method, invoke.getType()); - DexMethod actualTarget = lenseLookup.getMethod(); - Invoke.Type actualInvokeType = lenseLookup.getType(); - if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) { - RewrittenPrototypeDescription prototypeChanges = - graphLense.lookupPrototypeChanges(actualTarget); - - List<Value> newInValues; - ArgumentInfoCollection argumentInfoCollection = - prototypeChanges.getArgumentInfoCollection(); - if (argumentInfoCollection.isEmpty()) { - newInValues = invoke.inValues(); - } else { - if (argumentInfoCollection.hasRemovedArguments()) { - if (Log.ENABLED) { - Log.info( - getClass(), - "Invoked method " - + invokedMethod.toSourceString() - + " with " - + argumentInfoCollection.numberOfRemovedArguments() - + " arguments removed"); - } - } - newInValues = new ArrayList<>(actualTarget.proto.parameters.size()); - for (int i = 0; i < invoke.inValues().size(); i++) { - ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i); - if (argumentInfo.isRewrittenTypeInfo()) { - RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo(); - Value rewrittenValue = - rewriteValueIfDefault( - code, - iterator, - argInfo.getOldType(), - argInfo.getNewType(), - invoke.inValues().get(i)); - newInValues.add(rewrittenValue); - } else if (!argumentInfo.isRemovedArgumentInfo()) { - newInValues.add(invoke.inValues().get(i)); + switch (current.opcode()) { + case INVOKE_CUSTOM: + { + InvokeCustom invokeCustom = current.asInvokeCustom(); + DexCallSite callSite = invokeCustom.getCallSite(); + DexCallSite newCallSite = rewriteCallSite(callSite, method); + if (newCallSite != callSite) { + Value newOutValue = makeOutValue(invokeCustom, code); + InvokeCustom newInvokeCustom = + new InvokeCustom(newCallSite, newOutValue, invokeCustom.inValues()); + iterator.replaceCurrentInstruction(newInvokeCustom); + if (newOutValue != null && newOutValue.getType() != invokeCustom.getOutType()) { + affectedPhis.addAll(newOutValue.uniquePhiUsers()); } } } + break; - ConstInstruction constantReturnMaterializingInstruction = null; - if (prototypeChanges.hasBeenChangedToReturnVoid(appView) && invoke.outValue() != null) { - constantReturnMaterializingInstruction = - prototypeChanges.getConstantReturn(code, invoke.getPosition()); - if (invoke.outValue().hasLocalInfo()) { - constantReturnMaterializingInstruction - .outValue() - .setLocalInfo(invoke.outValue().getLocalInfo()); - } - invoke.outValue().replaceUsers(constantReturnMaterializingInstruction.outValue()); - if (graphLense.lookupType(invoke.getReturnType()) != invoke.getReturnType()) { - affectedPhis.addAll( - constantReturnMaterializingInstruction.outValue().uniquePhiUsers()); + case CONST_METHOD_HANDLE: + { + DexMethodHandle handle = current.asConstMethodHandle().getValue(); + DexMethodHandle newHandle = + rewriteDexMethodHandle(handle, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY); + if (newHandle != handle) { + Value newOutValue = makeOutValue(current, code); + iterator.replaceCurrentInstruction(new ConstMethodHandle(newOutValue, newHandle)); + if (newOutValue != null && newOutValue.getType() != current.getOutType()) { + affectedPhis.addAll(newOutValue.uniquePhiUsers()); + } } } + break; - Value newOutValue = - prototypeChanges.hasBeenChangedToReturnVoid(appView) - ? null - : makeOutValue(invoke, code); - - if (prototypeChanges.hasExtraNullParameter()) { - iterator.previous(); - Value extraNullValue = iterator.insertConstNullInstruction(code, appView.options()); - iterator.next(); - newInValues.add(extraNullValue); + case INIT_CLASS: + { + InitClass initClass = current.asInitClass(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + initClass.getClassValue(), (t, v) -> new InitClass(v, t)); } + break; - assert newInValues.size() - == actualTarget.proto.parameters.size() + (actualInvokeType == STATIC ? 0 : 1); + case INVOKE_DIRECT: + case INVOKE_INTERFACE: + case INVOKE_POLYMORPHIC: + case INVOKE_STATIC: + case INVOKE_SUPER: + case INVOKE_VIRTUAL: + { + InvokeMethod invoke = current.asInvokeMethod(); + DexMethod invokedMethod = invoke.getInvokedMethod(); + DexType invokedHolder = invokedMethod.holder; + if (invokedHolder.isArrayType()) { + DexType baseType = invokedHolder.toBaseType(factory); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + baseType, + (t, v) -> { + DexType mappedHolder = invokedHolder.replaceBaseType(t, factory); + // Just reuse proto and name, as no methods on array types cant be renamed + // nor change signature. + DexMethod actualTarget = + factory.createMethod( + mappedHolder, invokedMethod.proto, invokedMethod.name); + return Invoke.create(VIRTUAL, actualTarget, null, v, invoke.inValues()); + }); + continue; + } + if (!invokedHolder.isClassType()) { + assert false; + continue; + } + if (invoke.isInvokeDirect()) { + checkInvokeDirect(method.method, invoke.asInvokeDirect()); + } + GraphLenseLookupResult lenseLookup = + graphLense.lookupMethod(invokedMethod, method.method, invoke.getType()); + DexMethod actualTarget = lenseLookup.getMethod(); + Invoke.Type actualInvokeType = lenseLookup.getType(); + if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) { + RewrittenPrototypeDescription prototypeChanges = + graphLense.lookupPrototypeChanges(actualTarget); - Invoke newInvoke = - Invoke.create(actualInvokeType, actualTarget, null, newOutValue, newInValues); - iterator.replaceCurrentInstruction(newInvoke); - if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) { - affectedPhis.addAll(newOutValue.uniquePhiUsers()); - } + List<Value> newInValues; + ArgumentInfoCollection argumentInfoCollection = + prototypeChanges.getArgumentInfoCollection(); + if (argumentInfoCollection.isEmpty()) { + newInValues = invoke.inValues(); + } else { + if (argumentInfoCollection.hasRemovedArguments()) { + if (Log.ENABLED) { + Log.info( + getClass(), + "Invoked method " + + invokedMethod.toSourceString() + + " with " + + argumentInfoCollection.numberOfRemovedArguments() + + " arguments removed"); + } + } + newInValues = new ArrayList<>(actualTarget.proto.parameters.size()); + for (int i = 0; i < invoke.inValues().size(); i++) { + ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i); + if (argumentInfo.isRewrittenTypeInfo()) { + RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo(); + Value rewrittenValue = + rewriteValueIfDefault( + code, + iterator, + argInfo.getOldType(), + argInfo.getNewType(), + invoke.inValues().get(i)); + newInValues.add(rewrittenValue); + } else if (!argumentInfo.isRemovedArgumentInfo()) { + newInValues.add(invoke.inValues().get(i)); + } + } + } - if (constantReturnMaterializingInstruction != null) { - if (block.hasCatchHandlers()) { - // Split the block to ensure no instructions after throwing instructions. - iterator - .split(code, blocks) - .listIterator(code) - .add(constantReturnMaterializingInstruction); - } else { - iterator.add(constantReturnMaterializingInstruction); + ConstInstruction constantReturnMaterializingInstruction = null; + if (prototypeChanges.hasBeenChangedToReturnVoid(appView) + && invoke.outValue() != null) { + constantReturnMaterializingInstruction = + prototypeChanges.getConstantReturn(code, invoke.getPosition()); + if (invoke.outValue().hasLocalInfo()) { + constantReturnMaterializingInstruction + .outValue() + .setLocalInfo(invoke.outValue().getLocalInfo()); + } + invoke.outValue().replaceUsers(constantReturnMaterializingInstruction.outValue()); + if (graphLense.lookupType(invoke.getReturnType()) != invoke.getReturnType()) { + affectedPhis.addAll( + constantReturnMaterializingInstruction.outValue().uniquePhiUsers()); + } + } + + Value newOutValue = + prototypeChanges.hasBeenChangedToReturnVoid(appView) + ? null + : makeOutValue(invoke, code); + + if (prototypeChanges.hasExtraNullParameter()) { + iterator.previous(); + Value extraNullValue = + iterator.insertConstNullInstruction(code, appView.options()); + iterator.next(); + newInValues.add(extraNullValue); + } + + assert newInValues.size() + == actualTarget.proto.parameters.size() + (actualInvokeType == STATIC ? 0 : 1); + + Invoke newInvoke = + Invoke.create(actualInvokeType, actualTarget, null, newOutValue, newInValues); + iterator.replaceCurrentInstruction(newInvoke); + if (newOutValue != null && newOutValue.getType() != current.getOutType()) { + affectedPhis.addAll(newOutValue.uniquePhiUsers()); + } + + if (constantReturnMaterializingInstruction != null) { + if (block.hasCatchHandlers()) { + // Split the block to ensure no instructions after throwing instructions. + iterator + .split(code, blocks) + .listIterator(code) + .add(constantReturnMaterializingInstruction); + } else { + iterator.add(constantReturnMaterializingInstruction); + } + } + + DexType actualReturnType = actualTarget.proto.returnType; + DexType expectedReturnType = graphLense.lookupType(invokedMethod.proto.returnType); + if (newInvoke.outValue() != null && actualReturnType != expectedReturnType) { + throw new Unreachable( + "Unexpected need to insert a cast. Possibly related to resolving" + + " b/79143143.\n" + + invokedMethod + + " type changed from " + + expectedReturnType + + " to " + + actualReturnType); + } } } + break; - DexType actualReturnType = actualTarget.proto.returnType; - DexType expectedReturnType = graphLense.lookupType(invokedMethod.proto.returnType); - if (newInvoke.outValue() != null && actualReturnType != expectedReturnType) { - throw new Unreachable( - "Unexpected need to insert a cast. Possibly related to resolving b/79143143.\n" - + invokedMethod - + " type changed from " + expectedReturnType - + " to " + actualReturnType); + case INSTANCE_GET: + { + InstanceGet instanceGet = current.asInstanceGet(); + DexField field = instanceGet.getField(); + DexField actualField = graphLense.lookupField(field); + DexMethod replacementMethod = + graphLense.lookupGetFieldForMethod(actualField, method.method); + if (replacementMethod != null) { + Value newOutValue = makeOutValue(current, code); + iterator.replaceCurrentInstruction( + new InvokeStatic(replacementMethod, newOutValue, current.inValues())); + if (newOutValue != null && newOutValue.getType() != current.getOutType()) { + affectedPhis.addAll(current.outValue().uniquePhiUsers()); + } + } else if (actualField != field) { + Value newOutValue = makeOutValue(instanceGet, code); + iterator.replaceCurrentInstruction( + new InstanceGet(newOutValue, instanceGet.object(), actualField)); + if (newOutValue != null && newOutValue.getType() != current.getOutType()) { + affectedPhis.addAll(newOutValue.uniquePhiUsers()); + } + } } - } - } else if (current.isInstanceGet()) { - InstanceGet instanceGet = current.asInstanceGet(); - DexField field = instanceGet.getField(); - DexField actualField = graphLense.lookupField(field); - DexMethod replacementMethod = - graphLense.lookupGetFieldForMethod(actualField, method.method); - if (replacementMethod != null) { - Value newOutValue = makeOutValue(current, code); - iterator.replaceCurrentInstruction( - new InvokeStatic(replacementMethod, newOutValue, current.inValues())); - if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) { - affectedPhis.addAll(current.outValue().uniquePhiUsers()); + break; + + case INSTANCE_PUT: + { + InstancePut instancePut = current.asInstancePut(); + DexField field = instancePut.getField(); + DexField actualField = graphLense.lookupField(field); + DexMethod replacementMethod = + graphLense.lookupPutFieldForMethod(actualField, method.method); + if (replacementMethod != null) { + iterator.replaceCurrentInstruction( + new InvokeStatic(replacementMethod, null, current.inValues())); + } else if (actualField != field) { + Value rewrittenValue = + rewriteValueIfDefault( + code, iterator, field.type, actualField.type, instancePut.value()); + InstancePut newInstancePut = + InstancePut.createPotentiallyInvalid( + actualField, instancePut.object(), rewrittenValue); + iterator.replaceCurrentInstruction(newInstancePut); + } } - } else if (actualField != field) { - Value newOutValue = makeOutValue(instanceGet, code); - iterator.replaceCurrentInstruction( - new InstanceGet(newOutValue, instanceGet.object(), actualField)); - if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) { - affectedPhis.addAll(newOutValue.uniquePhiUsers()); + break; + + case STATIC_GET: + { + StaticGet staticGet = current.asStaticGet(); + DexField field = staticGet.getField(); + DexField actualField = graphLense.lookupField(field); + DexMethod replacementMethod = + graphLense.lookupGetFieldForMethod(actualField, method.method); + if (replacementMethod != null) { + Value newOutValue = makeOutValue(current, code); + iterator.replaceCurrentInstruction( + new InvokeStatic(replacementMethod, newOutValue, current.inValues())); + if (newOutValue != null && newOutValue.getType() != current.getOutType()) { + affectedPhis.addAll(newOutValue.uniquePhiUsers()); + } + } else if (actualField != field) { + Value newOutValue = makeOutValue(staticGet, code); + iterator.replaceCurrentInstruction(new StaticGet(newOutValue, actualField)); + if (newOutValue != null && newOutValue.getType() != current.getOutType()) { + affectedPhis.addAll(newOutValue.uniquePhiUsers()); + } + } } - } - } else if (current.isInstancePut()) { - InstancePut instancePut = current.asInstancePut(); - DexField field = instancePut.getField(); - DexField actualField = graphLense.lookupField(field); - DexMethod replacementMethod = - graphLense.lookupPutFieldForMethod(actualField, method.method); - if (replacementMethod != null) { - iterator.replaceCurrentInstruction( - new InvokeStatic(replacementMethod, null, current.inValues())); - } else if (actualField != field) { - Value rewrittenValue = - rewriteValueIfDefault( - code, iterator, field.type, actualField.type, instancePut.value()); - InstancePut newInstancePut = - InstancePut.createPotentiallyInvalid( - actualField, instancePut.object(), rewrittenValue); - iterator.replaceCurrentInstruction(newInstancePut); - } - } else if (current.isStaticGet()) { - StaticGet staticGet = current.asStaticGet(); - DexField field = staticGet.getField(); - DexField actualField = graphLense.lookupField(field); - DexMethod replacementMethod = - graphLense.lookupGetFieldForMethod(actualField, method.method); - if (replacementMethod != null) { - Value newOutValue = makeOutValue(current, code); - iterator.replaceCurrentInstruction( - new InvokeStatic(replacementMethod, newOutValue, current.inValues())); - if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) { - affectedPhis.addAll(newOutValue.uniquePhiUsers()); + break; + + case STATIC_PUT: + { + StaticPut staticPut = current.asStaticPut(); + DexField field = staticPut.getField(); + DexField actualField = graphLense.lookupField(field); + DexMethod replacementMethod = + graphLense.lookupPutFieldForMethod(actualField, method.method); + if (replacementMethod != null) { + iterator.replaceCurrentInstruction( + new InvokeStatic(replacementMethod, current.outValue(), current.inValues())); + } else if (actualField != field) { + Value rewrittenValue = + rewriteValueIfDefault( + code, iterator, field.type, actualField.type, staticPut.value()); + StaticPut newStaticPut = new StaticPut(rewrittenValue, actualField); + iterator.replaceCurrentInstruction(newStaticPut); + } } - } else if (actualField != field) { - Value newOutValue = makeOutValue(staticGet, code); - iterator.replaceCurrentInstruction(new StaticGet(newOutValue, actualField)); - if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) { - affectedPhis.addAll(newOutValue.uniquePhiUsers()); + break; + + case CHECK_CAST: + { + CheckCast checkCast = current.asCheckCast(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + checkCast.getType(), (t, v) -> new CheckCast(v, checkCast.object(), t)); } - } - } else if (current.isStaticPut()) { - StaticPut staticPut = current.asStaticPut(); - DexField field = staticPut.getField(); - DexField actualField = graphLense.lookupField(field); - DexMethod replacementMethod = - graphLense.lookupPutFieldForMethod(actualField, method.method); - if (replacementMethod != null) { - iterator.replaceCurrentInstruction( - new InvokeStatic(replacementMethod, current.outValue(), current.inValues())); - } else if (actualField != field) { - Value rewrittenValue = - rewriteValueIfDefault( - code, iterator, field.type, actualField.type, staticPut.value()); - StaticPut newStaticPut = new StaticPut(rewrittenValue, actualField); - iterator.replaceCurrentInstruction(newStaticPut); - } - } else if (current.isCheckCast()) { - CheckCast checkCast = current.asCheckCast(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - checkCast.getType(), (t, v) -> new CheckCast(v, checkCast.object(), t)); - } else if (current.isConstClass()) { - ConstClass constClass = current.asConstClass(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - constClass.getValue(), (t, v) -> new ConstClass(v, t)); - } else if (current.isInstanceOf()) { - InstanceOf instanceOf = current.asInstanceOf(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - instanceOf.type(), (t, v) -> new InstanceOf(v, instanceOf.value(), t)); - } else if (current.isInvokeMultiNewArray()) { - InvokeMultiNewArray multiNewArray = current.asInvokeMultiNewArray(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - multiNewArray.getArrayType(), - (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues())); - } else if (current.isInvokeNewArray()) { - InvokeNewArray newArray = current.asInvokeNewArray(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - newArray.getArrayType(), (t, v) -> new InvokeNewArray(t, v, newArray.inValues())); - } else if (current.isMoveException()) { - MoveException moveException = current.asMoveException(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - moveException.getExceptionType(), - (t, v) -> new MoveException(v, t, appView.options())); - } else if (current.isNewArrayEmpty()) { - NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty(); - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged( - newArrayEmpty.type, (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t)); - } else if (current.isNewInstance()) { - DexType type = current.asNewInstance().clazz; - new InstructionReplacer(code, current, iterator, affectedPhis) - .replaceInstructionIfTypeChanged(type, NewInstance::new); - } else if (current.outValue() != null) { - // For all other instructions, substitute any changed type. - TypeElement typeLattice = current.outValue().getType(); - TypeElement substituted = - typeLattice.fixupClassTypeReferences(graphLense::lookupType, appView); - if (substituted != typeLattice) { - current.outValue().setType(substituted); - affectedPhis.addAll(current.outValue().uniquePhiUsers()); - } + break; + + case CONST_CLASS: + { + ConstClass constClass = current.asConstClass(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + constClass.getValue(), (t, v) -> new ConstClass(v, t)); + } + break; + + case INSTANCE_OF: + { + InstanceOf instanceOf = current.asInstanceOf(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + instanceOf.type(), (t, v) -> new InstanceOf(v, instanceOf.value(), t)); + } + break; + + case INVOKE_MULTI_NEW_ARRAY: + { + InvokeMultiNewArray multiNewArray = current.asInvokeMultiNewArray(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + multiNewArray.getArrayType(), + (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues())); + } + break; + + case INVOKE_NEW_ARRAY: + { + InvokeNewArray newArray = current.asInvokeNewArray(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + newArray.getArrayType(), + (t, v) -> new InvokeNewArray(t, v, newArray.inValues())); + } + break; + + case MOVE_EXCEPTION: + { + MoveException moveException = current.asMoveException(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + moveException.getExceptionType(), + (t, v) -> new MoveException(v, t, appView.options())); + } + break; + + case NEW_ARRAY_EMPTY: + { + NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty(); + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged( + newArrayEmpty.type, (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t)); + } + break; + + case NEW_INSTANCE: + { + DexType type = current.asNewInstance().clazz; + new InstructionReplacer(code, current, iterator, affectedPhis) + .replaceInstructionIfTypeChanged(type, NewInstance::new); + } + break; + + default: + if (current.hasOutValue()) { + // For all other instructions, substitute any changed type. + TypeElement type = current.getOutType(); + TypeElement substituted = + type.fixupClassTypeReferences(graphLense::lookupType, appView); + if (substituted != type) { + current.outValue().setType(substituted); + affectedPhis.addAll(current.outValue().uniquePhiUsers()); + } + } + break; } } } @@ -685,7 +785,7 @@ Instruction newInstruction = constructor.apply(newType, newOutValue); iterator.replaceCurrentInstruction(newInstruction); if (newOutValue != null) { - if (newOutValue.getType() != current.outValue().getType()) { + if (newOutValue.getType() != current.getOutType()) { affectedPhis.addAll(newOutValue.uniquePhiUsers()); } else { assert current.hasInvariantOutType();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java index e742528..328365e 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -175,7 +175,7 @@ OptimizationFeedback feedback) { // TODO(b/140766440): Make IRConverter#process receive a list of CodeOptimization to conduct. // Then, we can share IRCode creation there. - Origin origin = appView.appInfo().originFor(method.method.holder); + Origin origin = appView.appInfo().originFor(method.holder()); if (appView.options().skipIR) { feedback.markProcessed(method, ConstraintWithTarget.NEVER); return;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java index d7edc8a..58f7155 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
@@ -114,7 +114,7 @@ } } for (Instruction instruction : block.getInstructions()) { - if (instruction.outValue() != null && !instruction.outValue().getType().isPreciseType()) { + if (instruction.outValue() != null && !instruction.getOutType().isPreciseType()) { impreciseValues.add(instruction.outValue()); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java index 43db91d..49c8000 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -125,6 +125,11 @@ } } + public static void registerAssumedLibraryTypes(InternalOptions options) { + // TODO(b/150693139): Remove the pre-registration once fixed. + BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory); + } + public void desugar(IRCode code) { if (!enabled) { return; // Nothing to do! @@ -154,7 +159,7 @@ if (provider.requiresGenerationOfCode()) { DexMethod newMethod = provider.provideMethod(appView); methodProviders.putIfAbsent(newMethod, provider); - holders.add(code.method.method.holder); + holders.add(code.method.holder()); } } if (!affectedValues.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java index 3d16d19..82c7c6a 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.desugar; +import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexAnnotationSet; @@ -18,9 +19,9 @@ import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ParameterAnnotationsList; import com.android.tools.r8.graph.ResolutionResult; -import com.android.tools.r8.graph.ResolutionResult.IncompatibleClassResult; import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode; import com.android.tools.r8.ir.synthetic.SynthesizedCode; +import com.android.tools.r8.position.MethodPosition; import com.android.tools.r8.utils.MethodSignatureEquivalence; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; @@ -74,14 +75,6 @@ return signatures.size() == merged.size() ? this : new MethodSignatures(merged); } - MethodSignatures merge(List<MethodSignatures> others) { - MethodSignatures merged = this; - for (MethodSignatures other : others) { - merged = merged.merge(others); - } - return merged; - } - boolean isEmpty() { return signatures.isEmpty(); } @@ -151,7 +144,7 @@ } } - // Specialized context to disable reporting when traversing the library strucure. + // Specialized context to disable reporting when traversing the library structure. private static class LibraryReportingContext extends ReportingContext { static final LibraryReportingContext LIBRARY_CONTEXT = new LibraryReportingContext(); @@ -281,22 +274,36 @@ return ClassInfo.create(superInfo, additionalForwards.build()); } - // Resolves a method signature from the point of 'clazz', if it must target a default method + // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method // the 'addForward' call-back is called with the target of the forward. private void resolveForwardForSignature( DexClass clazz, DexMethod method, BiConsumer<DexClass, DexEncodedMethod> addForward) { - ResolutionResult resolution = appView.appInfo().resolveMethod(clazz, method); - // If resolution fails, install a method throwing IncompatibleClassChangeError. - if (resolution.isFailedResolution()) { - if (resolution instanceof IncompatibleClassResult) { + // Resolve the default method at base type as the symbolic holder at call sites is not known. + // The dispatch target is then looked up from the possible "instance" class. + // Doing so can cause an invalid invoke to become valid (at runtime resolution at a subtype + // might have failed which is hidden by the insertion of the forward method). However, not doing + // so could cause valid dispatches to become invalid by resolving to private overrides. + DexClassAndMethod virtualDispatchTarget = + appView + .appInfo() + .resolveMethodOnInterface(method.holder, method) + .lookupVirtualDispatchTarget(clazz, appView.appInfo()); + if (virtualDispatchTarget == null) { + // If no target is found due to multiple default method targets, preserve ICCE behavior. + ResolutionResult resolutionFromSubclass = appView.appInfo().resolveMethod(clazz, method); + if (resolutionFromSubclass.isIncompatibleClassChangeErrorResult()) { addICCEThrowingMethod(method, clazz); + return; } + assert resolutionFromSubclass.isFailedResolution() + || resolutionFromSubclass.getSingleTarget().isPrivateMethod(); return; } - DexEncodedMethod target = resolution.getSingleTarget(); - DexClass targetHolder = appView.definitionFor(target.method.holder); + + DexEncodedMethod target = virtualDispatchTarget.getMethod(); + DexClass targetHolder = virtualDispatchTarget.getHolder(); // Don-t forward if the target is explicitly marked as 'dont-rewrite' - if (targetHolder == null || dontRewrite(targetHolder, target)) { + if (dontRewrite(targetHolder, target)) { return; } @@ -330,7 +337,7 @@ private boolean isRetargetMethod(DexLibraryClass holder, DexEncodedMethod method) { assert needsLibraryInfo(); - assert holder.type == method.method.holder; + assert holder.type == method.holder(); assert method.isNonPrivateVirtualMethod(); if (method.isFinal()) { return false; @@ -375,6 +382,16 @@ if (!clazz.isProgramClass()) { return; } + + DexEncodedMethod methodOnSelf = clazz.lookupMethod(target.method); + if (methodOnSelf != null) { + throw new CompilationError( + "Attempt to add forwarding method that conflicts with existing method.", + null, + clazz.getOrigin(), + new MethodPosition(methodOnSelf.method)); + } + DexMethod method = target.method; // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar // even if this results in invalid code, these classes are never desugared.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java index 2be1832..b5c3f3e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -152,12 +152,12 @@ MethodAccessFlags newAccessFlags = method.accessFlags.copy(); newAccessFlags.setBridge(); newAccessFlags.setSynthetic(); - DexMethod newMethod = factory.createMethod(method.method.holder, newProto, method.method.name); + DexMethod newMethod = factory.createMethod(method.holder(), newProto, method.method.name); ForwardMethodSourceCode.Builder forwardSourceCodeBuilder = ForwardMethodSourceCode.builder(newMethod); forwardSourceCodeBuilder .setReceiver(clazz.type) - .setTargetReceiver(method.method.holder) + .setTargetReceiver(method.holder()) .setTarget(method.method) .setInvokeType(Invoke.Type.VIRTUAL) .setCastResult();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java index 7b53a35..738f4aa 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -49,7 +49,7 @@ public void rewriteNestBasedAccesses( DexEncodedMethod encodedMethod, IRCode code, AppView<?> appView) { - DexClass currentClass = appView.definitionFor(encodedMethod.method.holder); + DexClass currentClass = appView.definitionFor(encodedMethod.holder()); assert currentClass != null; if (!currentClass.isInANest()) { return; @@ -120,7 +120,7 @@ private void addDeferredBridges(Collection<DexEncodedMethod> bridges) { for (DexEncodedMethod bridge : bridges) { - DexClass holder = definitionFor(bridge.method.holder); + DexClass holder = definitionFor(bridge.holder()); assert holder != null && holder.isProgramClass(); holder.asProgramClass().addMethod(bridge); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java index c81eedb..11180c4 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -106,7 +106,7 @@ public void desugar(IRCode code) { - if (wrapperSynthesizor.hasSynthesized(code.method.method.holder)) { + if (wrapperSynthesizor.hasSynthesized(code.method.holder())) { return; } @@ -140,7 +140,7 @@ if (!shouldRegisterCallback(encodedMethod)) { return true; } - DexProgramClass holderClass = appView.definitionForProgramType(encodedMethod.method.holder); + DexProgramClass holderClass = appView.definitionForProgramType(encodedMethod.holder()); DexMethod installedCallback = methodWithVivifiedTypeInSignature(encodedMethod.method, holderClass.type, appView); assert holderClass.lookupMethod(installedCallback) != null; @@ -161,7 +161,7 @@ public void registerCallbackIfRequired(DexEncodedMethod encodedMethod) { if (shouldRegisterCallback(encodedMethod)) { - DexClass dexClass = appView.definitionFor(encodedMethod.method.holder); + DexClass dexClass = appView.definitionFor(encodedMethod.holder()); assert dexClass != null; registerCallback(dexClass, encodedMethod); } @@ -197,7 +197,6 @@ return overridesLibraryMethod(dexClass, method); } - private boolean overridesLibraryMethod(DexClass theClass, DexMethod method) { // We look up everywhere to see if there is a supertype/interface implementing the method... WorkList<DexType> workList = WorkList.newIdentityWorkList(); @@ -253,7 +252,7 @@ } private synchronized void addCallBackSignature(DexClass dexClass, DexEncodedMethod method) { - assert dexClass.type == method.method.holder; + assert dexClass.type == method.holder(); if (callBackMethods.computeIfAbsent(dexClass, key -> new HashSet<>()).add(method)) { pendingCallBackMethods.computeIfAbsent(dexClass, key -> new ArrayList<>()).add(method); } @@ -347,22 +346,22 @@ appView.options().reporter.warning(new StringDiagnostic(sb.toString())); } - private void warnInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) { + public void reportInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) { DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView); appView .options() .reporter - .warning( + .info( new StringDiagnostic( "Invoke to " + invokedMethod.holder + "#" + invokedMethod.name - + " may not work correctly at runtime (" + + " may not work correctly at runtime (Cannot convert " + debugString - + " type " + + "type " + desugaredType - + " is a desugared type).")); + + ").")); } public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) { @@ -398,6 +397,7 @@ InstructionListIterator iterator, ListIterator<BasicBlock> blockIterator) { DexMethod invokedMethod = invokeMethod.getInvokedMethod(); + boolean invalidConversion = false; if (trackedAPIs != null) { trackedAPIs.add(invokedMethod); } @@ -412,12 +412,13 @@ // Return conversion added only if return value is used. if (invokeMethod.outValue() != null && invokeMethod.outValue().numberOfUsers() + invokeMethod.outValue().numberOfPhiUsers() - > 0) { + > 0) { returnConversion = createReturnConversionAndReplaceUses(code, invokeMethod, returnType, newReturnType); } } else { - warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return"); + reportInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return "); + invalidConversion = true; newReturnType = returnType; } } else { @@ -445,7 +446,8 @@ createParameterConversion(code, argType, argVivifiedType, inValue)); newInValues.add(parameterConversions.get(parameterConversions.size() - 1).outValue()); } else { - warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter"); + reportInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter "); + invalidConversion = true; newInValues.add(invokeMethod.inValues().get(i + receiverShift)); } } else { @@ -465,7 +467,8 @@ invokeMethod.outValue(), newInValues); assert newDexMethod - == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView); + == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView) + || invalidConversion; // Insert and reschedule all instructions. iterator.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java index 3949767..0e52152 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -5,7 +5,24 @@ package com.android.tools.r8.ir.desugar; import com.android.tools.r8.dex.Constants; -import com.android.tools.r8.graph.*; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.ClassAccessFlags; +import com.android.tools.r8.graph.DexAnnotationSet; +import com.android.tools.r8.graph.DexApplication; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexLibraryClass; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexProto; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.DexTypeList; +import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionListIterator; @@ -118,7 +135,7 @@ appView .appInfo() .withClassHierarchy() - .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder); + .lookupSuperTarget(invoke.getInvokedMethod(), code.method.holder()); // Final methods can be rewritten as a normal invoke. if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) { DexMethod retargetMethod = @@ -270,7 +287,7 @@ && dexClass.isLibraryClass() && dexClass.type != appView.dexItemFactory().objectType) { for (DexType dexType : map.keySet()) { - if (inherit(dexClass.asLibraryClass(), dexType)) { + if (inherit(dexClass.asLibraryClass(), dexType, emulatedDispatchMethods)) { addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType))); } } @@ -282,13 +299,21 @@ converter.processMethodsConcurrently(addedMethods, executorService); } - private boolean inherit(DexLibraryClass clazz, DexType typeToInherit) { + private boolean inherit(DexLibraryClass clazz, DexType typeToInherit, Set<DexMethod> retarget) { DexLibraryClass current = clazz; while (current.type != appView.dexItemFactory().objectType) { if (current.type == typeToInherit) { return true; } - current = appView.definitionFor(current.superType).asLibraryClass(); + DexClass dexClass = appView.definitionFor(current.superType); + if (dexClass == null || dexClass.isClasspathClass()) { + reportInvalidLibrarySupertype(current, retarget); + return false; + } else if (dexClass.isProgramClass()) { + // If dexClass is a program class, then it is already correctly desugared. + return false; + } + current = dexClass.asLibraryClass(); } return false; } @@ -413,6 +438,29 @@ } } + private void reportInvalidLibrarySupertype( + DexLibraryClass libraryClass, Set<DexMethod> retarget) { + DexClass dexClass = appView.definitionFor(libraryClass.superType); + String message; + if (dexClass == null) { + message = "missing"; + } else if (dexClass.isClasspathClass()) { + message = "a classpath class"; + } else { + message = "INVALID"; + assert false; + } + appView + .options() + .warningInvalidLibrarySuperclassForDesugar( + dexClass == null ? libraryClass.getOrigin() : dexClass.getOrigin(), + libraryClass.type, + libraryClass.superType, + message, + retarget, + appView); + } + private DexType dispatchInterfaceTypeFor(DexMethod method) { return dispatchTypeFor(method, "dispatchInterface"); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java index 30bc78e..e3c15ae 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -125,7 +125,7 @@ boolean canGenerateWrapper(DexType type) { DexClass dexClass = appView.definitionFor(type); - if (dexClass == null) { + if (dexClass == null || dexClass.accessFlags.isFinal()) { return false; } return dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation(); @@ -169,18 +169,9 @@ private DexClass getValidClassToWrap(DexType type) { DexClass dexClass = appView.definitionFor(type); // The dexClass should be a library class, so it cannot be null. - assert dexClass != null - && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation()); - if (dexClass.accessFlags.isFinal()) { - throw appView - .options() - .reporter - .fatalError( - new StringDiagnostic( - "Cannot generate a wrapper for final class " - + dexClass.type - + ". Add a custom conversion in the desugared library.")); - } + assert dexClass != null; + assert dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation(); + assert !dexClass.accessFlags.isFinal(); return dexClass; } @@ -223,7 +214,7 @@ DexTypeList interfaces = isItf ? new DexTypeList(new DexType[] {wrappingType}) : DexTypeList.empty(); return new DexProgramClass( - wrapperField.field.holder, + wrapperField.holder(), null, new SynthesizedOrigin("Desugared library API Converter", getClass()), ClassAccessFlags.fromSharedAccessFlags( @@ -260,23 +251,24 @@ // return v3; Set<DexMethod> finalMethods = Sets.newIdentityHashSet(); for (DexEncodedMethod dexEncodedMethod : dexMethods) { - DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder); + DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder()); boolean isInterface; if (holderClass == null) { - assert appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface() - .containsValue(dexEncodedMethod.method.holder); + assert appView + .options() + .desugaredLibraryConfiguration + .getEmulateLibraryInterface() + .containsValue(dexEncodedMethod.holder()); isInterface = true; } else { isInterface = holderClass.isInterface(); } DexMethod methodToInstall = factory.createMethod( - wrapperField.field.holder, - dexEncodedMethod.method.proto, - dexEncodedMethod.method.name); + wrapperField.holder(), dexEncodedMethod.method.proto, dexEncodedMethod.method.name); CfCode cfCode; if (dexEncodedMethod.isFinal()) { - invalidWrappers.add(wrapperField.field.holder); + invalidWrappers.add(wrapperField.holder()); finalMethods.add(dexEncodedMethod.method); continue; } else { @@ -312,15 +304,15 @@ // return v3; Set<DexMethod> finalMethods = Sets.newIdentityHashSet(); for (DexEncodedMethod dexEncodedMethod : dexMethods) { - DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder); + DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder()); assert holderClass != null || appView.options().isDesugaredLibraryCompilation(); boolean isInterface = holderClass == null || holderClass.isInterface(); DexMethod methodToInstall = DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature( - dexEncodedMethod.method, wrapperField.field.holder, appView); + dexEncodedMethod.method, wrapperField.holder(), appView); CfCode cfCode; if (dexEncodedMethod.isFinal()) { - invalidWrappers.add(wrapperField.field.holder); + invalidWrappers.add(wrapperField.holder()); finalMethods.add(dexEncodedMethod.method); continue; } else { @@ -342,7 +334,7 @@ List<DexEncodedMethod> generatedMethods = new ArrayList<>(); // Generate only abstract methods for library override detection. for (DexEncodedMethod dexEncodedMethod : dexMethods) { - DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder); + DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder()); assert holderClass != null || appView.options().isDesugaredLibraryCompilation(); if (!dexEncodedMethod.isFinal()) { DexMethod methodToInstall =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java index 1a4c416..e9b9b7e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -62,6 +62,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.function.BiConsumer; import java.util.function.Consumer; // @@ -143,6 +144,37 @@ initializeEmulatedInterfaceVariables(); } + public static void checkForAssumedLibraryTypes(AppInfo appInfo, InternalOptions options) { + DesugaredLibraryConfiguration config = options.desugaredLibraryConfiguration; + BiConsumer<DexType, DexType> registerEntry = registerMapEntry(appInfo); + config.getEmulateLibraryInterface().forEach(registerEntry); + config.getCustomConversions().forEach(registerEntry); + config.getRetargetCoreLibMember().forEach((method, types) -> types.forEach(registerEntry)); + } + + private static BiConsumer<DexType, DexType> registerMapEntry(AppInfo appInfo) { + return (key, value) -> { + registerType(appInfo, key); + registerType(appInfo, value); + }; + } + + private static void registerType(AppInfo appInfo, DexType type) { + appInfo.dexItemFactory().registerTypeNeededForDesugaring(type); + DexClass clazz = appInfo.definitionFor(type); + if (clazz != null && clazz.isLibraryClass() && clazz.isInterface()) { + clazz.forEachMethod( + m -> { + if (m.isDefaultMethod()) { + appInfo.dexItemFactory().registerTypeNeededForDesugaring(m.method.proto.returnType); + for (DexType param : m.method.proto.parameters.values) { + appInfo.dexItemFactory().registerTypeNeededForDesugaring(param); + } + } + }); + } + } + private void initializeEmulatedInterfaceVariables() { Map<DexType, DexType> emulateLibraryInterface = options.desugaredLibraryConfiguration.getEmulateLibraryInterface(); @@ -238,7 +270,7 @@ invokeStatic.outValue(), invokeStatic.arguments())); requiredDispatchClasses .computeIfAbsent(clazz.asLibraryClass(), k -> Sets.newConcurrentHashSet()) - .add(appInfo.definitionFor(encodedMethod.method.holder).asProgramClass()); + .add(appInfo.definitionFor(encodedMethod.holder()).asProgramClass()); } } else { instructions.replaceCurrentInstruction( @@ -269,8 +301,7 @@ // WARNING: This may result in incorrect code on older platforms! // Retarget call to an appropriate method of companion class. DexMethod amendedMethod = - amendDefaultMethod( - appInfo.definitionFor(encodedMethod.method.holder), invokedMethod); + amendDefaultMethod(appInfo.definitionFor(encodedMethod.holder()), invokedMethod); instructions.replaceCurrentInstruction( new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod), invokeSuper.outValue(), invokeSuper.arguments())); @@ -284,9 +315,9 @@ DexEncodedMethod dexEncodedMethod = appView .appInfo() - .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.method.holder); + .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.holder()); if (dexEncodedMethod != null) { - DexClass dexClass = appView.definitionFor(dexEncodedMethod.method.holder); + DexClass dexClass = appView.definitionFor(dexEncodedMethod.holder()); if (dexClass != null && dexClass.isLibraryClass()) { // Rewriting is required because the super invoke resolves into a missing // method (method is on desugared library). Find out if it needs to be @@ -418,8 +449,8 @@ // interfaces. return null; } - if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.method.holder)) { - return singleTarget.method.holder; + if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.holder())) { + return singleTarget.holder(); } return null; } @@ -616,8 +647,8 @@ } emulationMethods.add( DexEncodedMethod.toEmulateDispatchLibraryMethod( - method.method.holder, - emulateInterfaceLibraryMethod(method.method, method.method.holder, factory), + method.holder(), + emulateInterfaceLibraryMethod(method.method, method.holder(), factory), companionMethod, libraryMethod, extraDispatchCases, @@ -719,6 +750,10 @@ return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";"); } + public static boolean isEmulatedLibraryClassType(DexType type) { + return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";"); + } + // Gets the interface class for a companion class `type`. private DexType getInterfaceClassType(DexType type) { return getInterfaceClassType(type, factory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java index 4409c30..ab5a9cd 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -320,7 +320,7 @@ } else { assert code.isCfCode(); for (CfInstruction insn : code.asCfCode().getInstructions()) { - if (insn instanceof CfInvoke && ((CfInvoke) insn).isInvokeSuper(method.method.holder)) { + if (insn instanceof CfInvoke && ((CfInvoke) insn).isInvokeSuper(method.holder())) { return false; } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java index d440b01..2ac43f0 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -96,7 +96,7 @@ invocationContext == null ? null : lookupTargetMethod(appInfo, invocationContext); if (targetMethod != null) { targetAccessFlags = targetMethod.accessFlags.copy(); - targetHolder = targetMethod.method.holder; + targetHolder = targetMethod.holder(); } else { targetAccessFlags = null; targetHolder = null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java index 4ee35d6..d6d698e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -135,7 +135,7 @@ */ public void desugarLambdas(DexEncodedMethod encodedMethod, IRCode code) { Set<Value> affectedValues = Sets.newIdentityHashSet(); - DexType currentType = encodedMethod.method.holder; + DexType currentType = encodedMethod.holder(); ListIterator<BasicBlock> blocks = code.listIterator(); while (blocks.hasNext()) { BasicBlock block = blocks.next(); @@ -145,7 +145,7 @@ if (instruction.isInvokeCustom()) { InvokeCustom invoke = instruction.asInvokeCustom(); LambdaDescriptor descriptor = - inferLambdaDescriptor(invoke.getCallSite(), encodedMethod.method.holder); + inferLambdaDescriptor(invoke.getCallSite(), encodedMethod.holder()); if (descriptor == LambdaDescriptor.MATCH_FAILED) { continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java index b78757d..f4e1f4a 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -51,7 +51,6 @@ private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "sfput"; public static final String NEST_CONSTRUCTOR_NAME = NEST_ACCESS_NAME_PREFIX + "Constructor"; - private static final String FULL_NEST_CONTRUCTOR_NAME = "L" + NEST_CONSTRUCTOR_NAME + ";"; protected final AppView<?> appView; // Following maps are there to avoid creating the bridges multiple times @@ -161,7 +160,7 @@ private DexProgramClass createNestAccessConstructor() { return new DexProgramClass( - appView.dexItemFactory().createType(FULL_NEST_CONTRUCTOR_NAME), + appView.dexItemFactory().nestConstructorType, null, new SynthesizedOrigin("Nest based access desugaring", getClass()), // Make the synthesized class public since shared in the whole program. @@ -238,7 +237,7 @@ } private DexMethod computeFieldBridge(DexEncodedField field, boolean isGet) { - DexType holderType = field.field.holder; + DexType holderType = field.holder(); DexType fieldType = field.field.type; int bridgeParameterCount = BooleanUtils.intValue(!field.isStatic()) + BooleanUtils.intValue(!isGet); @@ -259,10 +258,10 @@ boolean invokeRequiresRewriting(DexEncodedMethod method, DexClass contextClass) { assert method != null; // Rewrite only when targeting other nest members private fields. - if (!method.accessFlags.isPrivate() || method.method.holder == contextClass.type) { + if (!method.accessFlags.isPrivate() || method.holder() == contextClass.type) { return false; } - DexClass methodHolder = definitionFor(method.method.holder); + DexClass methodHolder = definitionFor(method.holder()); assert methodHolder != null; // from encodedMethod return methodHolder.getNestHost() == contextClass.getNestHost(); } @@ -270,10 +269,10 @@ boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClass contextClass) { assert field != null; // Rewrite only when targeting other nest members private fields. - if (!field.accessFlags.isPrivate() || field.field.holder == contextClass.type) { + if (!field.accessFlags.isPrivate() || field.holder() == contextClass.type) { return false; } - DexClass fieldHolder = definitionFor(field.field.holder); + DexClass fieldHolder = definitionFor(field.holder()); assert fieldHolder != null; // from encodedField return fieldHolder.getNestHost() == contextClass.getNestHost(); } @@ -295,7 +294,7 @@ } DexMethod ensureFieldAccessBridge(DexEncodedField field, boolean isGet) { - DexClass holder = definitionFor(field.field.holder); + DexClass holder = definitionFor(field.holder()); assert holder != null; DexMethod bridgeMethod = computeFieldBridge(field, isGet); if (holderRequiresBridge(holder)) { @@ -314,7 +313,7 @@ DexMethod ensureInvokeBridge(DexEncodedMethod method) { // We add bridges only when targeting other nest members. - DexClass holder = definitionFor(method.method.holder); + DexClass holder = definitionFor(method.holder()); assert holder != null; DexMethod bridgeMethod; if (method.isInstanceInitializer()) { @@ -508,7 +507,7 @@ } public DexType getHolder() { - return field.field.holder; + return field.holder(); } public DexField getField() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java index 1c3b6a2..6f5c3f2 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -18,6 +18,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; public abstract class PrefixRewritingMapper { @@ -47,6 +48,8 @@ public abstract boolean isRewriting(); + public abstract void forAllRewrittenTypes(Consumer<DexType> consumer); + public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper { private final Set<DexType> notRewritten = Sets.newConcurrentHashSet(); @@ -70,6 +73,11 @@ return factory.createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix)); } + @Override + public void forAllRewrittenTypes(Consumer<DexType> consumer) { + rewritten.keySet().forEach(consumer); + } + private void validatePrefixes(Map<String, String> initialPrefixes) { String[] prefixes = initialPrefixes.keySet().toArray(new String[0]); for (int i = 0; i < prefixes.length; i++) { @@ -192,5 +200,8 @@ public boolean isRewriting() { return false; } + + @Override + public void forAllRewrittenTypes(Consumer<DexType> consumer) {} } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java index c4c9bd7..ca10fe6 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -56,7 +56,7 @@ private <E> void addDeferredBridgesAndMapMethods( Map<E, DexEncodedMethod> bridges, BiConsumer<E, DexMethod> lensInserter) { for (Map.Entry<E, DexEncodedMethod> entry : bridges.entrySet()) { - DexClass holder = definitionFor(entry.getValue().method.holder); + DexClass holder = definitionFor(entry.getValue().holder()); assert holder != null && holder.isProgramClass(); holder.asProgramClass().addMethod(entry.getValue()); lensInserter.accept(entry.getKey(), entry.getValue().method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java index 17250cf..e477fe3 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -99,7 +99,7 @@ new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues())); // Mark as a class referencing utility class. - referencingClasses.add(appInfo.definitionFor(code.method.method.holder).asProgramClass()); + referencingClasses.add(appInfo.definitionFor(code.method.holder()).asProgramClass()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java index 023ef1c..bc3c320 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -36,6 +36,7 @@ import com.android.tools.r8.cf.code.CfThrow; import com.android.tools.r8.cf.code.CfTryCatch; import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.ir.code.Cmp; import com.android.tools.r8.ir.code.If; @@ -47,6 +48,59 @@ public final class BackportedMethods { + public static void registerSynthesizedCodeReferences(DexItemFactory factory) { + factory.createSynthesizedType("Ljava/lang/ArithmeticException;"); + factory.createSynthesizedType("Ljava/lang/AssertionError;"); + factory.createSynthesizedType("Ljava/lang/Double;"); + factory.createSynthesizedType("Ljava/lang/ExceptionInInitializerError;"); + factory.createSynthesizedType("Ljava/lang/Float;"); + factory.createSynthesizedType("Ljava/lang/IllegalAccessException;"); + factory.createSynthesizedType("Ljava/lang/IllegalArgumentException;"); + factory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;"); + factory.createSynthesizedType("Ljava/lang/Integer;"); + factory.createSynthesizedType("Ljava/lang/Iterable;"); + factory.createSynthesizedType("Ljava/lang/Long;"); + factory.createSynthesizedType("Ljava/lang/Math;"); + factory.createSynthesizedType("Ljava/lang/NoSuchMethodException;"); + factory.createSynthesizedType("Ljava/lang/NullPointerException;"); + factory.createSynthesizedType("Ljava/lang/NumberFormatException;"); + factory.createSynthesizedType("Ljava/lang/Runnable;"); + factory.createSynthesizedType("Ljava/lang/SecurityException;"); + factory.createSynthesizedType("Ljava/lang/reflect/InvocationTargetException;"); + factory.createSynthesizedType("Ljava/lang/reflect/Method;"); + factory.createSynthesizedType("Ljava/util/AbstractMap$SimpleImmutableEntry;"); + factory.createSynthesizedType("Ljava/util/ArrayList;"); + factory.createSynthesizedType("Ljava/util/Arrays;"); + factory.createSynthesizedType("Ljava/util/Collection;"); + factory.createSynthesizedType("Ljava/util/Collections;"); + factory.createSynthesizedType("Ljava/util/Comparator;"); + factory.createSynthesizedType("Ljava/util/Enumeration;"); + factory.createSynthesizedType("Ljava/util/HashMap;"); + factory.createSynthesizedType("Ljava/util/HashSet;"); + factory.createSynthesizedType("Ljava/util/Iterator;"); + factory.createSynthesizedType("Ljava/util/List;"); + factory.createSynthesizedType("Ljava/util/ListIterator;"); + factory.createSynthesizedType("Ljava/util/Map$Entry;"); + factory.createSynthesizedType("Ljava/util/Map;"); + factory.createSynthesizedType("Ljava/util/Objects;"); + factory.createSynthesizedType("Ljava/util/Optional;"); + factory.createSynthesizedType("Ljava/util/OptionalDouble;"); + factory.createSynthesizedType("Ljava/util/OptionalInt;"); + factory.createSynthesizedType("Ljava/util/OptionalLong;"); + factory.createSynthesizedType("Ljava/util/Set;"); + factory.createSynthesizedType("Ljava/util/function/Consumer;"); + factory.createSynthesizedType("Ljava/util/function/DoubleConsumer;"); + factory.createSynthesizedType("Ljava/util/function/IntConsumer;"); + factory.createSynthesizedType("Ljava/util/function/LongConsumer;"); + factory.createSynthesizedType("Ljava/util/function/Supplier;"); + factory.createSynthesizedType("Ljava/util/stream/DoubleStream;"); + factory.createSynthesizedType("Ljava/util/stream/IntStream;"); + factory.createSynthesizedType("Ljava/util/stream/LongStream;"); + factory.createSynthesizedType("Ljava/util/stream/Stream;"); + factory.createSynthesizedType("[Ljava/lang/Class;"); + factory.createSynthesizedType("[Ljava/lang/Object;"); + } + public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) { CfLabel label0 = new CfLabel(); CfLabel label1 = new CfLabel(); @@ -235,7 +289,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Math;"), + options.itemFactory.createType("Ljava/lang/Math;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.intType, @@ -397,15 +451,15 @@ false), new CfConstString(options.itemFactory.createString("close")), new CfConstNumber(0, ValueType.INT), - new CfNewArray(options.itemFactory.createSynthesizedType("[Ljava/lang/Class;")), + new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")), new CfInvoke( 182, options.itemFactory.createMethod( options.itemFactory.classType, options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/lang/reflect/Method;"), + options.itemFactory.createType("Ljava/lang/reflect/Method;"), options.itemFactory.stringType, - options.itemFactory.createSynthesizedType("[Ljava/lang/Class;")), + options.itemFactory.createType("[Ljava/lang/Class;")), options.itemFactory.createString("getMethod")), false), new CfStore(ValueType.OBJECT, 2), @@ -413,15 +467,15 @@ new CfLoad(ValueType.OBJECT, 2), new CfLoad(ValueType.OBJECT, 1), new CfConstNumber(0, ValueType.INT), - new CfNewArray(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")), new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/reflect/Method;"), + options.itemFactory.createType("Ljava/lang/reflect/Method;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType, - options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + options.itemFactory.createType("[Ljava/lang/Object;")), options.itemFactory.createString("invoke")), false), new CfStackInstruction(CfStackInstruction.Opcode.Pop), @@ -430,7 +484,7 @@ label5, new CfStore(ValueType.OBJECT, 2), label6, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;")), + new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -477,7 +531,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;"), + options.itemFactory.createType("Ljava/lang/AssertionError;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType, @@ -488,7 +542,7 @@ label7, new CfStore(ValueType.OBJECT, 2), label8, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;")), + new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -535,7 +589,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;"), + options.itemFactory.createType("Ljava/lang/AssertionError;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType, @@ -550,8 +604,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/reflect/InvocationTargetException;"), + options.itemFactory.createType("Ljava/lang/reflect/InvocationTargetException;"), options.itemFactory.createProto(options.itemFactory.throwableType), options.itemFactory.createString("getCause")), false), @@ -577,40 +630,36 @@ label2, label4, ImmutableList.of( - options.itemFactory.createSynthesizedType("Ljava/lang/NoSuchMethodException;")), + options.itemFactory.createType("Ljava/lang/NoSuchMethodException;")), + ImmutableList.of(label5)), + new CfTryCatch( + label2, + label4, + ImmutableList.of(options.itemFactory.createType("Ljava/lang/SecurityException;")), ImmutableList.of(label5)), new CfTryCatch( label2, label4, ImmutableList.of( - options.itemFactory.createSynthesizedType("Ljava/lang/SecurityException;")), - ImmutableList.of(label5)), - new CfTryCatch( - label2, - label4, - ImmutableList.of( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IllegalAccessException;")), + options.itemFactory.createType("Ljava/lang/IllegalAccessException;")), ImmutableList.of(label7)), new CfTryCatch( label2, label4, ImmutableList.of( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IllegalArgumentException;")), + options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")), ImmutableList.of(label7)), new CfTryCatch( label2, label4, ImmutableList.of( - options.itemFactory.createSynthesizedType( - "Ljava/lang/ExceptionInInitializerError;")), + options.itemFactory.createType("Ljava/lang/ExceptionInInitializerError;")), ImmutableList.of(label7)), new CfTryCatch( label2, label4, ImmutableList.of( - options.itemFactory.createSynthesizedType( + options.itemFactory.createType( "Ljava/lang/reflect/InvocationTargetException;")), ImmutableList.of(label9)), new CfTryCatch( @@ -635,14 +684,14 @@ 6, ImmutableList.of( label0, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;")), + new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfArrayLength(), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"), + options.itemFactory.createType("Ljava/util/ArrayList;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("<init>")), @@ -670,7 +719,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -678,7 +727,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"), + options.itemFactory.createType("Ljava/util/ArrayList;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.objectType), options.itemFactory.createString("add")), @@ -692,10 +741,10 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/List;"), - options.itemFactory.createSynthesizedType("Ljava/util/List;")), + options.itemFactory.createType("Ljava/util/List;"), + options.itemFactory.createType("Ljava/util/List;")), options.itemFactory.createString("unmodifiableList")), false), new CfReturn(ValueType.OBJECT), @@ -717,15 +766,14 @@ ImmutableList.of( label0, new CfNew( - options.itemFactory.createSynthesizedType( - "Ljava/util/AbstractMap$SimpleImmutableEntry;")), + options.itemFactory.createType("Ljava/util/AbstractMap$SimpleImmutableEntry;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), label1, new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -735,7 +783,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -743,8 +791,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/util/AbstractMap$SimpleImmutableEntry;"), + options.itemFactory.createType("Ljava/util/AbstractMap$SimpleImmutableEntry;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.objectType, @@ -775,14 +822,14 @@ 8, ImmutableList.of( label0, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashMap;")), + new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfArrayLength(), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"), + options.itemFactory.createType("Ljava/util/HashMap;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("<init>")), @@ -809,14 +856,14 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"), + options.itemFactory.createType("Ljava/util/Map$Entry;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("getKey")), true), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -827,14 +874,14 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"), + options.itemFactory.createType("Ljava/util/Map$Entry;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("getValue")), true), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -847,7 +894,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"), + options.itemFactory.createType("Ljava/util/HashMap;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType, @@ -856,8 +903,7 @@ false), new CfIf(If.Type.EQ, ValueType.OBJECT, label7), label6, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/IllegalArgumentException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -896,8 +942,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IllegalArgumentException;"), + options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -911,10 +956,10 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Map;"), - options.itemFactory.createSynthesizedType("Ljava/util/Map;")), + options.itemFactory.createType("Ljava/util/Map;"), + options.itemFactory.createType("Ljava/util/Map;")), options.itemFactory.createString("unmodifiableMap")), false), new CfReturn(ValueType.OBJECT), @@ -938,14 +983,14 @@ 6, ImmutableList.of( label0, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashSet;")), + new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfArrayLength(), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"), + options.itemFactory.createType("Ljava/util/HashSet;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("<init>")), @@ -973,7 +1018,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -981,15 +1026,14 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"), + options.itemFactory.createType("Ljava/util/HashSet;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.objectType), options.itemFactory.createString("add")), false), new CfIf(If.Type.NE, ValueType.INT, label5), label4, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/IllegalArgumentException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -1028,8 +1072,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IllegalArgumentException;"), + options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -1043,10 +1086,10 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Set;"), - options.itemFactory.createSynthesizedType("Ljava/util/Set;")), + options.itemFactory.createType("Ljava/util/Set;"), + options.itemFactory.createType("Ljava/util/Set;")), options.itemFactory.createString("unmodifiableSet")), false), new CfReturn(ValueType.OBJECT), @@ -1069,20 +1112,20 @@ 4, ImmutableList.of( label0, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;")), + new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collection;"), + options.itemFactory.createType("Ljava/util/Collection;"), options.itemFactory.createProto(options.itemFactory.intType), options.itemFactory.createString("size")), true), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"), + options.itemFactory.createType("Ljava/util/ArrayList;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("<init>")), @@ -1093,9 +1136,9 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collection;"), + options.itemFactory.createType("Ljava/util/Collection;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")), + options.itemFactory.createType("Ljava/util/Iterator;")), options.itemFactory.createString("iterator")), true), new CfStore(ValueType.OBJECT, 2), @@ -1104,7 +1147,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("hasNext")), true), @@ -1113,7 +1156,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("next")), true), @@ -1124,7 +1167,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -1132,7 +1175,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"), + options.itemFactory.createType("Ljava/util/ArrayList;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.objectType), options.itemFactory.createString("add")), @@ -1145,10 +1188,10 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/List;"), - options.itemFactory.createSynthesizedType("Ljava/util/List;")), + options.itemFactory.createType("Ljava/util/List;"), + options.itemFactory.createType("Ljava/util/List;")), options.itemFactory.createString("unmodifiableList")), false), new CfReturn(ValueType.OBJECT), @@ -1174,20 +1217,20 @@ 4, ImmutableList.of( label0, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashMap;")), + new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Map;"), + options.itemFactory.createType("Ljava/util/Map;"), options.itemFactory.createProto(options.itemFactory.intType), options.itemFactory.createString("size")), true), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"), + options.itemFactory.createType("Ljava/util/HashMap;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("<init>")), @@ -1198,17 +1241,17 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Map;"), + options.itemFactory.createType("Ljava/util/Map;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Set;")), + options.itemFactory.createType("Ljava/util/Set;")), options.itemFactory.createString("entrySet")), true), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Set;"), + options.itemFactory.createType("Ljava/util/Set;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")), + options.itemFactory.createType("Ljava/util/Iterator;")), options.itemFactory.createString("iterator")), true), new CfStore(ValueType.OBJECT, 2), @@ -1217,7 +1260,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("hasNext")), true), @@ -1226,11 +1269,11 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("next")), true), - new CfCheckCast(options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;")), + new CfCheckCast(options.itemFactory.createType("Ljava/util/Map$Entry;")), new CfStore(ValueType.OBJECT, 3), label3, new CfLoad(ValueType.OBJECT, 1), @@ -1239,14 +1282,14 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"), + options.itemFactory.createType("Ljava/util/Map$Entry;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("getKey")), true), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -1256,14 +1299,14 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"), + options.itemFactory.createType("Ljava/util/Map$Entry;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("getValue")), true), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -1272,7 +1315,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"), + options.itemFactory.createType("Ljava/util/HashMap;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType, @@ -1287,10 +1330,10 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Map;"), - options.itemFactory.createSynthesizedType("Ljava/util/Map;")), + options.itemFactory.createType("Ljava/util/Map;"), + options.itemFactory.createType("Ljava/util/Map;")), options.itemFactory.createString("unmodifiableMap")), false), new CfReturn(ValueType.OBJECT), @@ -1313,20 +1356,20 @@ 4, ImmutableList.of( label0, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashSet;")), + new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collection;"), + options.itemFactory.createType("Ljava/util/Collection;"), options.itemFactory.createProto(options.itemFactory.intType), options.itemFactory.createString("size")), true), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"), + options.itemFactory.createType("Ljava/util/HashSet;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("<init>")), @@ -1337,9 +1380,9 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collection;"), + options.itemFactory.createType("Ljava/util/Collection;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")), + options.itemFactory.createType("Ljava/util/Iterator;")), options.itemFactory.createString("iterator")), true), new CfStore(ValueType.OBJECT, 2), @@ -1348,7 +1391,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("hasNext")), true), @@ -1357,7 +1400,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("next")), true), @@ -1368,7 +1411,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -1376,7 +1419,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"), + options.itemFactory.createType("Ljava/util/HashSet;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.objectType), options.itemFactory.createString("add")), @@ -1389,10 +1432,10 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Set;"), - options.itemFactory.createSynthesizedType("Ljava/util/Set;")), + options.itemFactory.createType("Ljava/util/Set;"), + options.itemFactory.createType("Ljava/util/Set;")), options.itemFactory.createString("unmodifiableSet")), false), new CfReturn(ValueType.OBJECT), @@ -1413,18 +1456,18 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/List;")), + options.itemFactory.createType("Ljava/util/List;")), options.itemFactory.createString("emptyList")), false), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Enumeration;"), - options.itemFactory.createSynthesizedType("Ljava/util/Collection;")), + options.itemFactory.createType("Ljava/util/Enumeration;"), + options.itemFactory.createType("Ljava/util/Collection;")), options.itemFactory.createString("enumeration")), false), new CfReturn(ValueType.OBJECT)), @@ -1443,17 +1486,17 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/List;")), + options.itemFactory.createType("Ljava/util/List;")), options.itemFactory.createString("emptyList")), false), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/List;"), + options.itemFactory.createType("Ljava/util/List;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")), + options.itemFactory.createType("Ljava/util/Iterator;")), options.itemFactory.createString("iterator")), true), new CfReturn(ValueType.OBJECT)), @@ -1473,17 +1516,17 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Collections;"), + options.itemFactory.createType("Ljava/util/Collections;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/List;")), + options.itemFactory.createType("Ljava/util/List;")), options.itemFactory.createString("emptyList")), false), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/List;"), + options.itemFactory.createType("Ljava/util/List;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/ListIterator;")), + options.itemFactory.createType("Ljava/util/ListIterator;")), options.itemFactory.createString("listIterator")), true), new CfReturn(ValueType.OBJECT)), @@ -1505,7 +1548,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Double;"), + options.itemFactory.createType("Ljava/lang/Double;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.doubleType), options.itemFactory.createString("doubleToLongBits")), @@ -1539,7 +1582,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Double;"), + options.itemFactory.createType("Ljava/lang/Double;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.doubleType), options.itemFactory.createString("isInfinite")), @@ -1549,7 +1592,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Double;"), + options.itemFactory.createType("Ljava/lang/Double;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.doubleType), options.itemFactory.createString("isNaN")), @@ -1581,7 +1624,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Float;"), + options.itemFactory.createType("Ljava/lang/Float;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.floatType), options.itemFactory.createString("isInfinite")), @@ -1591,7 +1634,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Float;"), + options.itemFactory.createType("Ljava/lang/Float;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.floatType), options.itemFactory.createString("isNaN")), @@ -1666,7 +1709,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"), + options.itemFactory.createType("Ljava/lang/Integer;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.intType, @@ -1726,7 +1769,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"), + options.itemFactory.createType("Ljava/lang/Integer;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.stringType, @@ -1794,7 +1837,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.stringType, @@ -1810,8 +1853,7 @@ new CfCmp(Cmp.Bias.NONE, NumericType.LONG), new CfIf(If.Type.EQ, ValueType.INT, label5), label4, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -1878,7 +1920,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"), + options.itemFactory.createType("Ljava/lang/NumberFormatException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -1959,7 +2001,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"), + options.itemFactory.createType("Ljava/lang/Integer;"), options.itemFactory.createProto( options.itemFactory.stringType, options.itemFactory.intType, @@ -1994,7 +2036,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.stringType, options.itemFactory.longType, @@ -2033,7 +2075,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.longType, @@ -2184,7 +2226,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.stringType, @@ -2240,14 +2282,13 @@ new CfLoad(ValueType.INT, 2), new CfIf(If.Type.NE, ValueType.INT, label3), label2, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfConstString(options.itemFactory.createString("empty string")), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"), + options.itemFactory.createType("Ljava/lang/NumberFormatException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -2261,8 +2302,7 @@ new CfConstNumber(36, ValueType.INT), new CfIfCmp(If.Type.LE, ValueType.INT, label5), label4, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfConstString(options.itemFactory.createString("illegal radix: ")), new CfLoad(ValueType.INT, 1), @@ -2285,7 +2325,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"), + options.itemFactory.createType("Ljava/lang/NumberFormatException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -2298,7 +2338,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.longType, @@ -2366,14 +2406,13 @@ new CfConstNumber(-1, ValueType.INT), new CfIfCmp(If.Type.NE, ValueType.INT, label15), label14, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 0), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"), + options.itemFactory.createType("Ljava/lang/NumberFormatException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -2400,7 +2439,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.longType, @@ -2410,8 +2449,7 @@ new CfNumberConversion(NumericType.LONG, NumericType.INT), new CfIfCmp(If.Type.LE, ValueType.INT, label18), label17, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfConstString(options.itemFactory.createString("Too large for unsigned long: ")), new CfLoad(ValueType.OBJECT, 0), @@ -2426,7 +2464,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"), + options.itemFactory.createType("Ljava/lang/NumberFormatException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -2570,7 +2608,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.stringType, options.itemFactory.longType, @@ -2636,7 +2674,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.stringType, options.itemFactory.longType, @@ -2674,7 +2712,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"), + options.itemFactory.createType("Ljava/lang/Integer;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.intType), options.itemFactory.createString("numberOfTrailingZeros")), @@ -2741,7 +2779,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.longType, @@ -2870,12 +2908,12 @@ new CfLoad(ValueType.INT, 4), new CfReturn(ValueType.INT), label4, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -2934,12 +2972,12 @@ new CfLoad(ValueType.LONG, 4), new CfReturn(ValueType.LONG), label7, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -2964,12 +3002,12 @@ new CfConstNumber(-2147483648, ValueType.INT), new CfIfCmp(If.Type.NE, ValueType.INT, label2), label1, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3000,12 +3038,12 @@ new CfCmp(Cmp.Bias.NONE, NumericType.LONG), new CfIf(If.Type.NE, ValueType.INT, label2), label1, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3155,7 +3193,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Math;"), + options.itemFactory.createType("Ljava/lang/Math;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.longType, @@ -3287,7 +3325,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Math;"), + options.itemFactory.createType("Ljava/lang/Math;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.longType, @@ -3316,12 +3354,12 @@ new CfConstNumber(2147483647, ValueType.INT), new CfIfCmp(If.Type.NE, ValueType.INT, label2), label1, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3352,12 +3390,12 @@ new CfCmp(Cmp.Bias.NONE, NumericType.LONG), new CfIf(If.Type.NE, ValueType.INT, label2), label1, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3405,12 +3443,12 @@ new CfLoad(ValueType.INT, 4), new CfReturn(ValueType.INT), label4, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3449,7 +3487,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.longType), options.itemFactory.createString("numberOfLeadingZeros")), @@ -3461,7 +3499,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.longType), options.itemFactory.createString("numberOfLeadingZeros")), @@ -3472,7 +3510,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.longType), options.itemFactory.createString("numberOfLeadingZeros")), @@ -3485,7 +3523,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Long;"), + options.itemFactory.createType("Ljava/lang/Long;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.longType), options.itemFactory.createString("numberOfLeadingZeros")), @@ -3545,12 +3583,12 @@ new CfLoad(ValueType.LONG, 5), new CfReturn(ValueType.LONG), label15, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3575,7 +3613,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Math;"), + options.itemFactory.createType("Ljava/lang/Math;"), options.itemFactory.createProto( options.itemFactory.longType, options.itemFactory.longType, @@ -3727,12 +3765,12 @@ new CfConstNumber(-2147483648, ValueType.INT), new CfIfCmp(If.Type.NE, ValueType.INT, label2), label1, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3762,12 +3800,12 @@ new CfCmp(Cmp.Bias.NONE, NumericType.LONG), new CfIf(If.Type.NE, ValueType.INT, label2), label1, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3795,7 +3833,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Math;"), + options.itemFactory.createType("Ljava/lang/Math;"), options.itemFactory.createProto( options.itemFactory.doubleType, options.itemFactory.doubleType), options.itemFactory.createString("nextUp")), @@ -3821,7 +3859,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Math;"), + options.itemFactory.createType("Ljava/lang/Math;"), options.itemFactory.createProto( options.itemFactory.floatType, options.itemFactory.floatType), options.itemFactory.createString("nextUp")), @@ -3866,12 +3904,12 @@ new CfLoad(ValueType.INT, 4), new CfReturn(ValueType.INT), label4, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3930,12 +3968,12 @@ new CfLoad(ValueType.LONG, 4), new CfReturn(ValueType.LONG), label7, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -3967,12 +4005,12 @@ new CfCmp(Cmp.Bias.NONE, NumericType.LONG), new CfIf(If.Type.EQ, ValueType.INT, label3), label2, - new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"), + options.itemFactory.createType("Ljava/lang/ArithmeticException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -4009,8 +4047,7 @@ new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT), new CfIfCmp(If.Type.LE, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -4103,8 +4140,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IndexOutOfBoundsException;"), + options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -4138,8 +4174,7 @@ new CfLoad(ValueType.INT, 2), new CfIfCmp(If.Type.LE, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -4214,8 +4249,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IndexOutOfBoundsException;"), + options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -4246,8 +4280,7 @@ new CfLoad(ValueType.INT, 1), new CfIfCmp(If.Type.LT, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -4304,8 +4337,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IndexOutOfBoundsException;"), + options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -4342,7 +4374,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Comparator;"), + options.itemFactory.createType("Ljava/util/Comparator;"), options.itemFactory.createProto( options.itemFactory.intType, options.itemFactory.objectType, @@ -4428,7 +4460,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.booleanArrayType, @@ -4457,7 +4489,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.byteArrayType, @@ -4486,7 +4518,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.charArrayType, @@ -4515,7 +4547,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.doubleArrayType, @@ -4544,7 +4576,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.floatArrayType, @@ -4573,7 +4605,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.intArrayType, @@ -4602,7 +4634,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.longArrayType, @@ -4631,7 +4663,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, options.itemFactory.shortArrayType, @@ -4647,24 +4679,24 @@ new CfReturn(ValueType.INT), label34, new CfLoad(ValueType.OBJECT, 0), - new CfInstanceOf(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")), new CfIf(If.Type.EQ, ValueType.INT, label38), label35, new CfLoad(ValueType.OBJECT, 1), - new CfInstanceOf(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")), new CfIf(If.Type.EQ, ValueType.INT, label36), new CfLoad(ValueType.OBJECT, 0), - new CfCheckCast(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + new CfCheckCast(options.itemFactory.createType("[Ljava/lang/Object;")), new CfLoad(ValueType.OBJECT, 1), - new CfCheckCast(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + new CfCheckCast(options.itemFactory.createType("[Ljava/lang/Object;")), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"), + options.itemFactory.createType("Ljava/util/Arrays;"), options.itemFactory.createProto( options.itemFactory.booleanType, - options.itemFactory.createSynthesizedType("[Ljava/lang/Object;"), - options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")), + options.itemFactory.createType("[Ljava/lang/Object;"), + options.itemFactory.createType("[Ljava/lang/Object;")), options.itemFactory.createString("deepEquals")), false), new CfIf(If.Type.EQ, ValueType.INT, label36), @@ -4831,7 +4863,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType, @@ -4866,19 +4898,18 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType, options.itemFactory.stringType), options.itemFactory.createString("requireNonNull")), false), - new CfCheckCast( - options.itemFactory.createSynthesizedType("Ljava/util/function/Supplier;")), + new CfCheckCast(options.itemFactory.createType("Ljava/util/function/Supplier;")), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/function/Supplier;"), + options.itemFactory.createType("Ljava/util/function/Supplier;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("get")), true), @@ -4889,7 +4920,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType, @@ -4917,14 +4948,13 @@ new CfLoad(ValueType.OBJECT, 0), new CfIf(If.Type.NE, ValueType.OBJECT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfLoad(ValueType.OBJECT, 1), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"), + options.itemFactory.createType("Ljava/lang/NullPointerException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -4952,7 +4982,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.stringType, options.itemFactory.objectType, @@ -5012,7 +5042,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Optional;"), + options.itemFactory.createType("Ljava/util/Optional;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5023,14 +5053,14 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Optional;"), + options.itemFactory.createType("Ljava/util/Optional;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("get")), false), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/function/Consumer;"), + options.itemFactory.createType("Ljava/util/function/Consumer;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.objectType), options.itemFactory.createString("accept")), @@ -5041,7 +5071,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"), + options.itemFactory.createType("Ljava/lang/Runnable;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("run")), true), @@ -5069,7 +5099,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"), + options.itemFactory.createType("Ljava/util/OptionalDouble;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5080,15 +5110,14 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"), + options.itemFactory.createType("Ljava/util/OptionalDouble;"), options.itemFactory.createProto(options.itemFactory.doubleType), options.itemFactory.createString("getAsDouble")), false), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/util/function/DoubleConsumer;"), + options.itemFactory.createType("Ljava/util/function/DoubleConsumer;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.doubleType), options.itemFactory.createString("accept")), @@ -5099,7 +5128,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"), + options.itemFactory.createType("Ljava/lang/Runnable;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("run")), true), @@ -5127,7 +5156,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"), + options.itemFactory.createType("Ljava/util/OptionalInt;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5138,14 +5167,14 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"), + options.itemFactory.createType("Ljava/util/OptionalInt;"), options.itemFactory.createProto(options.itemFactory.intType), options.itemFactory.createString("getAsInt")), false), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/function/IntConsumer;"), + options.itemFactory.createType("Ljava/util/function/IntConsumer;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.intType), options.itemFactory.createString("accept")), @@ -5156,7 +5185,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"), + options.itemFactory.createType("Ljava/lang/Runnable;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("run")), true), @@ -5184,7 +5213,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"), + options.itemFactory.createType("Ljava/util/OptionalLong;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5195,14 +5224,14 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"), + options.itemFactory.createType("Ljava/util/OptionalLong;"), options.itemFactory.createProto(options.itemFactory.longType), options.itemFactory.createString("getAsLong")), false), new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/function/LongConsumer;"), + options.itemFactory.createType("Ljava/util/function/LongConsumer;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.longType), options.itemFactory.createString("accept")), @@ -5213,7 +5242,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"), + options.itemFactory.createType("Ljava/lang/Runnable;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("run")), true), @@ -5239,7 +5268,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Optional;"), + options.itemFactory.createType("Ljava/util/Optional;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5270,7 +5299,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"), + options.itemFactory.createType("Ljava/util/OptionalDouble;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5301,7 +5330,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"), + options.itemFactory.createType("Ljava/util/OptionalInt;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5332,7 +5361,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"), + options.itemFactory.createType("Ljava/util/OptionalLong;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5365,7 +5394,7 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), @@ -5376,7 +5405,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Optional;"), + options.itemFactory.createType("Ljava/util/Optional;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5389,23 +5418,23 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/function/Supplier;"), + options.itemFactory.createType("Ljava/util/function/Supplier;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("get")), true), - new CfCheckCast(options.itemFactory.createSynthesizedType("Ljava/util/Optional;")), + new CfCheckCast(options.itemFactory.createType("Ljava/util/Optional;")), new CfStore(ValueType.OBJECT, 2), label4, new CfLoad(ValueType.OBJECT, 2), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Objects;"), + options.itemFactory.createType("Ljava/util/Objects;"), options.itemFactory.createProto( options.itemFactory.objectType, options.itemFactory.objectType), options.itemFactory.createString("requireNonNull")), false), - new CfCheckCast(options.itemFactory.createSynthesizedType("Ljava/util/Optional;")), + new CfCheckCast(options.itemFactory.createType("Ljava/util/Optional;")), new CfReturn(ValueType.OBJECT), label5), ImmutableList.of(), @@ -5427,7 +5456,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Optional;"), + options.itemFactory.createType("Ljava/util/Optional;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5437,16 +5466,16 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Optional;"), + options.itemFactory.createType("Ljava/util/Optional;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("get")), false), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"), + options.itemFactory.createType("Ljava/util/stream/Stream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"), + options.itemFactory.createType("Ljava/util/stream/Stream;"), options.itemFactory.objectType), options.itemFactory.createString("of")), true), @@ -5455,9 +5484,9 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"), + options.itemFactory.createType("Ljava/util/stream/Stream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;")), + options.itemFactory.createType("Ljava/util/stream/Stream;")), options.itemFactory.createString("empty")), true), new CfReturn(ValueType.OBJECT), @@ -5481,7 +5510,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"), + options.itemFactory.createType("Ljava/util/OptionalDouble;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5491,17 +5520,16 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"), + options.itemFactory.createType("Ljava/util/OptionalDouble;"), options.itemFactory.createProto(options.itemFactory.doubleType), options.itemFactory.createString("getAsDouble")), false), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/DoubleStream;"), + options.itemFactory.createType("Ljava/util/stream/DoubleStream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType( - "Ljava/util/stream/DoubleStream;"), + options.itemFactory.createType("Ljava/util/stream/DoubleStream;"), options.itemFactory.doubleType), options.itemFactory.createString("of")), true), @@ -5510,10 +5538,9 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/DoubleStream;"), + options.itemFactory.createType("Ljava/util/stream/DoubleStream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType( - "Ljava/util/stream/DoubleStream;")), + options.itemFactory.createType("Ljava/util/stream/DoubleStream;")), options.itemFactory.createString("empty")), true), new CfReturn(ValueType.OBJECT), @@ -5537,7 +5564,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"), + options.itemFactory.createType("Ljava/util/OptionalInt;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5547,16 +5574,16 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"), + options.itemFactory.createType("Ljava/util/OptionalInt;"), options.itemFactory.createProto(options.itemFactory.intType), options.itemFactory.createString("getAsInt")), false), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;"), + options.itemFactory.createType("Ljava/util/stream/IntStream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;"), + options.itemFactory.createType("Ljava/util/stream/IntStream;"), options.itemFactory.intType), options.itemFactory.createString("of")), true), @@ -5565,9 +5592,9 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;"), + options.itemFactory.createType("Ljava/util/stream/IntStream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;")), + options.itemFactory.createType("Ljava/util/stream/IntStream;")), options.itemFactory.createString("empty")), true), new CfReturn(ValueType.OBJECT), @@ -5591,7 +5618,7 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"), + options.itemFactory.createType("Ljava/util/OptionalLong;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("isPresent")), false), @@ -5601,16 +5628,16 @@ new CfInvoke( 182, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"), + options.itemFactory.createType("Ljava/util/OptionalLong;"), options.itemFactory.createProto(options.itemFactory.longType), options.itemFactory.createString("getAsLong")), false), new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;"), + options.itemFactory.createType("Ljava/util/stream/LongStream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;"), + options.itemFactory.createType("Ljava/util/stream/LongStream;"), options.itemFactory.longType), options.itemFactory.createString("of")), true), @@ -5619,9 +5646,9 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;"), + options.itemFactory.createType("Ljava/util/stream/LongStream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;")), + options.itemFactory.createType("Ljava/util/stream/LongStream;")), options.itemFactory.createString("empty")), true), new CfReturn(ValueType.OBJECT), @@ -5723,9 +5750,9 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"), + options.itemFactory.createType("Ljava/util/stream/Stream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;")), + options.itemFactory.createType("Ljava/util/stream/Stream;")), options.itemFactory.createString("empty")), true), new CfGoto(label2), @@ -5734,9 +5761,9 @@ new CfInvoke( 184, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"), + options.itemFactory.createType("Ljava/util/stream/Stream;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"), + options.itemFactory.createType("Ljava/util/stream/Stream;"), options.itemFactory.objectType), options.itemFactory.createString("of")), true), @@ -5849,14 +5876,13 @@ label0, new CfLoad(ValueType.OBJECT, 0), new CfIf(If.Type.NE, ValueType.OBJECT, label1), - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfConstString(options.itemFactory.createString("delimiter")), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"), + options.itemFactory.createType("Ljava/lang/NullPointerException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -5965,14 +5991,13 @@ label0, new CfLoad(ValueType.OBJECT, 0), new CfIf(If.Type.NE, ValueType.OBJECT, label1), - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfConstString(options.itemFactory.createString("delimiter")), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"), + options.itemFactory.createType("Ljava/lang/NullPointerException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")), @@ -5994,9 +6019,9 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/Iterable;"), + options.itemFactory.createType("Ljava/lang/Iterable;"), options.itemFactory.createProto( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")), + options.itemFactory.createType("Ljava/util/Iterator;")), options.itemFactory.createString("iterator")), true), new CfStore(ValueType.OBJECT, 3), @@ -6005,7 +6030,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("hasNext")), true), @@ -6016,7 +6041,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("next")), true), @@ -6036,7 +6061,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.booleanType), options.itemFactory.createString("hasNext")), true), @@ -6060,7 +6085,7 @@ new CfInvoke( 185, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"), + options.itemFactory.createType("Ljava/util/Iterator;"), options.itemFactory.createProto(options.itemFactory.objectType), options.itemFactory.createString("next")), true), @@ -6115,8 +6140,7 @@ new CfLoad(ValueType.INT, 1), new CfIf(If.Type.GE, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/IllegalArgumentException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfNew(options.itemFactory.stringBuilderType), new CfStackInstruction(CfStackInstruction.Opcode.Dup), @@ -6155,8 +6179,7 @@ new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType( - "Ljava/lang/IllegalArgumentException;"), + options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"), options.itemFactory.createProto( options.itemFactory.voidType, options.itemFactory.stringType), options.itemFactory.createString("<init>")),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java index da20118..a356e8b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -112,18 +112,18 @@ break; case PACKAGE: if (entry.value.size == 0) { - if (!method.method.holder.descriptor.contains(dexItemFactory.descriptorSeparator)) { + if (!method.holder().descriptor.contains(dexItemFactory.descriptorSeparator)) { transformation = entry.entry.getTransformation(); } - } else if (method.method.holder.descriptor.startsWith(entry.value)) { + } else if (method.holder().descriptor.startsWith(entry.value)) { transformation = entry.entry.getTransformation(); } break; case CLASS: - if (method.method.holder.descriptor.equals(entry.value)) { + if (method.holder().descriptor.equals(entry.value)) { transformation = entry.entry.getTransformation(); } - if (isDescriptorForClassOrInnerClass(entry.value, method.method.holder.descriptor)) { + if (isDescriptorForClassOrInnerClass(entry.value, method.holder().descriptor)) { transformation = entry.entry.getTransformation(); } break; @@ -311,7 +311,7 @@ if (method.isClassInitializer()) { clinit = method; } else { - DexClass clazz = appView.definitionFor(method.method.holder); + DexClass clazz = appView.definitionFor(method.holder()); if (clazz == null) { return; } @@ -328,7 +328,7 @@ if (current.isInvokeMethod()) { InvokeMethod invoke = current.asInvokeMethod(); if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) { - if (method.method.holder == dexItemFactory.kotlin.kotlinAssertions) { + if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) { rewriteKotlinAssertionEnable(code, transformation, iterator, invoke); } else { iterator.replaceCurrentInstruction(code.createIntConstant(0));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java index 368b58a..35895b7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -98,7 +98,7 @@ continue; } } - Collection<DexEncodedMethod> targets = invoke.lookupTargets(appView, context.method.holder); + Collection<DexEncodedMethod> targets = invoke.lookupTargets(appView, context.holder()); assert invoke.isInvokeMethodWithDynamicDispatch() // For other invocation types, the size of targets should be at most one. || targets == null || targets.size() <= 1; @@ -231,7 +231,7 @@ if (abstractValue.isSingleValue()) { assert appView.options().enablePropagationOfConstantsAtCallSites; SingleValue singleValue = abstractValue.asSingleValue(); - if (singleValue.isMaterializableInContext(appView, code.method.method.holder)) { + if (singleValue.isMaterializableInContext(appView, code.method.holder())) { Instruction replacement = singleValue.createMaterializingInstruction(appView, code, instr); replacement.setPosition(instr.getPosition());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java index 025c178..ce853a2 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -133,7 +133,7 @@ return ClassInitializerDefaultsResult.empty(); } - DexClass clazz = appView.definitionFor(method.method.holder); + DexClass clazz = appView.definitionFor(method.holder()); if (clazz == null) { return ClassInitializerDefaultsResult.empty(); } @@ -160,7 +160,7 @@ Value value = put.value(); if (unnecessaryStaticPuts.contains(put)) { if (fieldType == dexItemFactory.stringType) { - fieldsWithStaticValues.put(field, getDexStringValue(value, method.method.holder)); + fieldsWithStaticValues.put(field, getDexStringValue(value, method.holder())); } else if (fieldType.isClassType() || fieldType.isArrayType()) { if (value.isZero()) { fieldsWithStaticValues.put(field, DexValueNull.NULL); @@ -380,7 +380,7 @@ } else if (instruction.isStaticGet()) { StaticGet get = instruction.asStaticGet(); DexEncodedField field = appView.appInfo().resolveField(get.getField()); - if (field != null && field.field.holder == clazz.type) { + if (field != null && field.holder() == clazz.type) { isReadBefore.add(field.field); } else if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) { // Reading another field is only OK if the read does not have side-effects.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java index 5b78d8a..fee1f02 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1115,7 +1115,7 @@ BasicBlock defaultTarget = theSwitch.fallthroughBlock(); SwitchCaseEliminator eliminator = null; BasicBlockBehavioralSubsumption behavioralSubsumption = - new BasicBlockBehavioralSubsumption(appView, code.method.method.holder); + new BasicBlockBehavioralSubsumption(appView, code.method.holder()); // Compute the set of switch cases that can be removed. for (int i = 0; i < theSwitch.numberOfKeys(); i++) { @@ -1231,7 +1231,7 @@ } // Check if the invoked method is known to return one of its arguments. - DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.method.holder); + DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.holder()); if (target != null && target.getOptimizationInfo().returnsArgument()) { int argumentIndex = target.getOptimizationInfo().getReturnedArgument(); // Replace the out value of the invoke with the argument and ignore the out value. @@ -1353,7 +1353,7 @@ // If the cast type is not accessible in the current context, we should not remove the cast // in order to preserve IllegalAccessError. Note that JVM and ART behave differently: see // {@link com.android.tools.r8.ir.optimize.checkcast.IllegalAccessErrorTest}. - if (!isTypeVisibleFromContext(appView, code.method.method.holder, castType)) { + if (!isTypeVisibleFromContext(appView, code.method.holder(), castType)) { return RemoveCheckCastInstructionIfTrivialResult.NO_REMOVALS; } @@ -1410,7 +1410,7 @@ InstanceOf instanceOf, InstructionListIterator it, IRCode code) { // If the instance-of type is not accessible in the current context, we should not remove the // instance-of instruction in order to preserve IllegalAccessError. - if (!isTypeVisibleFromContext(appView, code.method.method.holder, instanceOf.type())) { + if (!isTypeVisibleFromContext(appView, code.method.holder(), instanceOf.type())) { return false; } @@ -2050,8 +2050,7 @@ for (ConstInstruction value : values) { stringValues.add(value.outValue()); } - Value invokeValue = - code.createValue(newArray.outValue().getType(), newArray.getLocalInfo()); + Value invokeValue = code.createValue(newArray.getOutType(), newArray.getLocalInfo()); InvokeNewArray invoke = new InvokeNewArray(dexItemFactory.stringArrayType, invokeValue, stringValues); for (Value value : newArray.inValues()) { @@ -2469,7 +2468,7 @@ } } } else { - DexType context = code.method.method.holder; + DexType context = code.method.holder(); AbstractValue abstractValue = lhs.getAbstractValue(appView, context); if (abstractValue.isSingleConstClassValue() || abstractValue.isSingleFieldValue()) { AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context); @@ -2761,7 +2760,7 @@ InvokeMethod invoke = insn.asInvokeMethod(); DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView.withLiveness(), code.method.method.holder); + invoke.lookupSingleTarget(appView.withLiveness(), code.method.holder()); if (singleTarget == null || !singleTarget.getOptimizationInfo().neverReturnsNormally()) { continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java index 2dff525..c98c262 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -88,7 +88,7 @@ public void canonicalize(AppView<?> appView, IRCode code) { DexEncodedMethod method = code.method; - DexType context = method.method.holder; + DexType context = method.holder(); Object2ObjectLinkedOpenCustomHashMap<Instruction, List<Value>> valuesDefinedByConstant = new Object2ObjectLinkedOpenCustomHashMap<>( new Strategy<Instruction>() { @@ -149,8 +149,7 @@ continue; } SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue(); - if (method.isClassInitializer() - && method.method.holder == singleFieldValue.getField().holder) { + if (method.isClassInitializer() && method.holder() == singleFieldValue.getField().holder) { // Avoid that canonicalization inserts a read before the unique write in the class // initializer, as that would change the program behavior. continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index d225220..d9773d9 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -105,7 +105,7 @@ return true; } - DexClass clazz = appView.definitionFor(singleTarget.method.holder); + DexClass clazz = appView.definitionFor(singleTarget.holder()); if (!clazz.isProgramClass()) { if (clazz.isClasspathClass()) { whyAreYouNotInliningReporter.reportClasspathMethod(); @@ -211,12 +211,12 @@ // Don't inline code with references beyond root main dex classes into a root main dex class. // If we do this it can increase the size of the main dex dependent classes. if (reason != Reason.FORCE - && inlineeRefersToClassesNotInMainDex(method.method.holder, singleTarget)) { + && inlineeRefersToClassesNotInMainDex(method.holder(), singleTarget)) { whyAreYouNotInliningReporter.reportInlineeRefersToClassesNotInMainDex(); return false; } assert reason != Reason.FORCE - || !inlineeRefersToClassesNotInMainDex(method.method.holder, singleTarget); + || !inlineeRefersToClassesNotInMainDex(method.holder(), singleTarget); return true; } @@ -360,8 +360,8 @@ // - the current method has already triggered the holder for the target method to be // initialized, or // - there is no non-trivial class initializer. - DexType targetHolder = target.method.holder; - if (appView.appInfo().isSubtype(method.method.holder, targetHolder)) { + DexType targetHolder = target.holder(); + if (appView.appInfo().isSubtype(method.holder(), targetHolder)) { return true; } DexClass clazz = appView.definitionFor(targetHolder); @@ -374,14 +374,14 @@ appView.withInitializedClassesInInstanceMethods( analysis -> analysis.isClassDefinitelyLoadedInInstanceMethodsOn( - target.method.holder, method.method.holder), + target.holder(), method.holder()), false); if (targetIsGuaranteedToBeInitialized) { return true; } } if (classInitializationAnalysis.isClassDefinitelyLoadedBeforeInstruction( - target.method.holder, invoke)) { + target.holder(), invoke)) { return true; } // Check for class initializer side effects when loading this class, as inlining might remove @@ -445,8 +445,8 @@ // Allow inlining a constructor into a constructor of the same class, as the constructor code // is expected to adhere to the VM specification. - DexType callerMethodHolder = method.method.holder; - DexType calleeMethodHolder = inlinee.method.method.holder; + DexType callerMethodHolder = method.holder(); + DexType calleeMethodHolder = inlinee.method.holder(); // Calling a constructor on the same class from a constructor can always be inlined. if (method.isInstanceInitializer() && callerMethodHolder == calleeMethodHolder) { return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java index 82ea8ab..897b85b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -136,7 +136,7 @@ if (target == null) { continue; } - DexType holderType = target.method.holder; + DexType holderType = target.holder(); DexClass holderClass = appView.definitionFor(holderType); // Make sure we are not landing on another interface, e.g., interface's default method. if (holderClass == null || holderClass.isInterface()) { @@ -284,7 +284,7 @@ // Most likely due to a missing class, or invoke is already as specific as it gets. return target; } - DexClass newTargetClass = appView.definitionFor(newTarget.method.holder); + DexClass newTargetClass = appView.definitionFor(newTarget.holder()); if (newTargetClass == null || newTargetClass.isLibraryClass() || !canInvokeTargetWithInvokeVirtual(newTarget) @@ -296,13 +296,12 @@ } private boolean canInvokeTargetWithInvokeVirtual(DexEncodedMethod target) { - return target.isNonPrivateVirtualMethod() - && appView.isInterface(target.method.holder).isFalse(); + return target.isNonPrivateVirtualMethod() && appView.isInterface(target.holder()).isFalse(); } private boolean hasAccessToInvokeTargetFromContext(DexEncodedMethod target, DexType context) { assert !target.accessFlags.isPrivate(); - DexType holder = target.method.holder; + DexType holder = target.holder(); if (holder == context) { // It is always safe to invoke a method from the same enclosing class. return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java index 8048e3a..d518ef6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -78,8 +78,7 @@ TypeElement.fromDexType(invokedMethod.holder, definitelyNotNull(), appView); dynamicLowerBoundType = null; } else { - DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView, code.method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder()); if (singleTarget == null) { continue; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java index 5dba772..76ca615 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -110,7 +110,7 @@ } }); - DexType context = code.method.method.holder; + DexType context = code.method.holder(); // Collect invocations along with arguments. for (BasicBlock block : code.blocks) { for (Instruction current : block.getInstructions()) { @@ -143,7 +143,7 @@ } // Verify that the target method is accessible in the current context. if (!isMemberVisibleFromOriginalContext( - appView, context, target.method.holder, target.accessFlags)) { + appView, context, target.holder(), target.accessFlags)) { continue; } // Check if the call could throw a NPE as a result of the receiver being null. @@ -207,7 +207,7 @@ } } Value canonicalizedValue = - code.createValue(invoke.outValue().getType(), invoke.outValue().getLocalInfo()); + code.createValue(invoke.getOutType(), invoke.outValue().getLocalInfo()); Invoke canonicalizedInvoke = Invoke.create( invoke.getType(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index b56dece..33d982a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -164,7 +164,7 @@ new InliningConstraints(appView, GraphLense.getIdentityLense()); for (Instruction instruction : code.instructions()) { ConstraintWithTarget state = - instructionAllowedForInlining(instruction, inliningConstraints, method.method.holder); + instructionAllowedForInlining(instruction, inliningConstraints, method.holder()); if (state == ConstraintWithTarget.NEVER) { result = state; break; @@ -192,12 +192,12 @@ } boolean hasInliningAccess(DexEncodedMethod method, DexEncodedMethod target) { - if (!isVisibleWithFlags(target.method.holder, method.method.holder, target.accessFlags)) { + if (!isVisibleWithFlags(target.holder(), method.holder(), target.accessFlags)) { return false; } // The class needs also to be visible for us to have access. - DexClass targetClass = appView.definitionFor(target.method.holder); - return isVisibleWithFlags(target.method.holder, method.method.holder, targetClass.accessFlags); + DexClass targetClass = appView.definitionFor(target.holder()); + return isVisibleWithFlags(target.holder(), method.holder(), targetClass.accessFlags); } private boolean isVisibleWithFlags(DexType target, DexType context, AccessFlags flags) { @@ -710,7 +710,7 @@ lockValue = code.createValue( TypeElement.fromDexType(dexItemFactory.objectType, definitelyNotNull(), appView)); - monitorEnterBlockIterator.add(new ConstClass(lockValue, target.method.holder)); + monitorEnterBlockIterator.add(new ConstClass(lockValue, target.holder())); } else { lockValue = entryBlock.getInstructions().getFirst().asArgument().outValue(); } @@ -738,10 +738,10 @@ if (inliningIRProvider.shouldApplyCodeRewritings(code.method)) { assert lensCodeRewriter != null; - lensCodeRewriter.rewrite(code, target); if (enumUnboxer != null) { enumUnboxer.rewriteCode(code); } + lensCodeRewriter.rewrite(code, target); } if (lambdaMerger != null) { lambdaMerger.rewriteCodeForInlining(target, code, context, inliningIRProvider); @@ -964,7 +964,7 @@ if (current.isInvokeMethod()) { InvokeMethod invoke = current.asInvokeMethod(); // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()! - DexEncodedMethod singleTarget = oracle.lookupSingleTarget(invoke, context.method.holder); + DexEncodedMethod singleTarget = oracle.lookupSingleTarget(invoke, context.holder()); if (singleTarget == null) { WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget(invoke, appView, context); continue; @@ -1115,8 +1115,8 @@ // method holder as a fallback. receiverType = invoke.getInvokedMethod().holder; } - if (!appView.appInfo().isSubtype(receiverType, target.method.holder)) { - return target.method.holder; + if (!appView.appInfo().isSubtype(receiverType, target.holder())) { + return target.holder(); } } return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java index a1307a0..ea77caf 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -197,7 +197,7 @@ DexEncodedMethod alternativeDexEncodedMethod = lookupFunction.apply(singleResolutionResult, superContext); if (alternativeDexEncodedMethod != null - && alternativeDexEncodedMethod.method.holder == superContext.type) { + && alternativeDexEncodedMethod.holder() == superContext.type) { return alternativeDexEncodedMethod; } return null; @@ -354,8 +354,8 @@ // `invocationContext` has access to the definition of the field. // // See, for example, InlineNonReboundFieldTest (b/128604123). - if (field.holder != target.field.holder) { - DexType actualFieldHolder = graphLense.lookupType(target.field.holder); + if (field.holder != target.holder()) { + DexType actualFieldHolder = graphLense.lookupType(target.holder()); fieldConstraintWithTarget = ConstraintWithTarget.meet( fieldConstraintWithTarget, @@ -379,7 +379,7 @@ return ConstraintWithTarget.ALWAYS; } if (target != null) { - DexType methodHolder = graphLense.lookupType(target.method.holder); + DexType methodHolder = graphLense.lookupType(target.holder()); DexClass methodClass = appView.definitionFor(methodHolder); if (methodClass != null) { if (!allowStaticInterfaceMethodCalls && methodClass.isInterface() && target.hasCode()) { @@ -423,7 +423,7 @@ return ConstraintWithTarget.NEVER; } - DexType methodHolder = graphLense.lookupType(resolutionTarget.method.holder); + DexType methodHolder = graphLense.lookupType(resolutionTarget.holder()); DexClass methodClass = appView.definitionFor(methodHolder); assert methodClass != null; ConstraintWithTarget methodConstraintWithTarget =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java index 4bf7a6d..bf7aa9b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize; import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; +import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexDefinition; @@ -14,7 +15,6 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; @@ -139,10 +139,9 @@ .createMaterializingInstruction(appView, code, instruction); } - TypeElement typeLattice = instruction.outValue().getType(); if (returnValueRule.isField()) { DexField field = returnValueRule.getField(); - assert typeLattice == TypeElement.fromDexType(field.type, Nullability.maybeNull(), appView); + assert instruction.getOutType() == TypeElement.fromDexType(field.type, maybeNull(), appView); DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field.holder, field); if (staticField == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java index c6694f4..508e394 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
@@ -46,7 +46,7 @@ InstructionListIterator iterator = code.instructionListIterator(); DexClass callerHolderClass = appView.definitionFor(callerHolder); assert callerHolderClass != null; - assert code.method.method.holder != callerHolder; + assert code.method.holder() != callerHolder; while (iterator.hasNext()) { Instruction instruction = iterator.next(); if (instruction.isInvokeDirect()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java index 15904c5..2e41703 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -98,8 +98,7 @@ } } - DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView, code.method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder()); if (singleTarget != null) { MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java index ab9fa71..d9ebcf6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -862,7 +862,7 @@ // See whether we could move this invoke somewhere else. We reuse the logic from inlining // here, as the constraints are the same. ConstraintWithTarget constraint = - invoke.inliningConstraint(inliningConstraints, method.method.holder); + invoke.inliningConstraint(inliningConstraints, method.holder()); if (constraint != ConstraintWithTarget.ALWAYS) { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java index de9d4cc..81b32e8 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -4,12 +4,15 @@ package com.android.tools.r8.ir.optimize; +import static com.android.tools.r8.utils.PredicateUtils.not; + import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; import com.android.tools.r8.ir.analysis.value.SingleValue; import com.android.tools.r8.ir.code.BasicBlock; @@ -29,12 +32,11 @@ import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection; import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo; -import com.android.tools.r8.utils.SetUtils; import com.google.common.collect.Sets; -import java.util.ArrayList; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.HashMap; import java.util.IdentityHashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -56,18 +58,11 @@ private final Set<Value> affectedValues = Sets.newIdentityHashSet(); // Maps keeping track of fields that have an already loaded value at basic block entry. - private final Map<BasicBlock, Set<DexType>> activeInitializedClassesAtEntry = - new IdentityHashMap<>(); - private final Map<BasicBlock, Map<FieldAndObject, FieldValue>> activeInstanceFieldsAtEntry = - new IdentityHashMap<>(); - private final Map<BasicBlock, Map<DexField, FieldValue>> activeStaticFieldsAtEntry = - new IdentityHashMap<>(); + private final Map<BasicBlock, State> activeStateAtExit = new IdentityHashMap<>(); // Maps keeping track of fields with already loaded values for the current block during // elimination. - private Set<DexType> activeInitializedClasses; - private Map<FieldAndObject, FieldValue> activeInstanceFieldValues; - private Map<DexField, FieldValue> activeStaticFieldValues; + private State activeState; public RedundantFieldLoadElimination(AppView<?> appView, IRCode code) { this.appView = appView; @@ -78,7 +73,7 @@ public static boolean shouldRun(AppView<?> appView, IRCode code) { return appView.options().enableRedundantFieldLoadElimination - && code.metadata().mayHaveFieldGet(); + && (code.metadata().mayHaveFieldGet() || code.metadata().mayHaveInitClass()); } private interface FieldValue { @@ -145,41 +140,28 @@ } } - private boolean couldBeVolatile(DexField field) { - DexEncodedField definition; + private DexEncodedField resolveField(DexField field) { if (appView.enableWholeProgramOptimizations()) { - definition = appView.appInfo().resolveField(field); - } else { - if (field.holder != method.method.holder) { - return true; - } - definition = appView.definitionFor(field); + return appView.appInfo().resolveField(field); } - return definition == null || definition.accessFlags.isVolatile(); + if (field.holder == method.holder()) { + return appView.definitionFor(field); + } + return null; } public void run() { - DexType context = method.method.holder; + DexType context = method.holder(); for (BasicBlock block : dominatorTree.getSortedBlocks()) { - activeInitializedClasses = - activeInitializedClassesAtEntry.containsKey(block) - ? activeInitializedClassesAtEntry.get(block) - : Sets.newIdentityHashSet(); - activeInstanceFieldValues = - activeInstanceFieldsAtEntry.containsKey(block) - ? activeInstanceFieldsAtEntry.get(block) - : new HashMap<>(); - activeStaticFieldValues = - activeStaticFieldsAtEntry.containsKey(block) - ? activeStaticFieldsAtEntry.get(block) - : new IdentityHashMap<>(); + computeActiveStateOnBlockEntry(block); InstructionListIterator it = block.listIterator(code); while (it.hasNext()) { Instruction instruction = it.next(); if (instruction.isFieldInstruction()) { DexField field = instruction.asFieldInstruction().getField(); - if (couldBeVolatile(field)) { - killAllActiveFields(); + DexEncodedField definition = resolveField(field); + if (definition == null || definition.isVolatile()) { + killAllNonFinalActiveFields(); continue; } @@ -190,56 +172,71 @@ } Value object = instanceGet.object().getAliasedValue(); FieldAndObject fieldAndObject = new FieldAndObject(field, object); - if (activeInstanceFieldValues.containsKey(fieldAndObject)) { - FieldValue replacement = activeInstanceFieldValues.get(fieldAndObject); + FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject); + if (replacement != null) { replacement.eliminateRedundantRead(it, instanceGet); } else { - activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(instanceGet.value())); + activeState.putNonFinalInstanceField( + fieldAndObject, new ExistingValue(instanceGet.value())); } } else if (instruction.isInstancePut()) { InstancePut instancePut = instruction.asInstancePut(); // An instance-put instruction can potentially write the given field on all objects // because of aliases. - killActiveFields(instancePut); + killNonFinalActiveFields(instancePut); // ... but at least we know the field value for this particular object. Value object = instancePut.object().getAliasedValue(); FieldAndObject fieldAndObject = new FieldAndObject(field, object); - activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(instancePut.value())); + ExistingValue value = new ExistingValue(instancePut.value()); + if (definition.isFinal()) { + assert method.isInstanceInitializer() || verifyWasInstanceInitializer(); + activeState.putFinalInstanceField(fieldAndObject, value); + } else { + activeState.putNonFinalInstanceField(fieldAndObject, value); + } } else if (instruction.isStaticGet()) { StaticGet staticGet = instruction.asStaticGet(); if (staticGet.outValue().hasLocalInfo()) { continue; } - if (activeStaticFieldValues.containsKey(field)) { - FieldValue replacement = activeStaticFieldValues.get(field); + FieldValue replacement = activeState.getStaticFieldValue(field); + if (replacement != null) { replacement.eliminateRedundantRead(it, staticGet); } else { // A field get on a different class can cause <clinit> to run and change static // field values. - killActiveFields(staticGet); - activeStaticFieldValues.put(field, new ExistingValue(staticGet.value())); + killNonFinalActiveFields(staticGet); + activeState.putNonFinalStaticField(field, new ExistingValue(staticGet.value())); } } else if (instruction.isStaticPut()) { StaticPut staticPut = instruction.asStaticPut(); // A field put on a different class can cause <clinit> to run and change static // field values. - killActiveFields(staticPut); - activeStaticFieldValues.put(field, new ExistingValue(staticPut.value())); + killNonFinalActiveFields(staticPut); + ExistingValue value = new ExistingValue(staticPut.value()); + if (definition.isFinal()) { + assert method.isClassInitializer(); + activeState.putFinalStaticField(field, value); + } else { + activeState.putNonFinalStaticField(field, value); + } } } else if (instruction.isInitClass()) { InitClass initClass = instruction.asInitClass(); assert !initClass.outValue().hasAnyUsers(); - if (activeInitializedClasses.contains(initClass.getClassValue())) { + DexType clazz = initClass.getClassValue(); + if (activeState.isClassInitialized(clazz)) { it.removeOrReplaceByDebugLocalRead(); } + activeState.markClassAsInitialized(clazz); } else if (instruction.isMonitor()) { if (instruction.asMonitor().isEnter()) { - killAllActiveFields(); + killAllNonFinalActiveFields(); } } else if (instruction.isInvokeDirect()) { handleInvokeDirect(instruction.asInvokeDirect()); } else if (instruction.isInvokeMethod() || instruction.isInvokeCustom()) { - killAllActiveFields(); + killAllNonFinalActiveFields(); } else if (instruction.isNewInstance()) { NewInstance newInstance = instruction.asNewInstance(); if (newInstance.clazz.classInitializationMayHaveSideEffects( @@ -247,7 +244,7 @@ // Types that are a super type of `context` are guaranteed to be initialized already. type -> appView.isSubtype(context, type).isTrue(), Sets.newIdentityHashSet())) { - killAllActiveFields(); + killAllNonFinalActiveFields(); } } else { // If the current instruction could trigger a method invocation, it could also cause field @@ -286,7 +283,7 @@ : "Unexpected instruction of type " + instruction.getClass().getTypeName(); } } - propagateActiveStateFrom(block); + recordActiveStateOnBlockExit(block); } if (!affectedValues.isEmpty()) { new TypeAnalysis(appView).narrowing(affectedValues); @@ -294,22 +291,33 @@ assert code.isConsistentSSA(); } + private boolean verifyWasInstanceInitializer() { + VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses(); + assert verticallyMergedClasses != null; + assert verticallyMergedClasses.isTarget(method.holder()); + assert appView + .dexItemFactory() + .isConstructor(appView.graphLense().getOriginalMethodSignature(method.method)); + assert method.getOptimizationInfo().forceInline(); + return true; + } + private void handleInvokeDirect(InvokeDirect invoke) { if (!appView.enableWholeProgramOptimizations()) { - killAllActiveFields(); + killAllNonFinalActiveFields(); return; } DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); if (singleTarget == null || !singleTarget.isInstanceInitializer()) { - killAllActiveFields(); + killAllNonFinalActiveFields(); return; } InstanceInitializerInfo instanceInitializerInfo = singleTarget.getOptimizationInfo().getInstanceInitializerInfo(); if (instanceInitializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) { - killAllActiveFields(); + killAllNonFinalActiveFields(); } InstanceFieldInitializationInfoCollection fieldInitializationInfos = @@ -325,13 +333,13 @@ invoke.getArgument(info.asArgumentInitializationInfo().getArgumentIndex()); Value object = invoke.getReceiver().getAliasedValue(); FieldAndObject fieldAndObject = new FieldAndObject(field.field, object); - activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(value)); + activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value)); } else if (info.isSingleValue()) { SingleValue value = info.asSingleValue(); if (value.isMaterializableInContext(appView, method.holder())) { Value object = invoke.getReceiver().getAliasedValue(); FieldAndObject fieldAndObject = new FieldAndObject(field.field, object); - activeInstanceFieldValues.put(fieldAndObject, new MaterializableValue(value)); + activeState.putNonFinalInstanceField(fieldAndObject, new MaterializableValue(value)); } } else { assert info.isTypeInitializationInfo(); @@ -339,84 +347,206 @@ }); } - private void propagateActiveStateFrom(BasicBlock block) { - for (BasicBlock successor : block.getSuccessors()) { + private void computeActiveStateOnBlockEntry(BasicBlock block) { + if (block.isEntry()) { + activeState = new State(); + return; + } + Deque<State> predecessorExitStates = new ArrayDeque<>(block.getPredecessors().size()); + for (BasicBlock predecessor : block.getPredecessors()) { + State predecessorExitState = activeStateAtExit.get(predecessor); + if (predecessorExitState == null) { + // Not processed yet. + activeState = new State(); + return; + } // Allow propagation across exceptional edges, just be careful not to propagate if the // throwing instruction is a field instruction. - if (successor.getPredecessors().size() == 1) { - if (block.hasCatchSuccessor(successor)) { - Instruction exceptionalExit = block.exceptionalExit(); - if (exceptionalExit != null) { - if (exceptionalExit.isFieldInstruction()) { - killActiveFieldsForExceptionalExit(exceptionalExit.asFieldInstruction()); - } else if (exceptionalExit.isInitClass()) { - killActiveInitializedClassesForExceptionalExit(exceptionalExit.asInitClass()); - } + if (predecessor.hasCatchSuccessor(block)) { + Instruction exceptionalExit = predecessor.exceptionalExit(); + if (exceptionalExit != null) { + predecessorExitState = new State(predecessorExitState); + if (exceptionalExit.isFieldInstruction()) { + predecessorExitState.killActiveFieldsForExceptionalExit( + exceptionalExit.asFieldInstruction()); + } else if (exceptionalExit.isInitClass()) { + predecessorExitState.killActiveInitializedClassesForExceptionalExit( + exceptionalExit.asInitClass()); } } - assert !activeInitializedClassesAtEntry.containsKey(successor); - activeInitializedClassesAtEntry.put( - successor, SetUtils.newIdentityHashSet(activeInitializedClasses)); - assert !activeInstanceFieldsAtEntry.containsKey(successor); - activeInstanceFieldsAtEntry.put(successor, new HashMap<>(activeInstanceFieldValues)); - assert !activeStaticFieldsAtEntry.containsKey(successor); - activeStaticFieldsAtEntry.put(successor, new IdentityHashMap<>(activeStaticFieldValues)); } + predecessorExitStates.addLast(predecessorExitState); } + State state = new State(predecessorExitStates.removeFirst()); + predecessorExitStates.forEach(state::intersect); + activeState = state; } - private void killAllActiveFields() { - activeInstanceFieldValues.clear(); - activeStaticFieldValues.clear(); + private void recordActiveStateOnBlockExit(BasicBlock block) { + assert !activeStateAtExit.containsKey(block); + activeStateAtExit.put(block, activeState); } - private void killActiveFields(FieldInstruction instruction) { + private void killAllNonFinalActiveFields() { + activeState.clearNonFinalInstanceFields(); + activeState.clearNonFinalStaticFields(); + } + + private void killNonFinalActiveFields(FieldInstruction instruction) { DexField field = instruction.getField(); if (instruction.isInstancePut()) { // Remove all the field/object pairs that refer to this field to make sure // that we are conservative. - List<FieldAndObject> keysToRemove = new ArrayList<>(); - for (FieldAndObject key : activeInstanceFieldValues.keySet()) { - if (key.field == field) { - keysToRemove.add(key); - } - } - keysToRemove.forEach(activeInstanceFieldValues::remove); + activeState.removeNonFinalInstanceFields(field); } else if (instruction.isStaticPut()) { - if (field.holder != code.method.method.holder) { + if (field.holder != code.method.holder()) { // Accessing a static field on a different object could cause <clinit> to run which // could modify any static field on any other object. - activeStaticFieldValues.clear(); + activeState.clearNonFinalStaticFields(); } else { - activeStaticFieldValues.remove(field); + activeState.removeNonFinalStaticField(field); } } else if (instruction.isStaticGet()) { - if (field.holder != code.method.method.holder) { + if (field.holder != code.method.holder()) { // Accessing a static field on a different object could cause <clinit> to run which // could modify any static field on any other object. - activeStaticFieldValues.clear(); + activeState.clearNonFinalStaticFields(); } } else if (instruction.isInstanceGet()) { throw new Unreachable(); } } - // If a field get instruction throws an exception it did not have an effect on the - // value of the field. Therefore, when propagating across exceptional edges for a - // field get instruction we have to exclude that field from the set of known - // field values. - private void killActiveFieldsForExceptionalExit(FieldInstruction instruction) { - DexField field = instruction.getField(); - if (instruction.isInstanceGet()) { - Value object = instruction.asInstanceGet().object().getAliasedValue(); - FieldAndObject fieldAndObject = new FieldAndObject(field, object); - activeInstanceFieldValues.remove(fieldAndObject); - } else if (instruction.isStaticGet()) { - activeStaticFieldValues.remove(field); - } - } + static class State { - private void killActiveInitializedClassesForExceptionalExit(InitClass instruction) { - activeInitializedClasses.remove(instruction.getClassValue()); + private final Map<FieldAndObject, FieldValue> finalInstanceFieldValues = new HashMap<>(); + + private final Map<DexField, FieldValue> finalStaticFieldValues = new IdentityHashMap<>(); + + private final Set<DexType> initializedClasses = Sets.newIdentityHashSet(); + + private final Map<FieldAndObject, FieldValue> nonFinalInstanceFieldValues = new HashMap<>(); + + private final Map<DexField, FieldValue> nonFinalStaticFieldValues = new IdentityHashMap<>(); + + public State() {} + + public State(State state) { + finalInstanceFieldValues.putAll(state.finalInstanceFieldValues); + finalStaticFieldValues.putAll(state.finalStaticFieldValues); + initializedClasses.addAll(state.initializedClasses); + nonFinalInstanceFieldValues.putAll(state.nonFinalInstanceFieldValues); + nonFinalStaticFieldValues.putAll(state.nonFinalStaticFieldValues); + } + + public void clearNonFinalInstanceFields() { + nonFinalInstanceFieldValues.clear(); + } + + public void clearNonFinalStaticFields() { + nonFinalStaticFieldValues.clear(); + } + + public FieldValue getInstanceFieldValue(FieldAndObject field) { + FieldValue value = nonFinalInstanceFieldValues.get(field); + return value != null ? value : finalInstanceFieldValues.get(field); + } + + public FieldValue getStaticFieldValue(DexField field) { + FieldValue value = nonFinalStaticFieldValues.get(field); + return value != null ? value : finalStaticFieldValues.get(field); + } + + public void intersect(State state) { + intersectFieldValues(finalInstanceFieldValues, state.finalInstanceFieldValues); + intersectFieldValues(finalStaticFieldValues, state.finalStaticFieldValues); + intersectInitializedClasses(initializedClasses, state.initializedClasses); + intersectFieldValues(nonFinalInstanceFieldValues, state.nonFinalInstanceFieldValues); + intersectFieldValues(nonFinalStaticFieldValues, state.nonFinalStaticFieldValues); + } + + private static <K> void intersectFieldValues( + Map<K, FieldValue> fieldValues, Map<K, FieldValue> other) { + fieldValues.entrySet().removeIf(entry -> other.get(entry.getKey()) != entry.getValue()); + } + + private static void intersectInitializedClasses( + Set<DexType> initializedClasses, Set<DexType> other) { + initializedClasses.removeIf(not(other::contains)); + } + + public boolean isClassInitialized(DexType clazz) { + return initializedClasses.contains(clazz); + } + + // If a field get instruction throws an exception it did not have an effect on the value of the + // field. Therefore, when propagating across exceptional edges for a field get instruction we + // have to exclude that field from the set of known field values. + public void killActiveFieldsForExceptionalExit(FieldInstruction instruction) { + DexField field = instruction.getField(); + if (instruction.isInstanceGet()) { + Value object = instruction.asInstanceGet().object().getAliasedValue(); + FieldAndObject fieldAndObject = new FieldAndObject(field, object); + removeNonFinalInstanceField(fieldAndObject); + } else if (instruction.isStaticGet()) { + removeNonFinalStaticField(field); + } + } + + private void killActiveInitializedClassesForExceptionalExit(InitClass instruction) { + initializedClasses.remove(instruction.getClassValue()); + } + + public void markClassAsInitialized(DexType clazz) { + initializedClasses.add(clazz); + } + + public void removeInstanceField(FieldAndObject field) { + removeFinalInstanceField(field); + removeNonFinalInstanceField(field); + } + + public void removeFinalInstanceField(FieldAndObject field) { + finalInstanceFieldValues.remove(field); + } + + public void removeNonFinalInstanceField(FieldAndObject field) { + nonFinalInstanceFieldValues.remove(field); + } + + public void removeNonFinalInstanceFields(DexField field) { + nonFinalInstanceFieldValues.keySet().removeIf(key -> key.field == field); + } + + public void removeStaticField(DexField field) { + removeFinalStaticField(field); + removeNonFinalStaticField(field); + } + + public void removeFinalStaticField(DexField field) { + finalStaticFieldValues.remove(field); + } + + public void removeNonFinalStaticField(DexField field) { + nonFinalStaticFieldValues.remove(field); + } + + public void putFinalInstanceField(FieldAndObject field, FieldValue value) { + finalInstanceFieldValues.put(field, value); + } + + public void putFinalStaticField(DexField field, FieldValue value) { + finalStaticFieldValues.put(field, value); + } + + public void putNonFinalInstanceField(FieldAndObject field, FieldValue value) { + assert !finalInstanceFieldValues.containsKey(field); + nonFinalInstanceFieldValues.put(field, value); + } + + public void putNonFinalStaticField(DexField field, FieldValue value) { + assert !nonFinalStaticFieldValues.containsKey(field); + nonFinalStaticFieldValues.put(field, value); + } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java index 1b56e8c..1e19dea 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -36,7 +36,7 @@ public static void rewriteGetClassOrForNameToConstClass( AppView<AppInfoWithLiveness> appView, IRCode code) { Set<Value> affectedValues = Sets.newIdentityHashSet(); - DexType context = code.method.method.holder; + DexType context = code.method.holder(); ClassInitializationAnalysis classInitializationAnalysis = new ClassInitializationAnalysis(appView, code); for (BasicBlock block : code.blocks) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java index 8e0858a..6b854e6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -351,8 +351,7 @@ Instruction instruction = instructionIterator.next(); if (instruction.throwsOnNullInput()) { Value couldBeNullValue = instruction.getNonNullInput(); - if (isThrowNullCandidate( - couldBeNullValue, instruction, appView, code.method.method.holder)) { + if (isThrowNullCandidate(couldBeNullValue, instruction, appView, code.method.holder())) { if (instruction.isInstanceGet() || instruction.isInstancePut()) { ++numberOfInstanceGetOrInstancePutWithNullReceiver; } else if (instruction.isInvokeMethodWithReceiver()) { @@ -451,7 +450,7 @@ IRCode code, AssumeDynamicTypeRemover assumeDynamicTypeRemover, Set<Value> affectedValues) { - DexType context = code.method.method.holder; + DexType context = code.method.holder(); DexField field = instruction.getField(); DexType fieldType = field.type; if (fieldType.isAlwaysNull(appView)) { @@ -507,7 +506,7 @@ AssumeDynamicTypeRemover assumeDynamicTypeRemover, Set<BasicBlock> blocksToBeRemoved, Set<Value> affectedValues) { - DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.method.holder); + DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.holder()); if (target == null) { return; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java index db393ed..7b60b90 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -157,8 +157,7 @@ // Constructors must be named `<init>`. return null; } - newSignature = - appView.dexItemFactory().createMethod(method.method.holder, newProto, newName); + newSignature = appView.dexItemFactory().createMethod(method.holder(), newProto, newName); count++; } while (!isMethodSignatureAvailable(newSignature)); return newSignature; @@ -198,8 +197,7 @@ // Constructors must be named `<init>`. return null; } - newSignature = - appView.dexItemFactory().createMethod(method.method.holder, newProto, newName); + newSignature = appView.dexItemFactory().createMethod(method.holder(), newProto, newName); count++; } while (methodPool.hasSeen(equivalence.wrap(newSignature))); return newSignature;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java index 81f68de..7f0ad04 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -160,7 +160,7 @@ if (eligibleClass.classInitializationMayHaveSideEffects( appView, // Types that are a super type of the current context are guaranteed to be initialized. - type -> appView.isSubtype(method.method.holder, type).isTrue(), + type -> appView.isSubtype(method.holder(), type).isTrue(), Sets.newIdentityHashSet())) { return EligibilityStatus.HAS_CLINIT; } @@ -170,7 +170,7 @@ assert root.isStaticGet(); StaticGet staticGet = root.asStaticGet(); - if (staticGet.instructionMayHaveSideEffects(appView, method.method.holder)) { + if (staticGet.instructionMayHaveSideEffects(appView, method.holder())) { return EligibilityStatus.RETRIEVAL_MAY_HAVE_SIDE_EFFECTS; } DexEncodedField field = appView.appInfo().resolveField(staticGet.getField()); @@ -265,8 +265,7 @@ if (user.isInvokeMethod()) { InvokeMethod invokeMethod = user.asInvokeMethod(); - DexEncodedMethod singleTarget = - invokeMethod.lookupSingleTarget(appView, method.method.holder); + DexEncodedMethod singleTarget = invokeMethod.lookupSingleTarget(appView, method.holder()); if (singleTarget == null) { return user; // Not eligible. } @@ -507,8 +506,7 @@ continue; } - DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView, code.method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder()); if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) { throw new IllegalClassInlinerStateException(); } @@ -569,7 +567,7 @@ continue; } - DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); if (singleTarget != null) { Predicate<InvokeMethod> noSideEffectsPredicate = dexItemFactory.libraryMethodsWithoutSideEffects.getOrDefault( @@ -1156,7 +1154,7 @@ } // Check if the method is inline-able by standard inliner. - DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); if (singleTarget == null) { return false; } @@ -1190,7 +1188,7 @@ } private boolean exemptFromInstructionLimit(DexEncodedMethod inlinee) { - DexType inlineeHolder = inlinee.method.holder; + DexType inlineeHolder = inlinee.holder(); DexClass inlineeClass = appView.definitionFor(inlineeHolder); assert inlineeClass != null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java index 84b952b..ea9141f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.ir.optimize.enums; +import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; @@ -28,6 +30,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.ArrayPut; import com.android.tools.r8.ir.code.BasicBlock; +import com.android.tools.r8.ir.code.CheckCast; import com.android.tools.r8.ir.code.ConstClass; import com.android.tools.r8.ir.code.FieldInstruction; import com.android.tools.r8.ir.code.IRCode; @@ -86,13 +89,14 @@ debugLogEnabled = false; debugLogs = null; } + assert !appView.options().debug; enumsUnboxingCandidates = new EnumUnboxingCandidateAnalysis(appView, this).findCandidates(); } public void analyzeEnums(IRCode code) { // Enum <clinit> and <init> are analyzed in between the two processing phases using optimization // feedback. Methods valueOf and values are generated by javac and are analyzed differently. - DexClass dexClass = appView.definitionFor(code.method.method.holder); + DexClass dexClass = appView.definitionFor(code.method.holder()); if (dexClass.isEnum() && (code.method.isInitializer() || appView.dexItemFactory().enumMethods.isValueOfMethod(code.method.method, dexClass) @@ -140,11 +144,6 @@ if (enumClass != null) { Reason reason = validateEnumUsages(code, outValue, enumClass); if (reason == Reason.ELIGIBLE) { - if (instruction.isCheckCast()) { - // We are doing a type check, which typically means the in-value is of an upper - // type and cannot be dealt with. - markEnumAsUnboxable(Reason.DOWN_CAST, enumClass); - } eligibleEnums.add(enumClass.type); } } @@ -152,15 +151,10 @@ addNullDependencies(outValue.uniqueUsers(), eligibleEnums); } } - // If we have a ConstClass referencing directly an enum, it cannot be unboxed, except if - // the constClass is in an enum valueOf method (in this case the valueOf method will be - // removed or the enum will be marked as non unboxable). if (instruction.isConstClass()) { - ConstClass constClass = instruction.asConstClass(); - if (enumsUnboxingCandidates.containsKey(constClass.getValue())) { - markEnumAsUnboxable( - Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue())); - } + analyzeConstClass(instruction.asConstClass()); + } else if (instruction.isCheckCast()) { + analyzeCheckCast(instruction.asCheckCast()); } else if (instruction.isInvokeStatic()) { // TODO(b/150370354): Since we temporary allow enum unboxing on enums with values and // valueOf static methods only if such methods are unused, such methods cannot be @@ -204,6 +198,48 @@ } } + private void analyzeCheckCast(CheckCast checkCast) { + // We are doing a type check, which typically means the in-value is of an upper + // type and cannot be dealt with. + // If the cast is on a dynamically typed object, the checkCast can be simply removed. + // This allows enum array clone and valueOf to work correctly. + TypeElement objectType = checkCast.object().getDynamicUpperBoundType(appView); + if (objectType.equalUpToNullability( + TypeElement.fromDexType(checkCast.getType(), definitelyNotNull(), appView))) { + return; + } + DexProgramClass enumClass = + getEnumUnboxingCandidateOrNull(checkCast.getType().toBaseType(factory)); + if (enumClass != null) { + markEnumAsUnboxable(Reason.DOWN_CAST, enumClass); + } + } + + private void analyzeConstClass(ConstClass constClass) { + // We are using the ConstClass of an enum, which typically means the enum cannot be unboxed. + // We however allow unboxing if the ConstClass is only used as an argument to Enum#valueOf, to + // allow unboxing of: MyEnum a = Enum.valueOf(MyEnum.class, "A");. + if (!enumsUnboxingCandidates.containsKey(constClass.getValue())) { + return; + } + if (constClass.outValue() == null) { + return; + } + if (constClass.outValue().hasPhiUsers()) { + markEnumAsUnboxable( + Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue())); + return; + } + for (Instruction user : constClass.outValue().uniqueUsers()) { + if (!(user.isInvokeStatic() + && user.asInvokeStatic().getInvokedMethod() == factory.enumMethods.valueOf)) { + markEnumAsUnboxable( + Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue())); + return; + } + } + } + private void addNullDependencies(Set<Instruction> uses, Set<DexType> eligibleEnums) { for (Instruction use : uses) { if (use.isInvokeMethod()) { @@ -349,7 +385,7 @@ return Reason.INVALID_INVOKE_ON_ARRAY; } DexEncodedMethod encodedSingleTarget = - invokeMethod.lookupSingleTarget(appView, code.method.method.holder); + invokeMethod.lookupSingleTarget(appView, code.method.holder()); if (encodedSingleTarget == null) { return Reason.INVALID_INVOKE; } @@ -359,14 +395,6 @@ return Reason.INVALID_INVOKE; } if (dexClass.isProgramClass()) { - // All invokes in the program are generally valid, but specific care is required - // for values() and valueOf(). - if (dexClass.isEnum() && factory.enumMethods.isValuesMethod(singleTarget, dexClass)) { - return Reason.VALUES_INVOKE; - } - if (dexClass.isEnum() && factory.enumMethods.isValueOfMethod(singleTarget, dexClass)) { - return Reason.VALUE_OF_INVOKE; - } int offset = BooleanUtils.intValue(!encodedSingleTarget.isStatic()); for (int i = 0; i < singleTarget.proto.parameters.size(); i++) { if (invokeMethod.inValues().get(offset + i) == enumValue) { @@ -384,8 +412,8 @@ if (dexClass.type != factory.enumType) { return Reason.UNSUPPORTED_LIBRARY_CALL; } - // TODO(b/147860220): Methods toString(), name(), compareTo(), EnumSet and EnumMap may be - // interesting to model. A the moment rewrite only Enum#ordinal(). + // TODO(b/147860220): Methods toString(), name(), compareTo(), EnumSet and EnumMap may + // be interesting to model. A the moment rewrite only Enum#ordinal() and Enum#valueOf. if (debugLogEnabled) { if (singleTarget == factory.enumMethods.compareTo) { return Reason.COMPARE_TO_INVOKE; @@ -397,10 +425,10 @@ return Reason.TO_STRING_INVOKE; } } - if (singleTarget != factory.enumMethods.ordinal) { - return Reason.UNSUPPORTED_LIBRARY_CALL; + if (singleTarget == factory.enumMethods.ordinal) { + return Reason.ELIGIBLE; } - return Reason.ELIGIBLE; + return Reason.UNSUPPORTED_LIBRARY_CALL; } // A field put is valid only if the field is not on an enum, and the field type and the valuePut @@ -411,7 +439,7 @@ if (field == null) { return Reason.INVALID_FIELD_PUT; } - DexProgramClass dexClass = appView.definitionForProgramType(field.field.holder); + DexProgramClass dexClass = appView.definitionForProgramType(field.holder()); if (dexClass == null) { return Reason.INVALID_FIELD_PUT; } @@ -603,7 +631,7 @@ for (DexProgramClass clazz : appView.appInfo().classes()) { if (enumsToUnbox.contains(clazz.type)) { assert clazz.instanceFields().size() == 0; - clearEnumtoUnboxMethods(clazz); + clearEnumToUnboxMethods(clazz); } else { clazz.getMethodCollection().replaceMethods(this::fixupMethod); fixupFields(clazz.staticFields(), clazz::setStaticField); @@ -616,7 +644,7 @@ return lensBuilder.build(factory, appView.graphLense()); } - private void clearEnumtoUnboxMethods(DexProgramClass clazz) { + private void clearEnumToUnboxMethods(DexProgramClass clazz) { // The compiler may have references to the enum methods, but such methods will be removed // and they cannot be reprocessed since their rewriting through the lensCodeRewriter/ // enumUnboxerRewriter will generate invalid code.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java index 0bf0aed..a09ea37 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
@@ -26,6 +26,7 @@ import com.android.tools.r8.cf.code.CfStore; import com.android.tools.r8.cf.code.CfThrow; import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.ir.code.If; import com.android.tools.r8.ir.code.MemberType; @@ -36,6 +37,10 @@ public final class EnumUnboxingCfMethods { + public static void registerSynthesizedCodeReferences(DexItemFactory factory) { + factory.createSynthesizedType("Ljava/lang/NullPointerException;"); + } + public static CfCode EnumUnboxingMethods_compareTo(InternalOptions options, DexMethod method) { CfLabel label0 = new CfLabel(); CfLabel label1 = new CfLabel(); @@ -52,13 +57,12 @@ new CfLoad(ValueType.INT, 1), new CfIf(If.Type.NE, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"), + options.itemFactory.createType("Ljava/lang/NullPointerException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -89,13 +93,12 @@ new CfLoad(ValueType.INT, 0), new CfIf(If.Type.NE, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"), + options.itemFactory.createType("Ljava/lang/NullPointerException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false), @@ -129,13 +132,12 @@ new CfLoad(ValueType.INT, 0), new CfIf(If.Type.NE, ValueType.INT, label2), label1, - new CfNew( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")), + new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")), new CfStackInstruction(CfStackInstruction.Opcode.Dup), new CfInvoke( 183, options.itemFactory.createMethod( - options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"), + options.itemFactory.createType("Ljava/lang/NullPointerException;"), options.itemFactory.createProto(options.itemFactory.voidType), options.itemFactory.createString("<init>")), false),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java index 0d065ff..73eff15 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -24,27 +24,28 @@ import com.android.tools.r8.graph.ParameterAnnotationsList; import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater; -import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.ArrayAccess; -import com.android.tools.r8.ir.code.ConstNumber; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionListIterator; import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.MemberType; -import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.code.StaticGet; +import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.conversion.IRConverter; +import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider; import com.android.tools.r8.origin.SynthesizedOrigin; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -56,6 +57,7 @@ private final AppView<AppInfoWithLiveness> appView; private final DexItemFactory factory; private final EnumValueInfoMapCollection enumsToUnbox; + private final Map<DexMethod, DexType> extraMethods = new ConcurrentHashMap<>(); private final DexType utilityClassType; private final DexMethod ordinalUtilityMethod; @@ -71,7 +73,7 @@ } this.enumsToUnbox = builder.build(); - this.utilityClassType = factory.createType("L" + ENUM_UNBOXING_UTILITY_CLASS_NAME + ";"); + this.utilityClassType = factory.enumUnboxingUtilityType; this.ordinalUtilityMethod = factory.createMethod( utilityClassType, @@ -89,6 +91,7 @@ if (enumsToUnbox.isEmpty()) { return; } + assert code.isConsistentSSABeforeTypesAreCorrect(); Set<Phi> affectedPhis = Sets.newIdentityHashSet(); InstructionListIterator iterator = code.instructionListIterator(); while (iterator.hasNext()) { @@ -99,14 +102,38 @@ InvokeMethodWithReceiver invokeMethod = instruction.asInvokeMethodWithReceiver(); DexMethod invokedMethod = invokeMethod.getInvokedMethod(); if (invokedMethod == factory.enumMethods.ordinal - && invokeMethod.getReceiver().getType().isInt()) { + && isEnumToUnboxOrInt(invokeMethod.getReceiver().getType())) { instruction = new InvokeStatic( ordinalUtilityMethod, invokeMethod.outValue(), invokeMethod.inValues()); iterator.replaceCurrentInstruction(instruction); requiresOrdinalUtilityMethod = true; + continue; } // TODO(b/147860220): rewrite also other enum methods. + } else if (instruction.isInvokeStatic()) { + InvokeStatic invokeStatic = instruction.asInvokeStatic(); + DexMethod invokedMethod = invokeStatic.getInvokedMethod(); + if (invokedMethod == factory.enumMethods.valueOf + && invokeStatic.inValues().get(0).isConstClass()) { + DexType enumType = + invokeStatic.inValues().get(0).getConstInstruction().asConstClass().getValue(); + if (enumsToUnbox.containsEnum(enumType)) { + DexMethod valueOfMethod = computeValueOfUtilityMethod(enumType); + Value outValue = invokeStatic.outValue(); + Value rewrittenOutValue = null; + if (outValue != null) { + rewrittenOutValue = code.createValue(TypeElement.getInt()); + affectedPhis.addAll(outValue.uniquePhiUsers()); + } + iterator.replaceCurrentInstruction( + new InvokeStatic( + valueOfMethod, + rewrittenOutValue, + Collections.singletonList(invokeStatic.inValues().get(1)))); + continue; + } + } } // Rewrites direct access to enum values into the corresponding int, $VALUES is not // supported. @@ -124,10 +151,8 @@ EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField()); assert enumValueInfo != null : "Invalid read to " + staticGet.getField().name + ", error during enum analysis"; - instruction = new ConstNumber(staticGet.outValue(), enumValueInfo.convertToInt()); - staticGet.outValue().setType(PrimitiveTypeElement.fromNumericType(NumericType.INT)); - iterator.replaceCurrentInstruction(instruction); affectedPhis.addAll(staticGet.outValue().uniquePhiUsers()); + iterator.replaceCurrentInstruction(code.createIntConstant(enumValueInfo.convertToInt())); } } // Rewrite array accesses from MyEnum[] (OBJECT) to int[] (INT). @@ -137,8 +162,8 @@ instruction = arrayAccess.withMemberType(MemberType.INT); iterator.replaceCurrentInstruction(instruction); } + assert validateArrayAccess(instruction.asArrayAccess()); } - assert validateEnumToUnboxRemoved(instruction); } if (!affectedPhis.isEmpty()) { new DestructivePhiTypeUpdater(appView).recomputeAndPropagateTypes(code, affectedPhis); @@ -146,33 +171,43 @@ assert code.isConsistentSSABeforeTypesAreCorrect(); } - private boolean shouldRewriteArrayAccess(ArrayAccess arrayAccess) { + private boolean validateArrayAccess(ArrayAccess arrayAccess) { ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType(); - return arrayAccess.getMemberType() == MemberType.OBJECT - && arrayType.getNesting() == 1 - && arrayType.getBaseType().isInt(); + assert arrayAccess.getMemberType() != MemberType.OBJECT + || arrayType.getNesting() > 1 + || arrayType.getBaseType().isReferenceType(); + return true; } - private boolean validateEnumToUnboxRemoved(Instruction instruction) { - if (instruction.isArrayAccess()) { - ArrayAccess arrayAccess = instruction.asArrayAccess(); - ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType(); - assert arrayAccess.getMemberType() != MemberType.OBJECT - || arrayType.getNesting() > 1 - || arrayType.getBaseType().isReferenceType(); - } - if (instruction.outValue() == null) { + private boolean isEnumToUnboxOrInt(TypeElement type) { + if (type.isInt()) { return true; } - TypeElement typeLattice = instruction.outValue().getType(); - assert !typeLattice.isClassType() - || !enumsToUnbox.containsEnum(typeLattice.asClassType().getClassType()); - if (typeLattice.isArrayType()) { - TypeElement arrayBaseTypeLattice = typeLattice.asArrayType().getBaseType(); - assert !arrayBaseTypeLattice.isClassType() - || !enumsToUnbox.containsEnum(arrayBaseTypeLattice.asClassType().getClassType()); + if (!type.isClassType()) { + return false; } - return true; + return enumsToUnbox.containsEnum(type.asClassType().getClassType()); + } + + private DexMethod computeValueOfUtilityMethod(DexType type) { + assert enumsToUnbox.containsEnum(type); + DexMethod valueOf = + factory.createMethod( + utilityClassType, + factory.createProto(factory.intType, factory.stringType), + "valueOf" + type.toSourceString().replace('.', '$')); + extraMethods.putIfAbsent(valueOf, type); + return valueOf; + } + + private boolean shouldRewriteArrayAccess(ArrayAccess arrayAccess) { + ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType(); + if (arrayType.getNesting() != 1) { + return false; + } + TypeElement baseType = arrayType.getBaseType(); + return baseType.isClassType() + && enumsToUnbox.containsEnum(baseType.asClassType().getClassType()); } // TODO(b/150172351): Synthesize the utility class upfront in the enqueuer. @@ -182,6 +217,11 @@ // Synthesize a class which holds various utility methods that may be called from the IR // rewriting. If any of these methods are not used, they will be removed by the Enqueuer. List<DexEncodedMethod> requiredMethods = new ArrayList<>(); + extraMethods.forEach( + (method, enumType) -> { + requiredMethods.add(synthesizeValueOfUtilityMethod(method, enumType)); + }); + requiredMethods.sort((m1, m2) -> m1.method.name.slowCompareTo(m2.method.name)); if (requiresOrdinalUtilityMethod) { requiredMethods.add(synthesizeOrdinalMethod()); } @@ -215,6 +255,21 @@ converter.optimizeSynthesizedClass(utilityClass, executorService); } + private DexEncodedMethod synthesizeValueOfUtilityMethod(DexMethod method, DexType enumType) { + CfCode cfCode = + new EnumUnboxingCfCodeProvider.EnumUnboxingValueOfCfCodeProvider( + appView, utilityClassType, enumType, enumsToUnbox.getEnumValueInfoMap(enumType)) + .generateCfCode(); + return new DexEncodedMethod( + method, + synthesizedMethodAccessFlags(), + DexAnnotationSet.empty(), + ParameterAnnotationsList.empty(), + cfCode, + REQUIRED_CLASS_FILE_VERSION, + true); + } + // TODO(b/150178516): Add a test for this case. private boolean utilityClassInMainDexList() { for (DexType toUnbox : enumsToUnbox.enumSet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java index ba24e19..791c889 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -85,7 +85,7 @@ TypeElement[] staticTypes = new TypeElement[size]; if (!encodedMethod.isStatic()) { staticTypes[0] = - TypeElement.fromDexType(encodedMethod.method.holder, definitelyNotNull(), appView); + TypeElement.fromDexType(encodedMethod.holder(), definitelyNotNull(), appView); } for (int i = 0; i < encodedMethod.method.getArity(); i++) { staticTypes[i + argOffset] = @@ -168,7 +168,7 @@ Value aliasedValue = arg.getAliasedValue(); if (!aliasedValue.isPhi()) { AbstractValue abstractValue = - aliasedValue.definition.getAbstractValue(appView, method.method.holder); + aliasedValue.definition.getAbstractValue(appView, method.holder()); if (abstractValue.isNonTrivial()) { newCallSiteInfo.constants.put(i, abstractValue); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index 19c897f..4889b2e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -177,7 +177,7 @@ return; } - DexClass clazz = appView.definitionFor(method.method.holder); + DexClass clazz = appView.definitionFor(method.holder()); if (clazz == null) { return; } @@ -241,8 +241,7 @@ case INVOKE_STATIC: { InvokeStatic invoke = insn.asInvokeStatic(); - DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView, method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); if (singleTarget == null) { return; // Not allowed. } @@ -263,7 +262,7 @@ DexMethod invokedMethod = invoke.getInvokedMethod(); DexType returnType = invokedMethod.proto.returnType; if (returnType.isClassType() - && appView.appInfo().isRelatedBySubtyping(returnType, method.method.holder)) { + && appView.appInfo().isRelatedBySubtyping(returnType, method.holder())) { return; // Not allowed, could introduce an alias of the receiver. } callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod)); @@ -373,7 +372,7 @@ if (definition.isArgument()) { feedback.methodReturnsArgument(method, definition.asArgument().getIndex()); } - DexType context = method.method.holder; + DexType context = method.holder(); AbstractValue abstractReturnValue = definition.getAbstractValue(appView, context); if (abstractReturnValue.isNonTrivial()) { feedback.methodReturnsAbstractValue(method, appView, abstractReturnValue); @@ -417,7 +416,7 @@ return; } - DexClass clazz = appView.appInfo().definitionFor(method.method.holder); + DexClass clazz = appView.appInfo().definitionFor(method.holder()); if (clazz == null) { assert false; return; @@ -687,7 +686,7 @@ if (method.isStatic()) { // Identifies if the method preserves class initialization after inlining. feedback.markTriggerClassInitBeforeAnySideEffect( - method, triggersClassInitializationBeforeSideEffect(method.method.holder, code, appView)); + method, triggersClassInitializationBeforeSideEffect(method.holder(), code, appView)); } else { // Identifies if the method preserves null check of the receiver after inlining. final Value receiver = code.getThis(); @@ -707,7 +706,7 @@ return alwaysTriggerExpectedEffectBeforeAnythingElse( code, (instruction, it) -> { - DexType context = code.method.method.holder; + DexType context = code.method.holder(); if (instruction.definitelyTriggersClassInitialization( clazz, context, appView, DIRECTLY, AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) { // In order to preserve class initialization semantic, the exception must not be caught @@ -849,7 +848,7 @@ // We found a NPE check on the value. return InstructionEffect.DESIRED_EFFECT; } - } else if (instr.instructionMayHaveSideEffects(appView, code.method.method.holder)) { + } else if (instr.instructionMayHaveSideEffects(appView, code.method.holder())) { // If the current instruction is const-string, this could load the parameter name. // Just make sure it is indeed not throwing. if (instr.isConstString() && !instr.instructionInstanceCanThrow()) { @@ -1014,7 +1013,7 @@ if (appView.appInfo().mayHaveSideEffects.containsKey(method.method)) { return; } - DexType context = method.method.holder; + DexType context = method.holder(); if (method.isClassInitializer()) { // For class initializers, we also wish to compute if the class initializer has observable // side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java index 3a98637..608c41f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
@@ -38,7 +38,7 @@ return cached; } Position position = Position.getPositionForInlining(appView, invoke, context); - Origin origin = appView.appInfo().originFor(method.method.holder); + Origin origin = appView.appInfo().originFor(method.holder()); return method.buildInliningIR( context, appView, valueNumberGenerator, position, origin, methodProcessor); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java index 1ff91c0..091a5e5 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -32,8 +32,7 @@ return; } - Collection<DexEncodedMethod> possibleTargets = - invoke.lookupTargets(appView, context.method.holder); + Collection<DexEncodedMethod> possibleTargets = invoke.lookupTargets(appView, context.holder()); if (possibleTargets == null) { // In principle, this invoke might target any method in the program, but we do not want to // report a message for each of the methods in `AppInfoWithLiveness#whyAreYouNotInlining`,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java index 64026ca..b141817 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -220,7 +220,7 @@ "final field `" + instancePut.getField() + "` must be initialized in a constructor of `" - + callee.method.holder.toSourceString() + + callee.holder().toSourceString() + "`."); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java index 1648958..eb8b742 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
@@ -218,7 +218,7 @@ private boolean shouldRewrite(DexType type) { // Rewrite references to lambda classes if we are outside the class. - return type != (context != null ? context : method).method.holder; + return type != (context != null ? context : method).holder(); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java index ce2394c..0bdaa57 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -133,7 +133,7 @@ Inliner inliner, DexEncodedMethod context, InliningIRProvider provider) { - DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass(); + DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass(); assert clazz != null; LambdaGroup lambdaGroup = lambdaGroups.get(clazz); @@ -162,7 +162,7 @@ assert resolution.isSingleResolution(); DexEncodedMethod singleTarget = resolution.getSingleTarget(); assert singleTarget != null; - invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.method.holder)); + invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.holder())); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java index 3e602b7..4d79186 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -54,7 +54,7 @@ return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName && lambda == field.type && context.factory.isClassConstructor(context.method.method) - && context.method.method.holder == lambda; + && context.method.holder() == lambda; } @Override @@ -100,10 +100,10 @@ // Allow calls to a constructor from other classes if the lambda is singleton, // otherwise allow such a call only from the same class static initializer. boolean isSingletonLambda = group.isStateless() && group.isSingletonLambda(lambda); - return (isSingletonLambda == (context.method.method.holder == lambda)) && - invoke.isInvokeDirect() && - context.factory.isConstructor(method) && - CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture); + return (isSingletonLambda == (context.method.holder() == lambda)) + && invoke.isInvokeDirect() + && context.factory.isConstructor(method) + && CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture); } private boolean isValidVirtualCall(InvokeMethod invoke) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java index 31444c6..16d514c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -70,7 +70,7 @@ InvokeMethod invoke, Set<Value> affectedValues) { Value argument = invoke.arguments().get(0); - AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.method.holder); + AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.holder()); if (abstractValue.isSingleNumberValue()) { instructionIterator.replaceCurrentInstructionWithStaticGet( appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java new file mode 100644 index 0000000..fd9d62b --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -0,0 +1,76 @@ +// Copyright (c) 2020, 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.library; + +import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.type.TypeElement; +import com.android.tools.r8.ir.code.Assume; +import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.InstructionListIterator; +import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.Value; +import java.util.Set; + +public class EnumMethodOptimizer implements LibraryMethodModelCollection { + private final AppView<?> appView; + + EnumMethodOptimizer(AppView<?> appView) { + this.appView = appView; + } + + @Override + public DexType getType() { + return appView.dexItemFactory().enumType; + } + + @Override + public void optimize( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethod invoke, + DexEncodedMethod singleTarget, + Set<Value> affectedValues) { + if (singleTarget.method == appView.dexItemFactory().enumMethods.valueOf + && invoke.inValues().get(0).isConstClass()) { + insertAssumeDynamicType(code, instructionIterator, invoke); + } + } + + private void insertAssumeDynamicType( + IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) { + // TODO(b/152516470): Support unboxing enums with Enum#valueOf in try-catch. + if (invoke.getBlock().hasCatchHandlers()) { + return; + } + DexType enumType = invoke.inValues().get(0).getConstInstruction().asConstClass().getValue(); + DexProgramClass enumClass = appView.definitionForProgramType(enumType); + if (enumClass == null || enumClass.superType != appView.dexItemFactory().enumType) { + return; + } + TypeElement dynamicUpperBoundType = + TypeElement.fromDexType(enumType, definitelyNotNull(), appView); + Value outValue = invoke.outValue(); + if (outValue == null) { + return; + } + // Replace usages of out-value by the out-value of the AssumeDynamicType instruction. + Value specializedOutValue = code.createValue(outValue.getType(), outValue.getLocalInfo()); + outValue.replaceUsers(specializedOutValue); + + // Insert AssumeDynamicType instruction. + Assume<DynamicTypeAssumption> assumeInstruction = + Assume.createAssumeDynamicTypeInstruction( + dynamicUpperBoundType, null, specializedOutValue, outValue, invoke, appView); + assumeInstruction.setPosition(appView.options().debug ? invoke.getPosition() : Position.none()); + instructionIterator.add(assumeInstruction); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java index f3d7cc6..fc687a0 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -37,6 +37,9 @@ register(new ObjectMethodOptimizer(appView)); register(new ObjectsMethodOptimizer(appView)); register(new StringMethodOptimizer(appView)); + if (appView.appInfo().hasSubtyping() && appView.options().enableDynamicTypeOptimization) { + register(new EnumMethodOptimizer(appView)); + } if (LogMethodOptimizer.isEnabled(appView)) { register(new LogMethodOptimizer(appView)); @@ -83,8 +86,7 @@ Instruction instruction = instructionIterator.next(); if (instruction.isInvokeMethod()) { InvokeMethod invoke = instruction.asInvokeMethod(); - DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView, code.method.method.holder); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder()); if (singleTarget != null) { optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues); } @@ -103,7 +105,7 @@ Set<Value> affectedValues) { LibraryMethodModelCollection optimizer = libraryMethodModelCollections.getOrDefault( - singleTarget.method.holder, NopLibraryMethodModelCollection.getInstance()); + singleTarget.holder(), NopLibraryMethodModelCollection.getInstance()); optimizer.optimize(code, instructionIterator, invoke, singleTarget, affectedValues); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java index 501a761..09c8f4e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -47,7 +47,7 @@ private void optimizeEquals( IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) { if (appView.appInfo().hasLiveness()) { - DexType context = code.method.method.holder; + DexType context = code.method.holder(); Value first = invoke.arguments().get(0).getAliasedValue(); Value second = invoke.arguments().get(1).getAliasedValue(); if (isPrunedClassNameComparison(first, second, context)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java index 127b144..3b93c50 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
@@ -113,7 +113,7 @@ Instruction current = it.next(); if (position != current.getPosition() || !current.isConstNumber() - || current.outValue().getType() != TypeElement.getInt() + || current.getOutType() != TypeElement.getInt() || current.asConstNumber().getIntValue() < -128 || current.asConstNumber().getIntValue() > 127 || !it.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java index 3c99a56..41df56a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -30,6 +30,7 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ListUtils; +import com.android.tools.r8.utils.SetUtils; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -78,11 +79,11 @@ } boolean isHostClassInitializer(DexEncodedMethod method) { - return factory.isClassConstructor(method.method) && method.method.holder == hostType(); + return factory.isClassConstructor(method.method) && method.holder() == hostType(); } DexType hostType() { - return singletonField.field.holder; + return singletonField.holder(); } DexClass hostClass() { @@ -212,7 +213,7 @@ public final void examineMethodCode(DexEncodedMethod method, IRCode code) { Set<Instruction> alreadyProcessed = Sets.newIdentityHashSet(); - CandidateInfo receiverClassCandidateInfo = candidates.get(method.method.holder); + CandidateInfo receiverClassCandidateInfo = candidates.get(method.holder()); Value receiverValue = code.getThis(); // NOTE: is null for static methods. if (receiverClassCandidateInfo != null) { if (receiverValue != null) { @@ -224,13 +225,13 @@ // If the candidate is still valid, ignore all instructions // we treat as valid usages on receiver. - if (candidates.get(method.method.holder) != null) { + if (candidates.get(method.holder()) != null) { alreadyProcessed.addAll(receiverValue.uniqueUsers()); } } else { // We are inside a static method of candidate class. // Check if this is a valid getter of the singleton field. - if (method.method.proto.returnType == method.method.holder) { + if (method.method.proto.returnType == method.holder()) { List<Instruction> examined = isValidGetter(receiverClassCandidateInfo, code); if (examined != null) { DexEncodedMethod getter = receiverClassCandidateInfo.getter.get(); @@ -267,12 +268,13 @@ NewInstance newInstance = instruction.asNewInstance(); CandidateInfo candidateInfo = processInstantiation(method, iterator, newInstance); if (candidateInfo != null) { + alreadyProcessed.addAll(newInstance.outValue().aliasedUsers()); // For host class initializers having eligible instantiation we also want to // ensure that the rest of the initializer consist of code w/o side effects. // This must guarantee that removing field access will not result in missing side // effects, otherwise we can still staticize, but cannot remove singleton reads. while (iterator.hasNext()) { - if (!isAllowedInHostClassInitializer(method.method.holder, iterator.next(), code)) { + if (!isAllowedInHostClassInitializer(method.holder(), iterator.next(), code)) { candidateInfo.preserveRead.set(true); iterator.previous(); break; @@ -396,10 +398,10 @@ return candidateInfo.invalidate(); } - if (candidateValue.numberOfUsers() != 2) { - // We expect only two users for each instantiation: constructor call and - // static field write. We only check count here, since the exact instructions - // will be checked later. + if (candidateValue.numberOfUsers() < 2) { + // We expect two special users for each instantiation: constructor call and static field + // write. We allow the instance to have other users as well, as long as they are valid + // according to the user analysis. return candidateInfo.invalidate(); } @@ -411,7 +413,7 @@ // invoke-direct {v0, ...}, void <candidate-type>.<init>(...) // sput-object v0, <instance-field> // ... - // ... + // ... // other usages that are valid according to the user analysis. // // In case we guarantee candidate constructor does not access <instance-field> // directly or indirectly we can guarantee that all the potential reads get @@ -422,26 +424,33 @@ // Intentionally empty. } iterator.previous(); - if (!iterator.hasNext()) { return candidateInfo.invalidate(); } - if (!isValidInitCall(candidateInfo, iterator.next(), candidateValue, candidateType)) { + Set<Instruction> users = SetUtils.newIdentityHashSet(candidateValue.uniqueUsers()); + Instruction constructorCall = iterator.next(); + if (!isValidInitCall(candidateInfo, constructorCall, candidateValue, candidateType)) { iterator.previous(); return candidateInfo.invalidate(); } - + boolean removedConstructorCall = users.remove(constructorCall); + assert removedConstructorCall; if (!iterator.hasNext()) { return candidateInfo.invalidate(); } - if (!isValidStaticPut(candidateInfo, iterator.next())) { + Instruction staticPut = iterator.next(); + if (!isValidStaticPut(candidateInfo, staticPut)) { iterator.previous(); return candidateInfo.invalidate(); } + boolean removedStaticPut = users.remove(staticPut); + assert removedStaticPut; if (candidateInfo.fieldWrites.incrementAndGet() > 1) { return candidateInfo.invalidate(); } - + if (!isSelectedValueUsersValid(candidateInfo, candidateValue, false, users)) { + return candidateInfo.invalidate(); + } return candidateInfo; } @@ -463,7 +472,7 @@ if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0 || methodInvoked == null - || methodInvoked.method.holder != candidateType) { + || methodInvoked.holder() != candidateType) { return false; } @@ -567,59 +576,79 @@ private CandidateInfo analyzeAllValueUsers( CandidateInfo candidateInfo, Value value, boolean ignoreSuperClassInitInvoke) { assert value != null && value == value.getAliasedValue(); - if (value.numberOfPhiUsers() > 0) { return candidateInfo.invalidate(); } + if (!isSelectedValueUsersValid( + candidateInfo, value, ignoreSuperClassInitInvoke, value.uniqueUsers())) { + return candidateInfo.invalidate(); + } + return candidateInfo; + } - Set<Instruction> currentUsers = value.uniqueUsers(); + private boolean isSelectedValueUsersValid( + CandidateInfo candidateInfo, + Value value, + boolean ignoreSuperClassInitInvoke, + Set<Instruction> currentUsers) { while (!currentUsers.isEmpty()) { Set<Instruction> indirectUsers = Sets.newIdentityHashSet(); for (Instruction user : currentUsers) { - if (user.isAssume()) { - if (user.outValue().numberOfPhiUsers() > 0) { - return candidateInfo.invalidate(); - } - indirectUsers.addAll(user.outValue().uniqueUsers()); - continue; + if (!isValidValueUser( + candidateInfo, value, ignoreSuperClassInitInvoke, indirectUsers, user)) { + return false; } - if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) { - InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver(); - Predicate<Value> isAliasedValue = v -> v.getAliasedValue() == value; - DexMethod methodReferenced = invoke.getInvokedMethod(); - if (factory.isConstructor(methodReferenced)) { - assert user.isInvokeDirect(); - if (ignoreSuperClassInitInvoke - && ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0 - && methodReferenced == factory.objectMembers.constructor) { - // If we are inside candidate constructor and analyzing usages - // of the receiver, we want to ignore invocations of superclass - // constructor which will be removed after staticizing. - continue; - } - return candidateInfo.invalidate(); - } - AppInfoWithLiveness appInfo = appView.appInfo(); - ResolutionResult resolutionResult = - appInfo.resolveMethod(methodReferenced.holder, methodReferenced); - DexEncodedMethod methodInvoked = - user.isInvokeDirect() - ? resolutionResult.lookupInvokeDirectTarget(candidateInfo.candidate, appInfo) - : resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; - if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0 - && methodInvoked != null - && methodInvoked.method.holder == candidateInfo.candidate.type) { - continue; - } - } - - // All other users are not allowed. - return candidateInfo.invalidate(); } currentUsers = indirectUsers; } + return true; + } - return candidateInfo; + private boolean isValidValueUser( + CandidateInfo candidateInfo, + Value value, + boolean ignoreSuperClassInitInvoke, + Set<Instruction> indirectUsers, + Instruction user) { + if (user.isAssume()) { + if (user.outValue().numberOfPhiUsers() > 0) { + return false; + } + indirectUsers.addAll(user.outValue().uniqueUsers()); + return true; + } + if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) { + InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver(); + Predicate<Value> isAliasedValue = v -> v.getAliasedValue() == value; + DexMethod methodReferenced = invoke.getInvokedMethod(); + if (factory.isConstructor(methodReferenced)) { + assert user.isInvokeDirect(); + if (ignoreSuperClassInitInvoke + && ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0 + && methodReferenced == factory.objectMembers.constructor) { + // If we are inside candidate constructor and analyzing usages + // of the receiver, we want to ignore invocations of superclass + // constructor which will be removed after staticizing. + return true; + } + return false; + } + AppInfoWithLiveness appInfo = appView.appInfo(); + ResolutionResult resolutionResult = + appInfo.resolveMethod(methodReferenced.holder, methodReferenced); + DexEncodedMethod methodInvoked = + user.isInvokeDirect() + ? resolutionResult.lookupInvokeDirectTarget(candidateInfo.candidate, appInfo) + : resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null; + if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0 + && methodInvoked != null + && methodInvoked.holder() == candidateInfo.candidate.type) { + return true; + } + } + + // All other users are not allowed. + return false; } // Perform staticizing candidates:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java index f79593b..4a1924b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.ir.optimize.staticizer; +import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; import com.android.tools.r8.graph.DexClass; @@ -18,8 +20,10 @@ import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionListIterator; +import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; import com.android.tools.r8.ir.code.InvokeStatic; +import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.code.StaticGet; import com.android.tools.r8.ir.code.StaticPut; @@ -184,7 +188,7 @@ if (method.isStatic() || factory().isConstructor(method.method)) { continue; } - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder)); + IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); assert code != null; Value thisValue = code.getThis(); assert thisValue != null; @@ -205,7 +209,7 @@ // CHECK: references to field read usages are fixable. boolean fixableFieldReads = true; for (DexEncodedMethod method : info.referencedFrom) { - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder)); + IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); assert code != null; List<StaticGet> singletonFieldReads = Streams.stream(code.instructionIterator()) @@ -301,7 +305,7 @@ Collection<BiConsumer<IRCode, MethodProcessor>> codeOptimizations, OptimizationFeedback feedback, OneTimeMethodProcessor methodProcessor) { - Origin origin = appView.appInfo().originFor(method.method.holder); + Origin origin = appView.appInfo().originFor(method.holder()); IRCode code = method.buildIR(appView, origin); codeOptimizations.forEach(codeOptimization -> codeOptimization.accept(code, methodProcessor)); CodeRewriter.removeAssumeInstructions(appView, code); @@ -329,9 +333,8 @@ assert candidateInfo != null; // Find and remove instantiation and its users. - for (Instruction instruction : code.instructions()) { - if (instruction.isNewInstance() - && instruction.asNewInstance().clazz == candidateInfo.candidate.type) { + for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) { + if (newInstance.clazz == candidateInfo.candidate.type) { // Remove all usages // NOTE: requiring (a) the instance initializer to be trivial, (b) not allowing // candidates with instance fields and (c) requiring candidate to directly @@ -340,10 +343,31 @@ assert candidateInfo.candidate.superType == factory().objectType; assert candidateInfo.candidate.instanceFields().size() == 0; - Value singletonValue = instruction.outValue(); + Value singletonValue = newInstance.outValue(); assert singletonValue != null; - singletonValue.uniqueUsers().forEach(user -> user.removeOrReplaceByDebugLocalRead(code)); - instruction.removeOrReplaceByDebugLocalRead(code); + + InvokeDirect uniqueConstructorInvoke = + newInstance.getUniqueConstructorInvoke(appView.dexItemFactory()); + assert uniqueConstructorInvoke != null; + uniqueConstructorInvoke.removeOrReplaceByDebugLocalRead(code); + + StaticPut uniqueStaticPut = null; + for (Instruction user : singletonValue.uniqueUsers()) { + if (user.isStaticPut()) { + assert uniqueStaticPut == null; + uniqueStaticPut = user.asStaticPut(); + } + } + assert uniqueStaticPut != null; + uniqueStaticPut.removeOrReplaceByDebugLocalRead(code); + + if (newInstance.outValue().hasAnyUsers()) { + TypeElement type = TypeElement.fromDexType(newInstance.clazz, maybeNull(), appView); + newInstance.replace( + new StaticGet(code.createValue(type), candidateInfo.singletonField.field), code); + } else { + newInstance.removeOrReplaceByDebugLocalRead(code); + } return; } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java index 20ea5df..140c844 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -228,24 +228,23 @@ int concatenationCount = 0; // During the second iteration, count builders' usage. for (Instruction instr : code.instructions()) { - if (!instr.isInvokeVirtual()) { - continue; - } - InvokeVirtual invoke = instr.asInvokeVirtual(); - DexMethod invokedMethod = invoke.getInvokedMethod(); - if (optimizationConfiguration.isAppendMethod(invokedMethod)) { - concatenationCount++; - // The analysis might be overwhelmed. - if (concatenationCount > CONCATENATION_THRESHOLD) { - return ImmutableSet.of(); - } - } else if (optimizationConfiguration.isToStringMethod(invokedMethod)) { - assert invoke.inValues().size() == 1; - Value receiver = invoke.getReceiver().getAliasedValue(); - for (Value builder : collectAllLinkedBuilders(receiver)) { - if (builderToStringCounts.containsKey(builder)) { - int count = builderToStringCounts.getInt(builder); - builderToStringCounts.put(builder, count + 1); + if (instr.isInvokeMethod()) { + InvokeMethod invoke = instr.asInvokeMethod(); + DexMethod invokedMethod = invoke.getInvokedMethod(); + if (optimizationConfiguration.isAppendMethod(invokedMethod)) { + concatenationCount++; + // The analysis might be overwhelmed. + if (concatenationCount > CONCATENATION_THRESHOLD) { + return ImmutableSet.of(); + } + } else if (optimizationConfiguration.isToStringMethod(invokedMethod)) { + assert invoke.arguments().size() == 1; + Value receiver = invoke.getArgument(0).getAliasedValue(); + for (Value builder : collectAllLinkedBuilders(receiver)) { + if (builderToStringCounts.containsKey(builder)) { + int count = builderToStringCounts.getInt(builder); + builderToStringCounts.put(builder, count + 1); + } } } } @@ -582,9 +581,9 @@ if (instr == null) { break; } - InvokeVirtual invoke = instr.asInvokeVirtual(); - assert invoke.inValues().size() == 1; - Value builder = invoke.getReceiver().getAliasedValue(); + InvokeMethod invoke = instr.asInvokeMethod(); + assert invoke.arguments().size() == 1; + Value builder = invoke.getArgument(0).getAliasedValue(); Value outValue = invoke.outValue(); if (outValue == null || outValue.isDead(appView, code)) { // If the out value is still used but potentially dead, replace it with a dummy string. @@ -627,16 +626,16 @@ } private boolean isToStringOfInterest(Set<Value> candidateBuilders, Instruction instr) { - if (!instr.isInvokeVirtual()) { + if (!instr.isInvokeMethod()) { return false; } - InvokeVirtual invoke = instr.asInvokeVirtual(); + InvokeMethod invoke = instr.asInvokeMethod(); DexMethod invokedMethod = invoke.getInvokedMethod(); if (!optimizationConfiguration.isToStringMethod(invokedMethod)) { return false; } - assert invoke.inValues().size() == 1; - Value builder = invoke.getReceiver().getAliasedValue(); + assert invoke.arguments().size() == 1; + Value builder = invoke.getArgument(0).getAliasedValue(); if (!candidateBuilders.contains(builder)) { return false; } @@ -736,11 +735,11 @@ InstructionListIterator it = code.instructionListIterator(); while (it.hasNext()) { Instruction instr = it.next(); - if (instr.isInvokeVirtual()) { - InvokeVirtual invoke = instr.asInvokeVirtual(); + if (instr.isInvokeMethod()) { + InvokeMethod invoke = instr.asInvokeMethod(); DexMethod invokedMethod = invoke.getInvokedMethod(); if (optimizationConfiguration.isToStringMethod(invokedMethod) - && buildersToRemove.contains(invoke.getReceiver().getAliasedValue())) { + && buildersToRemove.contains(invoke.getArgument(0).getAliasedValue())) { it.removeOrReplaceByDebugLocalRead(); } } @@ -852,7 +851,8 @@ @Override public boolean isToStringMethod(DexMethod method) { return method == factory.stringBuilderMethods.toString - || method == factory.stringBufferMethods.toString; + || method == factory.stringBufferMethods.toString + || method == factory.stringMethods.valueOf; } private boolean canHandleArgumentType(DexType argType) { @@ -900,20 +900,12 @@ InvokeMethod invoke = escapeRoute.asInvokeMethod(); DexMethod invokedMethod = invoke.getInvokedMethod(); - // Make sure builder's uses are local, i.e., not escaping from the current method. - if (invokedMethod.holder != builderType) { - logEscapingRoute(false); - return false; - } - // <init> is legitimate. - if (optimizationConfiguration.isBuilderInit(invokedMethod, builderType)) { - return true; - } + if (optimizationConfiguration.isToStringMethod(invokedMethod)) { Value out = escapeRoute.outValue(); if (out != null) { - // If Builder#toString is interned, it could be used for equality check. - // Replacing builder-based runtime result with a compile time constant may change + // If Builder#toString or String#valueOf is interned, it could be used for equality + // check. Replacing builder-based runtime result with a compile time constant may change // the program's runtime behavior. for (Instruction outUser : out.uniqueUsers()) { if (outUser.isInvokeMethodWithReceiver() @@ -924,7 +916,18 @@ } } } - // Otherwise, use of Builder#toString is legitimate. + // Otherwise, use of Builder#toString and String#valueOf is legitimate. + return true; + } + + // Make sure builder's uses are local, i.e., not escaping from the current method. + if (invokedMethod.holder != builderType) { + logEscapingRoute(false); + return false; + } + + // <init> is legitimate. + if (optimizationConfiguration.isBuilderInit(invokedMethod, builderType)) { return true; } // Even though all invocations belong to the builder type, there are some methods other
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java index 1cb6482..f7e377d 100644 --- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java +++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -139,14 +139,14 @@ if (move.definition.isArgument()) { Argument argument = move.definition.asArgument(); int argumentRegister = argument.outValue().getLiveIntervals().getRegister(); - Value to = new FixedRegisterValue(argument.outValue().getType(), move.dst); - Value from = new FixedRegisterValue(argument.outValue().getType(), argumentRegister); + Value to = new FixedRegisterValue(argument.getOutType(), move.dst); + Value from = new FixedRegisterValue(argument.getOutType(), argumentRegister); instruction = new Move(to, from); } else { assert move.definition.isOutConstant(); ConstInstruction definition = move.definition.getOutConstantConstInstruction(); if (definition.isConstNumber()) { - Value to = new FixedRegisterValue(move.definition.outValue().getType(), move.dst); + Value to = new FixedRegisterValue(move.definition.getOutType(), move.dst); instruction = new ConstNumber(to, definition.asConstNumber().getRawValue()); } else { throw new Unreachable("Unexpected definition");
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java index 50885dc..cb1823a 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -30,7 +30,6 @@ import com.android.tools.r8.ir.code.If; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter; -import com.android.tools.r8.utils.StringDiagnostic; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Opcodes; @@ -41,24 +40,14 @@ super(appView, holder); } - boolean shouldConvert( - DexType type, DesugaredLibraryAPIConverter converter, DexString methodName) { + boolean shouldConvert(DexType type, DesugaredLibraryAPIConverter converter, DexMethod method) { if (!appView.rewritePrefix.hasRewrittenType(type, appView)) { return false; } if (converter.canConvert(type)) { return true; } - appView - .options() - .reporter - .warning( - new StringDiagnostic( - "Desugared library API conversion failed for " - + type - + ", unexpected behavior for method " - + methodName - + ".")); + converter.reportInvalidInvoke(type, method, ""); return false; } @@ -101,7 +90,7 @@ DexType[] newParameters = forwardMethod.proto.parameters.values.clone(); for (DexType param : forwardMethod.proto.parameters.values) { instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); - if (shouldConvert(param, converter, forwardMethod.name)) { + if (shouldConvert(param, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC, @@ -132,7 +121,7 @@ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false)); } - if (shouldConvert(returnType, converter, forwardMethod.name)) { + if (shouldConvert(returnType, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC, @@ -188,7 +177,7 @@ int stackIndex = 1; for (DexType param : forwardMethod.proto.parameters.values) { instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); - if (shouldConvert(param, converter, forwardMethod.name)) { + if (shouldConvert(param, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC, @@ -208,7 +197,7 @@ } DexType returnType = forwardMethod.proto.returnType; - if (shouldConvert(returnType, converter, forwardMethod.name)) { + if (shouldConvert(returnType, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC,
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java new file mode 100644 index 0000000..fbfacb8 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -0,0 +1,107 @@ +// Copyright (c) 2020, 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.synthetic; + +import com.android.tools.r8.cf.code.CfConstNumber; +import com.android.tools.r8.cf.code.CfConstString; +import com.android.tools.r8.cf.code.CfIf; +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.cf.code.CfInvoke; +import com.android.tools.r8.cf.code.CfLabel; +import com.android.tools.r8.cf.code.CfLoad; +import com.android.tools.r8.cf.code.CfNew; +import com.android.tools.r8.cf.code.CfReturn; +import com.android.tools.r8.cf.code.CfStackInstruction; +import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfThrow; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; +import com.android.tools.r8.ir.code.If; +import com.android.tools.r8.ir.code.ValueType; +import java.util.ArrayList; +import java.util.List; +import org.objectweb.asm.Opcodes; + +public abstract class EnumUnboxingCfCodeProvider extends SyntheticCfCodeProvider { + + EnumUnboxingCfCodeProvider(AppView<?> appView, DexType holder) { + super(appView, holder); + } + + public static class EnumUnboxingValueOfCfCodeProvider extends EnumUnboxingCfCodeProvider { + + private DexType enumType; + private EnumValueInfoMap map; + + public EnumUnboxingValueOfCfCodeProvider( + AppView<?> appView, DexType holder, DexType enumType, EnumValueInfoMap map) { + super(appView, holder); + this.enumType = enumType; + this.map = map; + } + + @Override + public CfCode generateCfCode() { + // Generated static method, for class com.x.MyEnum {A,B} would look like: + // int UtilityClass#com.x.MyEnum_valueOf(String s) { + // if (s == null) { throw npe("Name is null"); } + // if (s.equals("A")) { return 1;} + // if (s.equals("B")) { return 2;} + // throw new IllegalArgumentException( + // "No enum constant com.x.MyEnum." + s); + DexItemFactory factory = appView.dexItemFactory(); + List<CfInstruction> instructions = new ArrayList<>(); + + // if (s == null) { throw npe("Name is null"); } + CfLabel nullDest = new CfLabel(); + instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0)); + instructions.add(new CfIf(If.Type.NE, ValueType.OBJECT, nullDest)); + instructions.add(new CfNew(factory.npeType)); + instructions.add(new CfStackInstruction(Opcode.Dup)); + instructions.add(new CfConstString(appView.dexItemFactory().createString("Name is null"))); + instructions.add( + new CfInvoke(Opcodes.INVOKESPECIAL, factory.npeMethods.initWithMessage, false)); + instructions.add(new CfThrow()); + instructions.add(nullDest); + + // if (s.equals("A")) { return 1;} + // if (s.equals("B")) { return 2;} + map.forEach( + (field, enumValueInfo) -> { + CfLabel dest = new CfLabel(); + instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0)); + instructions.add(new CfConstString(field.name)); + instructions.add( + new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.stringMethods.equals, false)); + instructions.add(new CfIf(If.Type.EQ, ValueType.INT, dest)); + instructions.add(new CfConstNumber(enumValueInfo.convertToInt(), ValueType.INT)); + instructions.add(new CfReturn(ValueType.INT)); + instructions.add(dest); + }); + + // throw new IllegalArgumentException("No enum constant com.x.MyEnum." + s); + instructions.add(new CfNew(factory.illegalArgumentExceptionType)); + instructions.add(new CfStackInstruction(Opcode.Dup)); + instructions.add( + new CfConstString( + appView + .dexItemFactory() + .createString( + "No enum constant " + enumType.toSourceString().replace('$', '.') + "."))); + instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0)); + instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.stringMethods.concat, false)); + instructions.add( + new CfInvoke( + Opcodes.INVOKESPECIAL, + factory.illegalArgumentExceptionMethods.initWithMessage, + false)); + instructions.add(new CfThrow()); + return standardCfCodeFromInstructions(instructions); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java index 688d59b..e04f07c 100644 --- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java +++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -128,7 +128,8 @@ ClassWriter writer = new ClassWriter(0); int markerStringPoolIndex = writer.newConst(markerString); assert markerStringPoolIndex == MARKER_STRING_CONSTANT_POOL_INDEX; - writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null); + String sourceDebug = getSourceDebugExtension(clazz.annotations()); + writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug); int version = getClassFileVersion(clazz); int access = clazz.accessFlags.getAsCfAccessFlags(); String desc = namingLens.lookupDescriptor(clazz.type).toString(); @@ -242,6 +243,16 @@ return res.toString(); } + private String getSourceDebugExtension(DexAnnotationSet annotations) { + DexValue debugExtensions = + getSystemAnnotationValue( + annotations, application.dexItemFactory.annotationSourceDebugExtension); + if (debugExtensions == null) { + return null; + } + return debugExtensions.asDexValueString().getValue().toString(); + } + private ImmutableMap<DexString, DexValue> getAnnotationDefaults(DexAnnotationSet annotations) { DexValue value = getSystemAnnotationValue(annotations, application.dexItemFactory.annotationDefault);
diff --git a/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java b/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java new file mode 100644 index 0000000..ea1ddc3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java
@@ -0,0 +1,78 @@ +// Copyright (c) 2020, 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.kotlin; + +import static kotlinx.metadata.FlagsKt.flagsOf; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; +import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import java.util.List; +import java.util.function.Function; +import kotlinx.metadata.KmType; +import kotlinx.metadata.KmTypeParameter; +import kotlinx.metadata.KmTypeProjection; +import kotlinx.metadata.KmVariance; + +class ClassTypeSignatureToRenamedKmTypeConverter implements ClassTypeSignature.Converter<KmType> { + + private final AppView<AppInfoWithLiveness> appView; + private final List<KmTypeParameter> typeParameters; + private final Function<DexType, String> toRenamedClassClassifier; + + ClassTypeSignatureToRenamedKmTypeConverter( + AppView<AppInfoWithLiveness> appView, + List<KmTypeParameter> typeParameters, + Function<DexType, String> toRenamedClassClassifier) { + this.appView = appView; + this.typeParameters = typeParameters; + this.toRenamedClassClassifier = toRenamedClassClassifier; + } + + @Override + public KmType init() { + return new KmType(flagsOf()); + } + + @Override + public KmType visitType(DexType type, KmType result) { + String classifier = toRenamedClassClassifier.apply(type); + if (classifier == null) { + return null; + } + result.visitClass(classifier); + return result; + } + + @Override + public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) { + if (result == null) { + return null; + } + List<KmTypeProjection> arguments = result.getArguments(); + KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature( + typeArgument, + () -> { + KmType kmTypeArgument = new KmType(flagsOf()); + arguments.add(new KmTypeProjection(KmVariance.INVARIANT, kmTypeArgument)); + return kmTypeArgument; + }, + typeParameters, + appView.dexItemFactory()); + return result; + } + + @Override + public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) { + // Do nothing + return result; + } + + public List<KmTypeParameter> getTypeParameters() { + return typeParameters; + } +}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java index 136947b..c891897 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -6,9 +6,6 @@ import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix; import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedClassifier; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmConstructor; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType; import static kotlinx.metadata.Flag.IS_SEALED; import com.android.tools.r8.graph.AppView; @@ -22,7 +19,6 @@ import com.android.tools.r8.graph.InnerClassAttribute; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.StringUtils; import java.util.List; import kotlinx.metadata.KmClass; import kotlinx.metadata.KmConstructor; @@ -77,21 +73,26 @@ @Override void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) { + KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this); if (appView.options().enableKotlinMetadataRewritingForRenamedClasses && lens.lookupType(clazz.type, appView.dexItemFactory()) != clazz.type) { - String renamedClassifier = toRenamedClassifier(clazz.type, appView, lens); + String renamedClassifier = synthesizer.toRenamedClassifier(clazz.type); if (renamedClassifier != null) { assert !kmClass.getName().equals(renamedClassifier); kmClass.setName(renamedClassifier); } } + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, getTypeParameters(), synthesizer::toRenamedClassifier); + // Rewriting upward hierarchy. List<KmType> superTypes = kmClass.getSupertypes(); superTypes.clear(); for (DexType itfType : clazz.interfaces.values) { // TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures - KmType kmType = toRenamedKmType(itfType, null, appView, lens); + KmType kmType = synthesizer.toRenamedKmType(itfType, null, null, converter); if (kmType != null) { superTypes.add(kmType); } @@ -99,7 +100,8 @@ assert clazz.superType != null; if (clazz.superType != appView.dexItemFactory().objectType) { // TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature - KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, null, appView, lens); + KmType kmTypeForSupertype = + synthesizer.toRenamedKmType(clazz.superType, null, null, converter); if (kmTypeForSupertype != null) { superTypes.add(kmTypeForSupertype); } @@ -131,7 +133,7 @@ sealedSubclasses.clear(); if (IS_SEALED.invoke(kmClass.getFlags())) { for (DexType subtype : appView.appInfo().allImmediateSubtypes(clazz.type)) { - String classifier = toRenamedClassifier(subtype, appView, lens); + String classifier = synthesizer.toRenamedClassifier(subtype); if (classifier != null) { sealedSubclasses.add(classifier); } @@ -149,7 +151,7 @@ if (!method.isInstanceInitializer()) { continue; } - KmConstructor constructor = toRenamedKmConstructor(method, appView, lens); + KmConstructor constructor = synthesizer.toRenamedKmConstructor(method); if (constructor != null) { constructors.add(constructor); } @@ -162,7 +164,7 @@ // TODO(b/151193864): enum entries - rewriteDeclarationContainer(appView, lens); + rewriteDeclarationContainer(synthesizer); } @Override @@ -190,11 +192,13 @@ @Override public String toString(String indent) { StringBuilder sb = new StringBuilder(indent); - sb.append("Metadata.Class {"); - sb.append(StringUtils.LINE_SEPARATOR); - sb.append(kmDeclarationContainerToString(indent + INDENT)); - sb.append(indent); - sb.append("}"); + appendKmSection( + indent, + "Metadata.Class", + sb, + newIndent -> { + appendKmClass(newIndent, sb, kmClass); + }); return sb.toString(); } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java index 627ac9d..fc3fbbe 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -4,8 +4,6 @@ package com.android.tools.r8.kotlin; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedBinaryName; - import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexType; @@ -46,12 +44,13 @@ @Override void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) { ListIterator<String> partClassIterator = partClassNames.listIterator(); + KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this); while (partClassIterator.hasNext()) { String partClassName = partClassIterator.next(); partClassIterator.remove(); DexType partClassType = appView.dexItemFactory().createType( DescriptorUtils.getDescriptorFromClassBinaryName(partClassName)); - String renamedPartClassName = toRenamedBinaryName(partClassType, appView, lens); + String renamedPartClassName = synthesizer.toRenamedBinaryName(partClassType); if (renamedPartClassName != null) { partClassIterator.add(renamedPartClassName); } @@ -82,6 +81,6 @@ @Override public String toString(String indent) { - return indent + "MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")"; + return indent + "MetaData.MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")"; } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java index e67cd05..90f265c 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,15 +4,12 @@ package com.android.tools.r8.kotlin; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedBinaryName; - import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.DescriptorUtils; -import com.android.tools.r8.utils.StringUtils; import kotlinx.metadata.KmPackage; import kotlinx.metadata.jvm.KotlinClassHeader; import kotlinx.metadata.jvm.KotlinClassMetadata; @@ -45,11 +42,12 @@ void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) { DexType facadeClassType = appView.dexItemFactory().createType( DescriptorUtils.getDescriptorFromClassBinaryName(facadeClassName)); - facadeClassName = toRenamedBinaryName(facadeClassType, appView, lens); + KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this); + facadeClassName = synthesizer.toRenamedBinaryName(facadeClassType); if (!appView.options().enableKotlinMetadataRewritingForMembers) { return; } - rewriteDeclarationContainer(appView, lens); + rewriteDeclarationContainer(synthesizer); } @Override @@ -85,12 +83,14 @@ @Override public String toString(String indent) { StringBuilder sb = new StringBuilder(indent); - sb.append("Metadata.MultiFileClassPart {"); - sb.append(StringUtils.LINE_SEPARATOR); - sb.append(kmDeclarationContainerToString(indent + INDENT)); - appendKeyValue(indent + INDENT, "facadeClassName", facadeClassName, sb); - sb.append(indent); - sb.append("}"); + appendKmSection( + indent, + "Metadata.MultiFileClassPart", + sb, + newIndent -> { + appendKeyValue(newIndent, "facadeClassName", sb, facadeClassName); + appendKmPackage(newIndent, sb, kmPackage); + }); return sb.toString(); } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java index 08fc8ef..8ec966b 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -38,7 +38,7 @@ if (!appView.options().enableKotlinMetadataRewritingForMembers) { return; } - rewriteDeclarationContainer(appView, lens); + rewriteDeclarationContainer(new KotlinMetadataSynthesizer(appView, lens, this)); } @Override @@ -66,11 +66,13 @@ @Override public String toString(String indent) { StringBuilder sb = new StringBuilder(indent); - sb.append("Metadata.MultiFileClassPart {\n"); - sb.append(kmDeclarationContainerToString(indent + INDENT)); - appendKeyValue(indent + INDENT, "package", kmPackage.toString(), sb); - sb.append(indent); - sb.append("}"); + appendKmSection( + indent, + "Metadata.FileFacade", + sb, + newIndent -> { + appendKmPackage(newIndent, sb, kmPackage); + }); return sb.toString(); } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java index 967cf05..64ed559 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.kotlin; -import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction; +import static com.android.tools.r8.utils.StringUtils.LINE_SEPARATOR; import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT; import com.android.tools.r8.errors.Unreachable; @@ -12,29 +12,46 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFieldInfo; +import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo; import com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.KmPropertyGroup; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.Action; import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiFunction; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; +import kotlinx.metadata.KmAnnotation; +import kotlinx.metadata.KmAnnotationArgument; +import kotlinx.metadata.KmClass; +import kotlinx.metadata.KmConstructor; import kotlinx.metadata.KmDeclarationContainer; import kotlinx.metadata.KmFunction; +import kotlinx.metadata.KmPackage; import kotlinx.metadata.KmProperty; import kotlinx.metadata.KmType; import kotlinx.metadata.KmTypeAlias; import kotlinx.metadata.KmTypeParameter; +import kotlinx.metadata.KmTypeProjection; +import kotlinx.metadata.KmValueParameter; +import kotlinx.metadata.jvm.JvmExtensionsKt; +import kotlinx.metadata.jvm.JvmFieldSignature; +import kotlinx.metadata.jvm.JvmMethodSignature; import kotlinx.metadata.jvm.KotlinClassHeader; import kotlinx.metadata.jvm.KotlinClassMetadata; // Provides access to package/class-level Kotlin information. public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> { + final DexClass clazz; + private static final List<KmTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of(); KotlinInfo(MetadataKind metadata, DexClass clazz) { assert clazz != null; @@ -52,6 +69,13 @@ abstract KotlinClassHeader createHeader(); + public final List<KmTypeParameter> getTypeParameters() { + if (!this.isClass()) { + return EMPTY_TYPE_PARAMS; + } + return this.asClass().kmClass.getTypeParameters(); + } + public enum Kind { Class, File, Synthetic, Part, Facade } @@ -116,15 +140,37 @@ // {@link KmClass} and {@link KmPackage} are inherited from {@link KmDeclarationContainer} that // abstract functions and properties. Rewriting of those portions can be unified here. - void rewriteDeclarationContainer(AppView<AppInfoWithLiveness> appView, NamingLens lens) { + void rewriteDeclarationContainer(KotlinMetadataSynthesizer synthesizer) { assert clazz != null; KmDeclarationContainer kmDeclarationContainer = getDeclarations(); - Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>(); + rewriteFunctions(synthesizer, kmDeclarationContainer.getFunctions()); + rewriteProperties(synthesizer, kmDeclarationContainer.getProperties()); + } + private void rewriteFunctions(KotlinMetadataSynthesizer synthesizer, List<KmFunction> functions) { + functions.clear(); + for (DexEncodedMethod method : clazz.methods()) { + if (method.isInitializer()) { + continue; + } + if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) { + KmFunction function = synthesizer.toRenamedKmFunction(method); + if (function != null) { + functions.add(function); + } + } + // TODO(b/151194869): What should we do for methods that fall into this category---no mark? + } + } + + private void rewriteProperties( + KotlinMetadataSynthesizer synthesizer, List<KmProperty> properties) { + Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>(); // Backing fields for a companion object are declared in its host class. Iterable<DexEncodedField> fields = clazz.fields(); Predicate<DexEncodedField> backingFieldTester = DexEncodedField::isKotlinBackingField; + List<KmTypeParameter> classTypeParameters = getTypeParameters(); if (isClass()) { KotlinClass ktClass = asClass(); if (IS_COMPANION_OBJECT.invoke(ktClass.kmClass.getFlags()) && ktClass.hostClass != null) { @@ -132,54 +178,46 @@ backingFieldTester = DexEncodedField::isKotlinBackingFieldForCompanionObject; } } - for (DexEncodedField field : fields) { if (backingFieldTester.test(field)) { - String name = field.getKotlinMemberInfo().propertyName; + KotlinFieldInfo kotlinFieldInfo = field.getKotlinMemberInfo().asFieldInfo(); + assert kotlinFieldInfo != null; + String name = kotlinFieldInfo.propertyName; assert name != null; KmPropertyGroup.Builder builder = propertyGroupBuilderMap.computeIfAbsent( name, - k -> KmPropertyGroup.builder(field.getKotlinMemberInfo().propertyFlags, name)); + k -> KmPropertyGroup.builder(kotlinFieldInfo.flags, name, classTypeParameters)); builder.foundBackingField(field); } } - - List<KmFunction> functions = kmDeclarationContainer.getFunctions(); - functions.clear(); for (DexEncodedMethod method : clazz.methods()) { if (method.isInitializer()) { continue; } - - if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) { - KmFunction function = toRenamedKmFunction(method, appView, lens); - if (function != null) { - functions.add(function); - } - continue; - } if (method.isKotlinProperty() || method.isKotlinExtensionProperty()) { - String name = method.getKotlinMemberInfo().propertyName; + assert method.getKotlinMemberInfo().isPropertyInfo(); + KotlinPropertyInfo kotlinPropertyInfo = method.getKotlinMemberInfo().asPropertyInfo(); + String name = kotlinPropertyInfo.propertyName; assert name != null; KmPropertyGroup.Builder builder = propertyGroupBuilderMap.computeIfAbsent( name, // Hitting here (creating a property builder) after visiting all fields means that // this property doesn't have a backing field. Don't use members' flags. - k -> KmPropertyGroup.builder(method.getKotlinMemberInfo().propertyFlags, name)); - switch (method.getKotlinMemberInfo().memberKind) { + k -> KmPropertyGroup.builder(kotlinPropertyInfo.flags, name, classTypeParameters)); + switch (kotlinPropertyInfo.memberKind) { case EXTENSION_PROPERTY_GETTER: builder.isExtensionGetter(); // fallthrough; case PROPERTY_GETTER: - builder.foundGetter(method, method.getKotlinMemberInfo().flags); + builder.foundGetter(method, kotlinPropertyInfo); break; case EXTENSION_PROPERTY_SETTER: builder.isExtensionSetter(); // fallthrough; case PROPERTY_SETTER: - builder.foundSetter(method, method.getKotlinMemberInfo().flags); + builder.foundSetter(method, kotlinPropertyInfo); break; case EXTENSION_PROPERTY_ANNOTATIONS: builder.isExtensionAnnotations(); @@ -190,20 +228,16 @@ default: throw new Unreachable("Not a Kotlin property: " + method.getKotlinMemberInfo()); } - continue; } - // TODO(b/151194869): What should we do for methods that fall into this category---no mark? } - - List<KmProperty> properties = kmDeclarationContainer.getProperties(); properties.clear(); for (KmPropertyGroup.Builder builder : propertyGroupBuilderMap.values()) { KmPropertyGroup group = builder.build(); if (group == null) { continue; } - KmProperty property = group.toRenamedKmProperty(appView, lens); + KmProperty property = group.toRenamedKmProperty(synthesizer); if (property != null) { properties.add(property); } @@ -212,124 +246,557 @@ public abstract String toString(String indent); - String kmDeclarationContainerToString(String indent) { - StringBuilder sb = new StringBuilder(); - KmDeclarationContainer declarations = getDeclarations(); - appendKmSection(indent, "functions", declarations.getFunctions(), this::kmFunctionToString, sb); - appendKmSection( - indent, "properties", declarations.getProperties(), this::kmPropertyToString, sb); - appendKmSection( - indent, "typeAliases", declarations.getTypeAliases(), this::kmTypeAliasToString, sb); - return sb.toString(); + static final String INDENT = " "; + + private static <T> void appendKmHelper( + String key, StringBuilder sb, Action appendContent, String start, String end) { + sb.append(key); + sb.append(start); + appendContent.execute(); + sb.append(end); } - final String INDENT = " "; + static <T> void appendKmSection( + String indent, String typeDescription, StringBuilder sb, Consumer<String> appendContent) { + appendKmHelper( + typeDescription, + sb, + () -> appendContent.accept(indent + INDENT), + "{" + LINE_SEPARATOR, + indent + "}"); + } - private <T> void appendKmSection( + static <T> void appendKmList( String indent, - String header, + String typeDescription, + StringBuilder sb, List<T> items, - BiFunction<String, T, String> stringify, - StringBuilder sb) { - if (items.size() > 0) { - sb.append(indent); - sb.append(header); - sb.append(": ["); - sb.append(StringUtils.LINE_SEPARATOR); + BiConsumer<String, T> appendItem) { + if (items.isEmpty()) { + sb.append(typeDescription).append("[]"); + return; } - for (T item : items) { - sb.append(stringify.apply(indent + INDENT, item)); - sb.append(","); - sb.append(StringUtils.LINE_SEPARATOR); - } - if (items.size() > 0) { - sb.append(indent); - sb.append("]"); - sb.append(StringUtils.LINE_SEPARATOR); - } + appendKmHelper( + typeDescription, + sb, + () -> { + for (T kmItem : items) { + sb.append(indent).append(INDENT); + appendItem.accept(indent + INDENT, kmItem); + sb.append(LINE_SEPARATOR); + } + }, + "[" + LINE_SEPARATOR, + indent + "]"); } - private String kmFunctionToString(String indent, KmFunction function) { - assert function != null; - StringBuilder sb = new StringBuilder(); + static void appendKeyValue( + String indent, String key, StringBuilder sb, Consumer<String> appendValue) { sb.append(indent); - sb.append("KmFunction {"); - sb.append(StringUtils.LINE_SEPARATOR); - String newIndent = indent + INDENT; - KmType receiverParameterType = function.getReceiverParameterType(); + appendKmHelper(key, sb, () -> appendValue.accept(indent), ": ", "," + LINE_SEPARATOR); + } + + static void appendKeyValue(String indent, String key, StringBuilder sb, String value) { + sb.append(indent); + appendKmHelper(key, sb, () -> sb.append(value), ": ", "," + LINE_SEPARATOR); + } + + static void appendKmDeclarationContainer( + String indent, StringBuilder sb, KmDeclarationContainer container) { appendKeyValue( - newIndent, - "receiverParameterType", - receiverParameterType == null ? "null" : kmTypeToString(receiverParameterType), - sb); - appendKeyValue(newIndent, "returnType", kmTypeToString(function.returnType), sb); - appendKeyValue(newIndent, "name", function.getName(), sb); - // TODO(b/148581822): Print flags, generic signature etc. - sb.append(indent); - sb.append("}"); - return sb.toString(); + indent, + "functions", + sb, + newIndent -> { + appendKmList( + newIndent, + "KmFunction", + sb, + container.getFunctions().stream() + .sorted(Comparator.comparing(KmFunction::getName)) + .collect(Collectors.toList()), + (nextIndent, kmFunction) -> { + appendKmFunction(nextIndent, sb, kmFunction); + }); + }); + appendKeyValue( + indent, + "properties", + sb, + newIndent -> { + appendKmList( + newIndent, + "KmProperty", + sb, + container.getProperties().stream() + .sorted(Comparator.comparing(KmProperty::getName)) + .collect(Collectors.toList()), + (nextIndent, kmProperty) -> { + appendKmProperty(nextIndent, sb, kmProperty); + }); + }); + appendKeyValue( + indent, + "typeAliases", + sb, + newIndent -> { + appendKmList( + newIndent, + "KmTypeAlias", + sb, + container.getTypeAliases().stream() + .sorted(Comparator.comparing(KmTypeAlias::getName)) + .collect(Collectors.toList()), + (nextIndent, kmTypeAlias) -> { + appendTypeAlias(nextIndent, sb, kmTypeAlias); + }); + }); } - private String kmPropertyToString(String indent, KmProperty property) { - // TODO(b/148581822): Add information. - return indent + "KmProperty { " + property + "}"; + static void appendKmPackage(String indent, StringBuilder sb, KmPackage kmPackage) { + appendKmDeclarationContainer(indent, sb, kmPackage); + appendKeyValue(indent, "moduleName", sb, JvmExtensionsKt.getModuleName(kmPackage)); + appendKeyValue( + indent, + "localDelegatedProperties", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmProperty", + sb, + JvmExtensionsKt.getLocalDelegatedProperties(kmPackage), + (nextNextIndent, kmProperty) -> { + appendKmProperty(nextNextIndent, sb, kmProperty); + }); + }); } - private String kmTypeAliasToString(String indent, KmTypeAlias alias) { - assert alias != null; - StringBuilder sb = new StringBuilder(indent); - sb.append("KmTypeAlias {"); - sb.append(StringUtils.LINE_SEPARATOR); - String newIndent = indent + INDENT; - appendKeyValue(newIndent, "name", alias.getName(), sb); - if (!alias.getTypeParameters().isEmpty()) { - appendKeyValue( - newIndent, - "typeParameters", - alias.getTypeParameters().stream() - .map(KmTypeParameter::getName) - .collect(Collectors.joining(",")), - sb); + static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) { + appendKeyValue(indent, "flags", sb, kmClass.getFlags() + ""); + appendKeyValue(indent, "name", sb, kmClass.getName()); + appendKeyValue( + indent, + "typeParameters", + sb, + newIndent -> { + appendTypeParameters(newIndent, sb, kmClass.getTypeParameters()); + }); + appendKeyValue( + indent, + "superTypes", + sb, + newIndent -> { + appendKmList( + newIndent, + "KmType", + sb, + kmClass.getSupertypes(), + (nextIndent, kmType) -> { + appendKmType(nextIndent, sb, kmType); + }); + }); + String companionObject = kmClass.getCompanionObject(); + appendKeyValue( + indent, "enumEntries", sb, "[" + StringUtils.join(kmClass.getEnumEntries(), ",") + "]"); + appendKeyValue( + indent, "companionObject", sb, companionObject == null ? "null" : companionObject); + appendKeyValue( + indent, + "sealedSubclasses", + sb, + "[" + StringUtils.join(kmClass.getSealedSubclasses(), ",") + "]"); + appendKeyValue( + indent, "nestedClasses", sb, "[" + StringUtils.join(kmClass.getNestedClasses(), ",") + "]"); + appendKeyValue( + indent, + "anonymousObjectOriginName", + sb, + JvmExtensionsKt.getAnonymousObjectOriginName(kmClass)); + appendKeyValue(indent, "moduleName", sb, JvmExtensionsKt.getModuleName(kmClass)); + appendKeyValue( + indent, + "localDelegatedProperties", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmProperty", + sb, + JvmExtensionsKt.getLocalDelegatedProperties(kmClass), + (nextNextIndent, kmProperty) -> { + appendKmProperty(nextNextIndent, sb, kmProperty); + }); + }); + appendKeyValue( + indent, + "constructors", + sb, + newIndent -> { + appendKmList( + newIndent, + "KmConstructor", + sb, + kmClass.getConstructors(), + (nextIndent, constructor) -> { + appendKmConstructor(nextIndent, sb, constructor); + }); + }); + appendKmDeclarationContainer(indent, sb, kmClass); + } + + private static void appendKmConstructor( + String indent, StringBuilder sb, KmConstructor constructor) { + appendKmSection( + indent, + "KmConstructor", + sb, + newIndent -> { + appendKeyValue(newIndent, "flags", sb, constructor.getFlags() + ""); + appendKeyValue( + newIndent, + "valueParameters", + sb, + nextIndent -> + appendValueParameters(nextIndent, sb, constructor.getValueParameters())); + JvmMethodSignature signature = JvmExtensionsKt.getSignature(constructor); + appendKeyValue( + newIndent, "signature", sb, signature != null ? signature.asString() : "null"); + }); + } + + private static void appendKmFunction(String indent, StringBuilder sb, KmFunction function) { + appendKmSection( + indent, + "KmFunction", + sb, + newIndent -> { + appendKeyValue(newIndent, "flags", sb, function.getFlags() + ""); + appendKeyValue(newIndent, "name", sb, function.getName()); + appendKeyValue( + newIndent, + "receiverParameterType", + sb, + nextIndent -> appendKmType(nextIndent, sb, function.getReceiverParameterType())); + appendKeyValue( + newIndent, + "returnType", + sb, + nextIndent -> appendKmType(nextIndent, sb, function.getReturnType())); + appendKeyValue( + newIndent, + "typeParameters", + sb, + nextIndent -> appendTypeParameters(nextIndent, sb, function.getTypeParameters())); + appendKeyValue( + newIndent, + "valueParameters", + sb, + nextIndent -> appendValueParameters(nextIndent, sb, function.getValueParameters())); + JvmMethodSignature signature = JvmExtensionsKt.getSignature(function); + appendKeyValue( + newIndent, "signature", sb, signature != null ? signature.asString() : "null"); + appendKeyValue( + newIndent, + "lambdaClassOriginName", + sb, + JvmExtensionsKt.getLambdaClassOriginName(function)); + }); + } + + private static void appendKmProperty(String indent, StringBuilder sb, KmProperty kmProperty) { + appendKmSection( + indent, + "KmProperty", + sb, + newIndent -> { + appendKeyValue(newIndent, "flags", sb, kmProperty.getFlags() + ""); + appendKeyValue(newIndent, "name", sb, kmProperty.getName()); + appendKeyValue( + newIndent, + "receiverParameterType", + sb, + nextIndent -> appendKmType(nextIndent, sb, kmProperty.getReceiverParameterType())); + appendKeyValue( + newIndent, + "returnType", + sb, + nextIndent -> appendKmType(nextIndent, sb, kmProperty.getReturnType())); + appendKeyValue( + newIndent, + "typeParameters", + sb, + nextIndent -> appendTypeParameters(nextIndent, sb, kmProperty.getTypeParameters())); + appendKeyValue(newIndent, "getterFlags", sb, kmProperty.getGetterFlags() + ""); + appendKeyValue(newIndent, "setterFlags", sb, kmProperty.getSetterFlags() + ""); + appendKeyValue( + newIndent, + "setterParameter", + sb, + nextIndent -> appendValueParameter(nextIndent, sb, kmProperty.getSetterParameter())); + appendKeyValue(newIndent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmProperty) + ""); + JvmFieldSignature fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty); + appendKeyValue( + newIndent, + "fieldSignature", + sb, + fieldSignature != null ? fieldSignature.asString() : "null"); + JvmMethodSignature getterSignature = JvmExtensionsKt.getGetterSignature(kmProperty); + appendKeyValue( + newIndent, + "getterSignature", + sb, + getterSignature != null ? getterSignature.asString() : "null"); + JvmMethodSignature setterSignature = JvmExtensionsKt.getSetterSignature(kmProperty); + appendKeyValue( + newIndent, + "setterSignature", + sb, + setterSignature != null ? setterSignature.asString() : "null"); + JvmMethodSignature syntheticMethod = + JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty); + appendKeyValue( + newIndent, + "syntheticMethodForAnnotations", + sb, + syntheticMethod != null ? syntheticMethod.asString() : "null"); + }); + } + + private static void appendKmType(String indent, StringBuilder sb, KmType kmType) { + if (kmType == null) { + sb.append("null"); + return; } - appendType(newIndent, "underlyingType", alias.underlyingType, sb); - appendType(newIndent, "expandedType", alias.expandedType, sb); - // TODO(b/151783973): Extend with annotations. - sb.append(indent); - sb.append("}"); - return sb.toString(); + appendKmSection( + indent, + "KmType", + sb, + newIndent -> { + appendKeyValue(newIndent, "flags", sb, kmType.getFlags() + ""); + appendKeyValue(newIndent, "classifier", sb, kmType.classifier.toString()); + appendKeyValue( + newIndent, + "arguments", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmTypeProjection", + sb, + kmType.getArguments(), + (nextNextIndent, kmTypeProjection) -> { + appendKmTypeProjection(nextNextIndent, sb, kmTypeProjection); + }); + }); + appendKeyValue( + newIndent, + "abbreviatedType", + sb, + nextIndent -> appendKmType(newIndent, sb, kmType.getAbbreviatedType())); + appendKeyValue( + newIndent, + "outerType", + sb, + nextIndent -> appendKmType(newIndent, sb, kmType.getOuterType())); + appendKeyValue( + newIndent, + "extensions", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmAnnotion", + sb, + JvmExtensionsKt.getAnnotations(kmType), + (nextNextIndent, kmAnnotation) -> { + appendKmAnnotation(nextNextIndent, sb, kmAnnotation); + }); + }); + }); } - void appendType(String indent, String key, KmType kmType, StringBuilder sb) { - sb.append(indent); - sb.append(key); - sb.append(" {"); - sb.append(StringUtils.LINE_SEPARATOR); - String newIndent = indent + INDENT; - appendKeyValue(newIndent, "classifier", kmType.classifier.toString(), sb); - if (!kmType.getArguments().isEmpty()) { - appendKeyValue( - newIndent, - "arguments", - kmType.getArguments().stream() - .map(arg -> arg.getType().classifier.toString()) - .collect(Collectors.joining(",")), - sb); + private static void appendKmTypeProjection( + String indent, StringBuilder sb, KmTypeProjection projection) { + appendKmSection( + indent, + "KmTypeProjection", + sb, + newIndent -> { + appendKeyValue( + newIndent, + "type", + sb, + nextIndent -> { + appendKmType(nextIndent, sb, projection.getType()); + }); + if (projection.getVariance() != null) { + appendKeyValue(newIndent, "variance", sb, projection.getVariance().name()); + } + }); + } + + private static void appendValueParameters( + String indent, StringBuilder sb, List<KmValueParameter> valueParameters) { + appendKmList( + indent, + "KmValueParameter", + sb, + valueParameters, + (newIndent, parameter) -> { + appendValueParameter(newIndent, sb, parameter); + }); + } + + private static void appendValueParameter( + String indent, StringBuilder sb, KmValueParameter valueParameter) { + if (valueParameter == null) { + sb.append("null"); + return; } - sb.append(indent); - sb.append("}"); - sb.append(StringUtils.LINE_SEPARATOR); + appendKmSection( + indent, + "KmValueParameter", + sb, + newIndent -> { + appendKeyValue(newIndent, "flags", sb, valueParameter.getFlags() + ""); + appendKeyValue(newIndent, "name", sb, valueParameter.getName()); + appendKeyValue( + newIndent, + "type", + sb, + nextIndent -> { + appendKmType(nextIndent, sb, valueParameter.getType()); + }); + appendKeyValue( + newIndent, + "varargElementType", + sb, + nextIndent -> { + appendKmType(nextIndent, sb, valueParameter.getVarargElementType()); + }); + }); } - void appendKeyValue(String indent, String key, String value, StringBuilder sb) { - sb.append(indent); - sb.append(key); - sb.append(": "); - sb.append(value); - sb.append(StringUtils.LINE_SEPARATOR); + private static void appendTypeParameters( + String indent, StringBuilder sb, List<KmTypeParameter> typeParameters) { + appendKmList( + indent, + "KmTypeParameter", + sb, + typeParameters, + (newIndent, parameter) -> { + appendTypeParameter(newIndent, sb, parameter); + }); } - private String kmTypeToString(KmType type) { - return DescriptorUtils.getDescriptorFromKmType(type); + private static void appendTypeParameter( + String indent, StringBuilder sb, KmTypeParameter typeParameter) { + appendKmSection( + indent, + "KmTypeParameter", + sb, + newIndent -> { + appendKeyValue(newIndent, "id", sb, typeParameter.getId() + ""); + appendKeyValue(newIndent, "flags", sb, typeParameter.getFlags() + ""); + appendKeyValue(newIndent, "name", sb, typeParameter.getName()); + appendKeyValue(newIndent, "variance", sb, typeParameter.getVariance().name()); + appendKeyValue( + newIndent, + "upperBounds", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmType", + sb, + typeParameter.getUpperBounds(), + (nextNextIndent, kmType) -> { + appendKmType(nextNextIndent, sb, kmType); + }); + }); + appendKeyValue( + newIndent, + "extensions", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmAnnotion", + sb, + JvmExtensionsKt.getAnnotations(typeParameter), + (nextNextIndent, kmAnnotation) -> { + appendKmAnnotation(nextNextIndent, sb, kmAnnotation); + }); + }); + }); + } + + private static void appendTypeAlias(String indent, StringBuilder sb, KmTypeAlias kmTypeAlias) { + appendKmSection( + indent, + "KmTypeAlias", + sb, + newIndent -> { + appendKeyValue( + newIndent, + "annotations", + sb, + nextIndent -> { + appendKmList( + nextIndent, + "KmAnnotation", + sb, + kmTypeAlias.getAnnotations(), + (nextNextIndent, kmAnnotation) -> { + appendKmAnnotation(nextNextIndent, sb, kmAnnotation); + }); + }); + appendKeyValue( + newIndent, + "expandedType", + sb, + nextIndent -> { + appendKmType(nextIndent, sb, kmTypeAlias.expandedType); + }); + appendKeyValue(newIndent, "flags", sb, kmTypeAlias.getFlags() + ""); + appendKeyValue(newIndent, "name", sb, kmTypeAlias.getName()); + appendKeyValue( + newIndent, + "typeParameters", + sb, + nextIndent -> { + appendTypeParameters(nextIndent, sb, kmTypeAlias.getTypeParameters()); + }); + appendKeyValue( + newIndent, + "underlyingType", + sb, + nextIndent -> { + appendKmType(nextIndent, sb, kmTypeAlias.underlyingType); + }); + }); + } + + private static void appendKmAnnotation( + String indent, StringBuilder sb, KmAnnotation kmAnnotation) { + appendKmSection( + indent, + "KmAnnotation", + sb, + newIndent -> { + appendKeyValue(newIndent, "className", sb, kmAnnotation.getClassName()); + appendKeyValue( + newIndent, + "arguments", + sb, + nextIndent -> { + Map<String, KmAnnotationArgument<?>> arguments = kmAnnotation.getArguments(); + for (String key : arguments.keySet()) { + appendKeyValue(nextIndent, key, sb, arguments.get(key).toString()); + } + }); + }); } @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java index dfdb28b..ed7b49f 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -3,6 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.CONSTRUCTOR; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_FUNCTION; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_GETTER; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_SETTER; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.FUNCTION; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_GETTER; +import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_SETTER; +import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.getJvmMethodSignature; import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature; import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature; import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension; @@ -10,7 +18,6 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor; import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor; import com.android.tools.r8.utils.Reporter; import com.google.common.collect.ImmutableList; @@ -18,18 +25,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import kotlinx.metadata.KmConstructor; import kotlinx.metadata.KmDeclarationContainer; import kotlinx.metadata.KmFunction; import kotlinx.metadata.KmProperty; +import kotlinx.metadata.KmType; import kotlinx.metadata.KmValueParameter; +import kotlinx.metadata.jvm.JvmMethodSignature; // Provides access to field/method-level Kotlin information. -public class KotlinMemberInfo { - private static final List<KmValueParameter> EMPTY_PARAM = ImmutableList.of(); +public abstract class KotlinMemberInfo { + private static final List<KotlinValueParameterInfo> EMPTY_PARAM_INFO = ImmutableList.of(); - private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = - new KotlinMemberInfo(MemberKind.NONE, 0, EMPTY_PARAM); + private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = new NoKotlinMemberInfo(); public static KotlinMemberInfo getNoKotlinMemberInfo() { return NO_KOTLIN_MEMBER_INFO; @@ -38,76 +47,198 @@ public final MemberKind memberKind; // Original member flags. May be necessary to keep Kotlin-specific flag, e.g., suspend function. final int flags; - // TODO(b/151194869): better to split into FunctionInfo v.s. PropertyInfo ? - // Original property flags. E.g., for property getter, getter flags are stored to `flags`, while - // the property's flags are stored here, in case of properties without a backing field. - final int propertyFlags; - // Original property name for (extension) property. Otherwise, null. - final String propertyName; - // Information from original KmValueParameter(s) if available. Otherwise, null. - private final List<KotlinValueParameterInfo> valueParameterInfos; - // Constructor for KmFunction - private KotlinMemberInfo( - MemberKind memberKind, int flags, List<KmValueParameter> kmValueParameters) { - this(memberKind, flags, 0, null, kmValueParameters); - } - - // Constructor for a backing field and a getter in KmProperty - private KotlinMemberInfo( - MemberKind memberKind, int flags, int propertyFlags, String propertyName) { - this(memberKind, flags, propertyFlags, propertyName, EMPTY_PARAM); - } - - // Constructor for a setter in KmProperty - private KotlinMemberInfo( - MemberKind memberKind, - int flags, - int propertyFlags, - String propertyName, - KmValueParameter kmValueParameter) { - this( - memberKind, - flags, - propertyFlags, - propertyName, - kmValueParameter != null ? ImmutableList.of(kmValueParameter) : EMPTY_PARAM); - } - - private KotlinMemberInfo( - MemberKind memberKind, - int flags, - int propertyFlags, - String propertyName, - List<KmValueParameter> kmValueParameters) { + private KotlinMemberInfo(MemberKind memberKind, int flags) { this.memberKind = memberKind; this.flags = flags; - this.propertyFlags = propertyFlags; - this.propertyName = propertyName; - assert kmValueParameters != null; - if (kmValueParameters.isEmpty()) { - this.valueParameterInfos = EMPTY_PARAM_INFO; - } else { - this.valueParameterInfos = new ArrayList<>(kmValueParameters.size()); - for (KmValueParameter kmValueParameter : kmValueParameters) { - valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter)); - } + } + + public boolean isFunctionInfo() { + return false; + } + + public KotlinFunctionInfo asFunctionInfo() { + return null; + } + + public boolean isFieldInfo() { + return false; + } + + public KotlinFieldInfo asFieldInfo() { + return null; + } + + public boolean isPropertyInfo() { + return false; + } + + public KotlinPropertyInfo asPropertyInfo() { + return null; + } + + private static class NoKotlinMemberInfo extends KotlinMemberInfo { + + private NoKotlinMemberInfo() { + super(MemberKind.NONE, 0); } } - KotlinValueParameterInfo getValueParameterInfo(int i) { - if (valueParameterInfos.isEmpty()) { - return null; + public static class KotlinFunctionInfo extends KotlinMemberInfo { + + // Information from original KmValueParameter(s) if available. + final List<KotlinValueParameterInfo> valueParameterInfos; + // Information from original KmFunction.returnType. Null if this is from a KmConstructor. + public final KotlinTypeInfo returnType; + + private KotlinFunctionInfo( + MemberKind memberKind, + int flags, + KotlinTypeInfo returnType, + List<KotlinValueParameterInfo> valueParameterInfos) { + super(memberKind, flags); + assert memberKind.isFunction() || memberKind.isConstructor(); + this.returnType = returnType; + this.valueParameterInfos = valueParameterInfos; } - if (i < 0 || i >= valueParameterInfos.size()) { - return null; + + KotlinValueParameterInfo getValueParameterInfo(int i) { + if (valueParameterInfos.isEmpty()) { + return null; + } + if (i < 0 || i >= valueParameterInfos.size()) { + return null; + } + return valueParameterInfos.get(i); } - return valueParameterInfos.get(i); + + @Override + public boolean isFunctionInfo() { + return true; + } + + @Override + public KotlinFunctionInfo asFunctionInfo() { + return this; + } + } + + public static class KotlinFieldInfo extends KotlinMemberInfo { + + // Original property name for (extension) property. Otherwise, null. + final String propertyName; + + private KotlinFieldInfo(MemberKind memberKind, int flags, String propertyName) { + super(memberKind, flags); + this.propertyName = propertyName; + } + + @Override + public boolean isFieldInfo() { + return true; + } + + @Override + public KotlinFieldInfo asFieldInfo() { + return this; + } + } + + public static class KotlinPropertyInfo extends KotlinMemberInfo { + + // Original getter flags. E.g., for property getter. + final int getterFlags; + + // Original setter flags. E.g., for property setter. + final int setterFlags; + + // Original property name for (extension) property. Otherwise, null. + final String propertyName; + + // Original return type information. This should never be NULL (even for setters without field). + final KotlinTypeInfo returnType; + + // Information from original KmValueParameter if available. + final KotlinValueParameterInfo valueParameterInfo; + + private KotlinPropertyInfo( + MemberKind memberKind, + int flags, + int getterFlags, + int setterFlags, + String propertyName, + KotlinTypeInfo returnType, + KotlinValueParameterInfo valueParameterInfo) { + super(memberKind, flags); + this.getterFlags = getterFlags; + this.setterFlags = setterFlags; + this.propertyName = propertyName; + this.returnType = returnType; + this.valueParameterInfo = valueParameterInfo; + } + + @Override + public KotlinPropertyInfo asPropertyInfo() { + return this; + } + + @Override + public boolean isPropertyInfo() { + return true; + } + } + + private static KotlinFunctionInfo createFunctionInfoFromConstructor(KmConstructor kmConstructor) { + return createFunctionInfo( + CONSTRUCTOR, kmConstructor.getFlags(), null, kmConstructor.getValueParameters()); + } + + private static KotlinFunctionInfo createFunctionInfo( + MemberKind memberKind, KmFunction kmFunction) { + return createFunctionInfo( + memberKind, + kmFunction.getFlags(), + kmFunction.getReturnType(), + kmFunction.getValueParameters()); + } + + private static KotlinFunctionInfo createFunctionInfo( + MemberKind memberKind, int flags, KmType returnType, List<KmValueParameter> valueParameters) { + assert memberKind.isFunction() || memberKind.isConstructor(); + KotlinTypeInfo returnTypeInfo = KotlinTypeInfo.create(returnType); + assert memberKind.isFunction() || memberKind.isConstructor(); + if (valueParameters.isEmpty()) { + return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, EMPTY_PARAM_INFO); + } + List<KotlinValueParameterInfo> valueParameterInfos = new ArrayList<>(valueParameters.size()); + for (KmValueParameter kmValueParameter : valueParameters) { + valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter)); + } + return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, valueParameterInfos); + } + + private static KotlinFieldInfo createFieldInfo(MemberKind memberKind, KmProperty kmProperty) { + assert memberKind.isBackingField() || memberKind.isBackingFieldForCompanionObject(); + return new KotlinFieldInfo(memberKind, kmProperty.getFlags(), kmProperty.getName()); + } + + private static KotlinPropertyInfo createPropertyInfo( + MemberKind memberKind, KmProperty kmProperty) { + assert memberKind.isProperty(); + return new KotlinPropertyInfo( + memberKind, + kmProperty.getFlags(), + kmProperty.getGetterFlags(), + kmProperty.getSetterFlags(), + kmProperty.getName(), + KotlinTypeInfo.create(kmProperty.getReturnType()), + KotlinValueParameterInfo.fromKmValueParameter(kmProperty.getSetterParameter())); } public enum MemberKind { NONE, + CONSTRUCTOR, FUNCTION, EXTENSION_FUNCTION, @@ -122,6 +253,10 @@ EXTENSION_PROPERTY_SETTER, EXTENSION_PROPERTY_ANNOTATIONS; + public boolean isConstructor() { + return this == CONSTRUCTOR; + } + public boolean isFunction() { return this == FUNCTION || isExtensionFunction(); } @@ -159,23 +294,37 @@ return; } - KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations(); - String companionObject = null; - if (kotlinInfo.isClass()) { - companionObject = kotlinInfo.asClass().kmClass.getCompanionObject(); - } - + Map<String, KmConstructor> kmConstructorMap = new HashMap<>(); Map<String, KmFunction> kmFunctionMap = new HashMap<>(); Map<String, KmProperty> kmPropertyFieldMap = new HashMap<>(); Map<String, KmProperty> kmPropertyGetterMap = new HashMap<>(); Map<String, KmProperty> kmPropertySetterMap = new HashMap<>(); - kmDeclarationContainer.getFunctions().forEach(kmFunction -> { - KmFunctionProcessor functionProcessor = new KmFunctionProcessor(kmFunction, reporter); - if (functionProcessor.signature() != null) { - kmFunctionMap.put(functionProcessor.signature().asString(), kmFunction); - } - }); + KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations(); + String companionObject = null; + if (kotlinInfo.isClass()) { + companionObject = kotlinInfo.asClass().kmClass.getCompanionObject(); + kotlinInfo + .asClass() + .kmClass + .getConstructors() + .forEach( + kmConstructor -> { + JvmMethodSignature methodSignature = getJvmMethodSignature(kmConstructor, reporter); + if (methodSignature != null) { + kmConstructorMap.put(methodSignature.asString(), kmConstructor); + } + }); + } + kmDeclarationContainer + .getFunctions() + .forEach( + kmFunction -> { + JvmMethodSignature methodSignature = getJvmMethodSignature(kmFunction, reporter); + if (methodSignature != null) { + kmFunctionMap.put(methodSignature.asString(), kmFunction); + } + }); kmDeclarationContainer.getProperties().forEach(kmProperty -> { KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty, reporter); if (propertyProcessor.fieldSignature() != null) { @@ -200,72 +349,35 @@ if (kmPropertyFieldMap.containsKey(key)) { KmProperty kmProperty = kmPropertyFieldMap.get(key); field.setKotlinMemberInfo( - new KotlinMemberInfo( + createFieldInfo( clazz == kotlinInfo.clazz ? MemberKind.PROPERTY_BACKING_FIELD : MemberKind.COMPANION_OBJECT_BACKING_FIELD, - kmProperty.getFlags(), - kmProperty.getFlags(), - kmProperty.getName())); + kmProperty)); } } for (DexEncodedMethod method : clazz.methods()) { - if (method.isInitializer()) { - continue; - } String key = toJvmMethodSignature(method.method).asString(); - if (kmFunctionMap.containsKey(key)) { + if (kmConstructorMap.containsKey(key)) { + // Interestingly we cannot assert that the method is a jvm initializer, because the jvm + // signature can be a different. + method.setKotlinMemberInfo(createFunctionInfoFromConstructor(kmConstructorMap.get(key))); + } else if (kmFunctionMap.containsKey(key)) { KmFunction kmFunction = kmFunctionMap.get(key); - if (isExtension(kmFunction)) { - method.setKotlinMemberInfo( - new KotlinMemberInfo( - MemberKind.EXTENSION_FUNCTION, - kmFunction.getFlags(), - kmFunction.getValueParameters())); - } else { - method.setKotlinMemberInfo( - new KotlinMemberInfo( - MemberKind.FUNCTION, - kmFunction.getFlags(), - kmFunction.getValueParameters())); - } + method.setKotlinMemberInfo( + createFunctionInfo( + isExtension(kmFunction) ? EXTENSION_FUNCTION : FUNCTION, kmFunction)); } else if (kmPropertyGetterMap.containsKey(key)) { KmProperty kmProperty = kmPropertyGetterMap.get(key); - if (isExtension(kmProperty)) { - method.setKotlinMemberInfo( - new KotlinMemberInfo( - MemberKind.EXTENSION_PROPERTY_GETTER, - kmProperty.getGetterFlags(), - kmProperty.getFlags(), - kmProperty.getName())); - } else { - method.setKotlinMemberInfo( - new KotlinMemberInfo( - MemberKind.PROPERTY_GETTER, - kmProperty.getGetterFlags(), - kmProperty.getFlags(), - kmProperty.getName())); - } + method.setKotlinMemberInfo( + createPropertyInfo( + isExtension(kmProperty) ? EXTENSION_PROPERTY_GETTER : PROPERTY_GETTER, kmProperty)); } else if (kmPropertySetterMap.containsKey(key)) { KmProperty kmProperty = kmPropertySetterMap.get(key); - if (isExtension(kmProperty)) { - method.setKotlinMemberInfo( - new KotlinMemberInfo( - MemberKind.EXTENSION_PROPERTY_SETTER, - kmProperty.getSetterFlags(), - kmProperty.getFlags(), - kmProperty.getName(), - kmProperty.getSetterParameter())); - } else { - method.setKotlinMemberInfo( - new KotlinMemberInfo( - MemberKind.PROPERTY_SETTER, - kmProperty.getSetterFlags(), - kmProperty.getFlags(), - kmProperty.getName(), - kmProperty.getSetterParameter())); - } + method.setKotlinMemberInfo( + createPropertyInfo( + isExtension(kmProperty) ? EXTENSION_PROPERTY_SETTER : PROPERTY_SETTER, kmProperty)); } } }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java index 474c123..855a71f 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -24,14 +24,12 @@ import kotlinx.metadata.KmConstructorVisitor; import kotlinx.metadata.KmExtensionType; import kotlinx.metadata.KmFunction; -import kotlinx.metadata.KmFunctionExtensionVisitor; -import kotlinx.metadata.KmFunctionVisitor; import kotlinx.metadata.KmProperty; import kotlinx.metadata.KmPropertyExtensionVisitor; import kotlinx.metadata.KmPropertyVisitor; import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor; +import kotlinx.metadata.jvm.JvmExtensionsKt; import kotlinx.metadata.jvm.JvmFieldSignature; -import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor; import kotlinx.metadata.jvm.JvmMethodSignature; import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor; @@ -174,37 +172,25 @@ } } - static class KmFunctionProcessor { - // Custom name via @JvmName("..."). Otherwise, null. - private JvmMethodSignature signature = null; + // Custom name via @JvmName("..."). Otherwise, null. + static JvmMethodSignature getJvmMethodSignature(KmConstructor kmConstructor, Reporter reporter) { + return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmConstructor), reporter); + } - KmFunctionProcessor(KmFunction kmFunction, Reporter reporter) { - kmFunction.accept(new KmFunctionVisitor() { - @Override - public KmFunctionExtensionVisitor visitExtensions(KmExtensionType type) { - if (type != JvmFunctionExtensionVisitor.TYPE) { - return null; - } - return new JvmFunctionExtensionVisitor() { - @Override - public void visit(JvmMethodSignature desc) { - assert signature == null : signature.asString(); - signature = desc; - } - }; - } - }); - if (signature != null) { - String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter); - if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) { - signature = new JvmMethodSignature(signature.getName(), remappedDesc); - } + // Custom name via @JvmName("..."). Otherwise, null. + static JvmMethodSignature getJvmMethodSignature(KmFunction kmFunction, Reporter reporter) { + return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmFunction), reporter); + } + + private static JvmMethodSignature remapJvmMethodSignature( + JvmMethodSignature signature, Reporter reporter) { + if (signature != null) { + String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter); + if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) { + signature = new JvmMethodSignature(signature.getName(), remappedDesc); } } - - JvmMethodSignature signature() { - return signature; - } + return signature; } static class KmPropertyProcessor {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java index 5d1777f..f83796f 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -61,6 +61,8 @@ } public void run(ExecutorService executorService) throws ExecutionException { + // TODO(b/152283077): Don't disable the assert. + appView.appInfo().disableDefinitionForAssert(); ThreadUtils.processItems( appView.appInfo().classes(), clazz -> { @@ -92,6 +94,7 @@ } }, executorService); + appView.appInfo().enableDefinitionForAssert(); } private DexAnnotation createKotlinMetadataAnnotation(KotlinClassHeader header) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java index 43b5cf2..2e90094 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -3,9 +3,10 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; -import static com.android.tools.r8.kotlin.Kotlin.NAME; import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature; import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature; +import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature; +import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.toClassifier; import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier; import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor; import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType; @@ -24,16 +25,21 @@ import com.android.tools.r8.graph.GenericSignature; import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; +import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter; import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature; import com.android.tools.r8.graph.GenericSignature.TypeSignature; +import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFunctionInfo; +import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.Box; import java.util.List; import java.util.function.Consumer; import kotlinx.metadata.KmConstructor; import kotlinx.metadata.KmFunction; import kotlinx.metadata.KmProperty; import kotlinx.metadata.KmType; +import kotlinx.metadata.KmTypeParameter; import kotlinx.metadata.KmTypeProjection; import kotlinx.metadata.KmValueParameter; import kotlinx.metadata.KmVariance; @@ -41,6 +47,17 @@ class KotlinMetadataSynthesizer { + private final AppView<AppInfoWithLiveness> appView; + private final NamingLens lens; + private final List<KmTypeParameter> classTypeParameters; + + public KotlinMetadataSynthesizer( + AppView<AppInfoWithLiveness> appView, NamingLens lens, KotlinInfo<?> kotlinInfo) { + this.appView = appView; + this.lens = lens; + this.classTypeParameters = kotlinInfo.getTypeParameters(); + } + static boolean isExtension(KmFunction kmFunction) { return kmFunction.getReceiverParameterType() != null; } @@ -55,8 +72,7 @@ return kmType; } - static DexType toRenamedType( - DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) { + private DexType toRenamedType(DexType type) { // For library or classpath class, synthesize @Metadata always. // For a program class, make sure it is live. if (!appView.appInfo().isNonProgramTypeOrLiveProgramType(type)) { @@ -70,141 +86,92 @@ return renamedType; } - static String toRenamedBinaryName( - DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) { - DexType renamedType = toRenamedType(type, appView, lens); + String toRenamedBinaryName(DexType type) { + DexType renamedType = toRenamedType(type); if (renamedType == null) { return null; } return getBinaryNameFromDescriptor(renamedType.toDescriptorString()); } - static String toRenamedClassifier( - DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) { - // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray - if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) { - DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type); - assert convertedType != null; - return descriptorToKotlinClassifier(convertedType.toDescriptorString()); - } - // E.g., [Ljava/lang/String; -> kotlin/Array - if (type.isArrayType()) { - return NAME + "/Array"; - } - DexType renamedType = toRenamedType(type, appView, lens); - if (renamedType == null) { + String toRenamedClassifier(DexType type) { + type = type.isClassType() ? toRenamedType(type) : type; + if (type == null) { return null; } - return descriptorToKotlinClassifier(renamedType.toDescriptorString()); + return toClassifier(type, appView.dexItemFactory()); } // TODO(b/148654451): Canonicalization? - static KmType toRenamedKmType( + KmType toRenamedKmType( DexType type, TypeSignature typeSignature, - AppView<AppInfoWithLiveness> appView, - NamingLens lens) { - if (typeSignature != null - && typeSignature.isFieldTypeSignature() - && typeSignature.asFieldTypeSignature().isClassTypeSignature() - && typeSignature.asFieldTypeSignature().asClassTypeSignature().type() == type) { - return toRenamedKmType( - typeSignature.asFieldTypeSignature().asClassTypeSignature(), appView, lens); + KotlinTypeInfo originalKotlinTypeInfo, + ClassTypeSignatureToRenamedKmTypeConverter converter) { + if (originalKotlinTypeInfo != null && originalKotlinTypeInfo.isTypeAlias()) { + KmType kmType = new KmType(flagsOf()); + kmType.visitTypeAlias(originalKotlinTypeInfo.asTypeAlias().getName()); + return kmType; } + return toRenamedKmTypeWithClassifier(type, typeSignature, converter); + } - String classifier = toRenamedClassifier(type, appView, lens); + private KmType toRenamedKmTypeWithClassifierForFieldSignature( + DexType type, + FieldTypeSignature fieldSignature, + ClassTypeSignatureToRenamedKmTypeConverter converter) { + if (fieldSignature.isClassTypeSignature()) { + ClassTypeSignature classTypeSignature = fieldSignature.asClassTypeSignature(); + if (classTypeSignature.type() != type) { + return null; + } + return classTypeSignature.convert(converter); + } + // If we fail to set kmType.classifier we will get a UninitializedPropertyAccessException: + // lateinit property classifier has not been initialized. + Box<KmType> kmTypeBox = new Box<>(); + populateKmTypeFromSignature( + fieldSignature, + () -> { + KmType value = new KmType(flagsOf()); + kmTypeBox.set(value); + return value; + }, + converter.getTypeParameters(), + appView.dexItemFactory()); + return kmTypeBox.get(); + } + + private KmType toRenamedKmTypeWithClassifier( + DexType type, + TypeSignature typeSignature, + ClassTypeSignatureToRenamedKmTypeConverter converter) { + if (typeSignature != null && typeSignature.isFieldTypeSignature()) { + KmType renamedKmType = + toRenamedKmTypeWithClassifierForFieldSignature( + type, typeSignature.asFieldTypeSignature(), converter); + if (renamedKmType != null) { + return renamedKmType; + } + } + String classifier = toRenamedClassifier(type); if (classifier == null) { return null; } - // TODO(b/151194869): Mysterious, why attempts to properly set flags bothers kotlinc? - // and/or why wiping out flags works for KmType but not KmFunction?! - KmType kmType = new KmType(flagsOf()); - kmType.visitClass(classifier); + // Seems like no flags for KmType are ever set, thus passing in flagsOf() seems ok. + KmType renamedKmType = new KmType(flagsOf()); + renamedKmType.visitClass(classifier); // TODO(b/151194164): Can be generalized too, like ArrayTypeSignature.Converter ? // E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String)) if (type.isArrayType() && !type.isPrimitiveArrayType()) { DexType elementType = type.toArrayElementType(appView.dexItemFactory()); - KmType argumentType = toRenamedKmType(elementType, null, appView, lens); - kmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType)); - } - return kmType; - } - - static KmType toRenamedKmType( - ClassTypeSignature classTypeSignature, - AppView<AppInfoWithLiveness> appView, - NamingLens lens) { - return classTypeSignature.convert( - new ClassTypeSignatureToRenamedKmTypeConverter(appView, lens)); - } - - static class ClassTypeSignatureToRenamedKmTypeConverter - implements ClassTypeSignature.Converter<KmType> { - - private final AppView<AppInfoWithLiveness> appView; - private final NamingLens lens; - - ClassTypeSignatureToRenamedKmTypeConverter( - AppView<AppInfoWithLiveness> appView, NamingLens lens) { - this.appView = appView; - this.lens = lens; - } - - @Override - public KmType init() { - return new KmType(flagsOf()); - } - - @Override - public KmType visitType(DexType type, KmType result) { - if (result == null) { - return result; - } - String classifier = toRenamedClassifier(type, appView, lens); - if (classifier == null) { - return null; - } - result.visitClass(classifier); - return result; - } - - @Override - public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) { - if (result == null) { - return result; - } - if (typeArgument.isClassTypeSignature()) { - KmType argumentType = typeArgument.asClassTypeSignature().convert(this); - result.getArguments().add(new KmTypeProjection(KmVariance.INVARIANT, argumentType)); - } - // TODO(b/151194164): for TypeVariableSignature, there is KmType::visitTypeParameter. - return result; - } - - @Override - public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) { - // Do nothing - return result; - } - } - - private static KmType setRenamedKmType( - DexType type, - TypeSignature signature, - AppView<AppInfoWithLiveness> appView, - NamingLens lens, - Consumer<KmType> consumer) { - KmType renamedKmType = toRenamedKmType(type, signature, appView, lens); - if (renamedKmType != null) { - consumer.accept(renamedKmType); + KmType argumentType = toRenamedKmTypeWithClassifier(elementType, null, converter); + renamedKmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType)); } return renamedKmType; } - static KmConstructor toRenamedKmConstructor( - DexEncodedMethod method, - AppView<AppInfoWithLiveness> appView, - NamingLens lens) { + KmConstructor toRenamedKmConstructor(DexEncodedMethod method) { // Make sure it is an instance initializer and live. if (!method.isInstanceInitializer() || !appView.appInfo().liveMethods.contains(method.method)) { @@ -213,17 +180,23 @@ KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags()); JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method)); MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView); + List<KmTypeParameter> typeParameters = + convertFormalTypeParameters( + signature.getFormalTypeParameters(), + kmTypeParameter -> { + assert false : "KmConstructor cannot have additional type parameters"; + }); + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, typeParameters, this::toRenamedClassifier); List<KmValueParameter> parameters = kmConstructor.getValueParameters(); - if (!populateKmValueParameters(parameters, method, signature, appView, lens)) { + if (!populateKmValueParameters(method, signature, parameters, converter)) { return null; } return kmConstructor; } - static KmFunction toRenamedKmFunction( - DexEncodedMethod method, - AppView<AppInfoWithLiveness> appView, - NamingLens lens) { + KmFunction toRenamedKmFunction(DexEncodedMethod method) { // For library overrides, synthesize @Metadata always. // For regular methods, make sure it is live or pinned. if (!method.isLibraryMethodOverride().isTrue() @@ -237,9 +210,11 @@ : method.toSourceString() + " -> " + renamedMethod.toSourceString(); // TODO(b/151194869): Should we keep kotlin-specific flags only while synthesizing the base // value from general JVM flags? + KotlinFunctionInfo kotlinMemberInfo = method.getKotlinMemberInfo().asFunctionInfo(); + assert kotlinMemberInfo != null; int flag = - appView.appInfo().isPinned(method.method) && method.getKotlinMemberInfo() != null - ? method.getKotlinMemberInfo().flags + appView.appInfo().isPinned(method.method) + ? kotlinMemberInfo.flags : method.accessFlags.getAsKotlinFlags(); KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString()); JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod)); @@ -248,57 +223,65 @@ // on demand? That may require utils to map internal encoding of signature back to // corresponding backend definitions, like DexAnnotation (DEX) or Signature attribute (CF). MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView); + List<KmTypeParameter> typeParameters = kmFunction.getTypeParameters(); + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, + convertFormalTypeParameters(signature.getFormalTypeParameters(), typeParameters::add), + this::toRenamedClassifier); DexProto proto = method.method.proto; DexType returnType = proto.returnType; TypeSignature returnSignature = signature.returnType().typeSignature(); - KmType kmReturnType = setRenamedKmType( - returnType, returnSignature, appView, lens, kmFunction::setReturnType); + KmType kmReturnType = + toRenamedKmType(returnType, returnSignature, kotlinMemberInfo.returnType, converter); if (kmReturnType == null) { return null; } - + kmFunction.setReturnType(kmReturnType); if (method.isKotlinExtensionFunction()) { - assert proto.parameters.values.length > 0 - : method.method.toSourceString(); + assert proto.parameters.values.length > 0 : method.method.toSourceString(); DexType receiverType = proto.parameters.values[0]; TypeSignature receiverSignature = signature.getParameterTypeSignature(0); - KmType kmReceiverType = setRenamedKmType( - receiverType, receiverSignature, appView, lens, kmFunction::setReceiverParameterType); + KmType kmReceiverType = toRenamedKmType(receiverType, receiverSignature, null, converter); if (kmReceiverType == null) { return null; } + kmFunction.setReceiverParameterType(kmReceiverType); } - List<KmValueParameter> parameters = kmFunction.getValueParameters(); - if (!populateKmValueParameters(parameters, method, signature, appView, lens)) { + if (!populateKmValueParameters(method, signature, kmFunction.getValueParameters(), converter)) { return null; } return kmFunction; } - private static boolean populateKmValueParameters( - List<KmValueParameter> parameters, + private List<KmTypeParameter> convertFormalTypeParameters( + List<FormalTypeParameter> parameters, Consumer<KmTypeParameter> addedFromParameters) { + return KotlinMetadataSynthesizerUtils.convertFormalTypeParameters( + classTypeParameters, parameters, appView.dexItemFactory(), addedFromParameters); + } + + private boolean populateKmValueParameters( DexEncodedMethod method, MethodTypeSignature signature, - AppView<AppInfoWithLiveness> appView, - NamingLens lens) { + List<KmValueParameter> parameters, + ClassTypeSignatureToRenamedKmTypeConverter converter) { + KotlinFunctionInfo kotlinFunctionInfo = method.getKotlinMemberInfo().asFunctionInfo(); + if (kotlinFunctionInfo == null) { + return false; + } boolean isExtension = method.isKotlinExtensionFunction(); for (int i = isExtension ? 1 : 0; i < method.method.proto.parameters.values.length; i++) { DexType parameterType = method.method.proto.parameters.values[i]; DebugLocalInfo debugLocalInfo = method.getParameterInfo().get(i); String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i); KotlinValueParameterInfo valueParameterInfo = - method.getKotlinMemberInfo().getValueParameterInfo(isExtension ? i - 1 : i); + kotlinFunctionInfo.getValueParameterInfo(isExtension ? i - 1 : i); TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i); KmValueParameter kmValueParameter = toRewrittenKmValueParameter( - valueParameterInfo, - parameterType, - parameterTypeSignature, - parameterName, - appView, - lens); + valueParameterInfo, parameterType, parameterTypeSignature, parameterName, converter); if (kmValueParameter == null) { return false; } @@ -307,26 +290,28 @@ return true; } - private static KmValueParameter toRewrittenKmValueParameter( + private KmValueParameter toRewrittenKmValueParameter( KotlinValueParameterInfo valueParameterInfo, DexType parameterType, TypeSignature parameterTypeSignature, String candidateParameterName, - AppView<AppInfoWithLiveness> appView, - NamingLens lens) { + ClassTypeSignatureToRenamedKmTypeConverter converter) { int flag = valueParameterInfo != null ? valueParameterInfo.flag : flagsOf(); String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName; KmValueParameter kmValueParameter = new KmValueParameter(flag, name); - KmType kmParamType = setRenamedKmType( - parameterType, parameterTypeSignature, appView, lens, kmValueParameter::setType); + KotlinTypeInfo originalKmTypeInfo = valueParameterInfo != null ? valueParameterInfo.type : null; + KmType kmParamType = + toRenamedKmType(parameterType, parameterTypeSignature, originalKmTypeInfo, converter); if (kmParamType == null) { return null; } + kmValueParameter.setType(kmParamType); if (valueParameterInfo != null) { JvmExtensionsKt.getAnnotations(kmParamType).addAll(valueParameterInfo.annotations); } if (valueParameterInfo != null && valueParameterInfo.isVararg) { + // TODO(b/152389234): Test for arrays in varargs. if (!parameterType.isArrayType()) { return null; } @@ -334,11 +319,11 @@ TypeSignature elementSignature = parameterTypeSignature != null ? parameterTypeSignature.toArrayElementTypeSignature(appView) : null; - KmType kmElementType = setRenamedKmType( - elementType, elementSignature, appView, lens, kmValueParameter::setVarargElementType); + KmType kmElementType = toRenamedKmType(elementType, elementSignature, null, converter); if (kmElementType == null) { return null; } + kmValueParameter.setVarargElementType(kmElementType); } return kmValueParameter; @@ -370,58 +355,65 @@ * getter, and so on. */ static class KmPropertyGroup { + final int flags; final String name; final DexEncodedField field; - final DexEncodedMethod getter; - final int getterFlags; - final DexEncodedMethod setter; - final int setterFlags; + private final DexEncodedMethod getter; + private final KotlinPropertyInfo getterInfo; + private final DexEncodedMethod setter; + private final KotlinPropertyInfo setterInfo; final DexEncodedMethod annotations; final boolean isExtension; + private final List<KmTypeParameter> classTypeParameters; private KmPropertyGroup( int flags, String name, DexEncodedField field, DexEncodedMethod getter, - int getterFlags, + KotlinPropertyInfo getterInfo, DexEncodedMethod setter, - int setterFlags, + KotlinPropertyInfo setterInfo, DexEncodedMethod annotations, - boolean isExtension) { + boolean isExtension, + List<KmTypeParameter> classTypeParameters) { this.flags = flags; this.name = name; this.field = field; this.getter = getter; - this.getterFlags = getterFlags; + this.getterInfo = getterInfo; this.setter = setter; - this.setterFlags = setterFlags; + this.setterInfo = setterInfo; this.annotations = annotations; this.isExtension = isExtension; + this.classTypeParameters = classTypeParameters; } - static Builder builder(int flags, String name) { - return new Builder(flags, name); + static Builder builder(int flags, String name, List<KmTypeParameter> classTypeParameters) { + return new Builder(flags, name, classTypeParameters); } static class Builder { + private final int flags; private final String name; private DexEncodedField field; private DexEncodedMethod getter; - private int getterFlags; + private KotlinPropertyInfo getterInfo; private DexEncodedMethod setter; - private int setterFlags; + private KotlinPropertyInfo setterInfo; private DexEncodedMethod annotations; + private List<KmTypeParameter> classTypeParameters; private boolean isExtensionGetter; private boolean isExtensionSetter; private boolean isExtensionAnnotations; - private Builder(int flags, String name) { + private Builder(int flags, String name, List<KmTypeParameter> classTypeParameters) { this.flags = flags; this.name = name; + this.classTypeParameters = classTypeParameters; } Builder foundBackingField(DexEncodedField field) { @@ -429,15 +421,15 @@ return this; } - Builder foundGetter(DexEncodedMethod getter, int flags) { + Builder foundGetter(DexEncodedMethod getter, KotlinPropertyInfo propertyInfo) { this.getter = getter; - this.getterFlags = flags; + this.getterInfo = propertyInfo; return this; } - Builder foundSetter(DexEncodedMethod setter, int flags) { + Builder foundSetter(DexEncodedMethod setter, KotlinPropertyInfo propertyInfo) { this.setter = setter; - this.setterFlags = flags; + this.setterInfo = propertyInfo; return this; } @@ -476,177 +468,234 @@ } } return new KmPropertyGroup( - flags, name, field, getter, getterFlags, setter, setterFlags, annotations, isExtension); + flags, + name, + field, + getter, + getterInfo, + setter, + setterInfo, + annotations, + isExtension, + classTypeParameters); } } - KmProperty toRenamedKmProperty(AppView<AppInfoWithLiveness> appView, NamingLens lens) { - KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf()); - KmType kmPropertyType = null; - KmType kmReceiverType = null; - - // A flag to indicate we can rename the property name. This will become false if any member - // is pinned. Then, we conservatively assume that users want the property to be pinned too. - // That is, we won't rename the property even though some other members could be renamed. - boolean canChangePropertyName = true; - // A candidate property name. Not overwritten by the following members, hence the order of - // preference: a backing field, getter, and setter. - String renamedPropertyName = name; - if (field != null) { - if (appView.appInfo().isPinned(field.field)) { - canChangePropertyName = false; + private String setFieldForProperty( + KmProperty kmProperty, + KmType defaultPropertyType, + AppView<AppInfoWithLiveness> appView, + NamingLens lens, + KotlinMetadataSynthesizer synthesizer) { + DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory()); + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, this.classTypeParameters, synthesizer::toRenamedClassifier); + if (kmProperty.getReturnType() == defaultPropertyType) { + KmType kmPropertyType = + synthesizer.toRenamedKmType(field.field.type, null, null, converter); + if (kmPropertyType != null) { + kmProperty.setReturnType(kmPropertyType); } - DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory()); - if (canChangePropertyName && renamedField.name != field.field.name) { - renamedPropertyName = renamedField.name.toString(); - } - FieldTypeSignature signature = - GenericSignature.Parser.toFieldTypeSignature(field, appView); - kmPropertyType = - setRenamedKmType(field.field.type, signature, appView, lens, kmProperty::setReturnType); - JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField)); } + JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField)); + return appView.appInfo().isPinned(field.field) ? null : renamedField.name.toString(); + } - GetterSetterCriteria criteria = checkGetterCriteria(); - if (criteria == GetterSetterCriteria.VIOLATE) { - return null; + private boolean setReceiverParameterTypeForExtensionProperty( + KmProperty kmProperty, + DexEncodedMethod method, + AppView<AppInfoWithLiveness> appView, + KotlinMetadataSynthesizer synthesizer) { + if (!isExtension) { + return true; } + MethodTypeSignature signature = + GenericSignature.Parser.toMethodTypeSignature(method, appView); + List<KmTypeParameter> typeParameters = + KotlinMetadataSynthesizerUtils.convertFormalTypeParameters( + classTypeParameters, + signature.getFormalTypeParameters(), + appView.dexItemFactory(), + kmTypeParameter -> {}); + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, typeParameters, synthesizer::toRenamedClassifier); + DexType receiverType = method.method.proto.parameters.values[0]; + TypeSignature receiverSignature = signature.getParameterTypeSignature(0); + KmType kmReceiverType = + synthesizer.toRenamedKmType(receiverType, receiverSignature, null, converter); + if (kmProperty.getReceiverParameterType() != null) { + // If the receiver type for the extension property is set already make sure it's consistent. + return getDescriptorFromKmType(kmReceiverType) + .equals(getDescriptorFromKmType(kmProperty.getReceiverParameterType())); + } + kmProperty.setReceiverParameterType(kmReceiverType); + return true; + } - if (criteria == GetterSetterCriteria.MET) { - assert getter != null && getter.method.proto.parameters.size() == (isExtension ? 1 : 0) - : "checkGetterCriteria: " + this.toString(); - MethodTypeSignature signature = - GenericSignature.Parser.toMethodTypeSignature(getter, appView); - if (isExtension) { - TypeSignature receiverSignature = signature.getParameterTypeSignature(0); - kmReceiverType = - setRenamedKmType( - getter.method.proto.parameters.values[0], - receiverSignature, - appView, - lens, - kmProperty::setReceiverParameterType); - } - - DexType returnType = getter.method.proto.returnType; - TypeSignature returnSignature = signature.returnType().typeSignature(); - if (kmPropertyType == null) { - // The property type is not set yet. - kmPropertyType = setRenamedKmType( - returnType, returnSignature, appView, lens, kmProperty::setReturnType); - } else { - // If property type is set already (via backing field), make sure it's consistent. - KmType kmPropertyTypeFromGetter = - toRenamedKmType(returnType, returnSignature, appView, lens); - if (!getDescriptorFromKmType(kmPropertyType) - .equals(getDescriptorFromKmType(kmPropertyTypeFromGetter))) { - return null; - } - } - if (appView.appInfo().isPinned(getter.method)) { - canChangePropertyName = false; - } - DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory()); - if (canChangePropertyName - && renamedGetter.name != getter.method.name - && renamedPropertyName.equals(name)) { - renamedPropertyName = renamedGetter.name.toString(); - } - kmProperty.setGetterFlags(getterFlags); - JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter)); - } else if (field != null) { + private String setGetterForProperty( + KmProperty kmProperty, + KmType defaultPropertyType, + AppView<AppInfoWithLiveness> appView, + NamingLens lens, + KotlinMetadataSynthesizer synthesizer) { + if (checkGetterCriteria() == GetterSetterCriteria.NOT_AVAILABLE) { // Property without getter. // Even though a getter does not exist, `kotlinc` still set getter flags and use them to // determine when to direct field access v.s. getter calls for property resolution. - kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags()); + if (field != null) { + kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags()); + } + return kmProperty.getName(); } + assert checkGetterCriteria() == GetterSetterCriteria.MET; + assert getter != null; + DexProto proto = getter.method.proto; + assert proto.parameters.size() == (isExtension ? 1 : 0) + : "checkGetterCriteria: " + this.toString(); + MethodTypeSignature signature = + GenericSignature.Parser.toMethodTypeSignature(getter, appView); + List<KmTypeParameter> typeParameters = + KotlinMetadataSynthesizerUtils.convertFormalTypeParameters( + this.classTypeParameters, + signature.getFormalTypeParameters(), + appView.dexItemFactory(), + kmTypeParameter -> {}); + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, typeParameters, synthesizer::toRenamedClassifier); + DexType returnType = proto.returnType; + TypeSignature returnSignature = signature.returnType().typeSignature(); + KmType kmPropertyType = + synthesizer.toRenamedKmType( + returnType, returnSignature, getterInfo.returnType, converter); + if (kmProperty.getReturnType() == defaultPropertyType) { + // The property type is not set yet. + kmProperty.setReturnType(kmPropertyType); + } else if (!getDescriptorFromKmType(kmPropertyType) + .equals(getDescriptorFromKmType(kmProperty.getReturnType()))) { + // If property type is set already (via backing field), make sure it's consistent. + return null; + } + DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory()); + kmProperty.setGetterFlags(getter.accessFlags.getAsKotlinFlags()); + JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter)); + return appView.appInfo().isPinned(getter.method) ? null : renamedGetter.name.toString(); + } - criteria = checkSetterCriteria(); + private String setSetterForProperty( + KmProperty kmProperty, + KmType defaultPropertyType, + AppView<AppInfoWithLiveness> appView, + NamingLens lens, + KotlinMetadataSynthesizer synthesizer) { + GetterSetterCriteria criteria = checkSetterCriteria(); if (criteria == GetterSetterCriteria.VIOLATE) { - return null; + return kmProperty.getName(); } - - if (criteria == GetterSetterCriteria.MET) { - assert setter != null && setter.method.proto.parameters.size() == (isExtension ? 2 : 1) - : "checkSetterCriteria: " + this.toString(); - MethodTypeSignature signature = - GenericSignature.Parser.toMethodTypeSignature(setter, appView); - if (isExtension) { - DexType receiverType = setter.method.proto.parameters.values[0]; - TypeSignature receiverSignature = signature.getParameterTypeSignature(0); - if (kmReceiverType == null) { - kmReceiverType = - setRenamedKmType( - receiverType, - receiverSignature, - appView, - lens, - kmProperty::setReceiverParameterType); - } else { - // If the receiver type for the extension property is set already (via getter), - // make sure it's consistent. - KmType kmReceiverTypeFromSetter = - toRenamedKmType(receiverType, receiverSignature, appView, lens); - if (!getDescriptorFromKmType(kmReceiverType) - .equals(getDescriptorFromKmType(kmReceiverTypeFromSetter))) { - return null; - } - } + if (criteria == GetterSetterCriteria.NOT_AVAILABLE) { + if (field != null && IS_VAR.invoke(flags)) { + // Editable property without setter. + // Even though a setter does not exist, `kotlinc` still set setter flags and use them to + // determine when to direct field access v.s. setter calls for property resolution. + kmProperty.setSetterFlags(field.accessFlags.getAsKotlinFlags()); } - - int valueIndex = isExtension ? 1 : 0; - DexType valueType = setter.method.proto.parameters.values[valueIndex]; - TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex); - if (kmPropertyType == null) { - // The property type is not set yet. - kmPropertyType = - setRenamedKmType(valueType, valueSignature, appView, lens, kmProperty::setReturnType); - } else { - // If property type is set already (via either backing field or getter), - // make sure it's consistent. - KmType kmPropertyTypeFromSetter = - toRenamedKmType(valueType, valueSignature, appView, lens); - if (!getDescriptorFromKmType(kmPropertyType) - .equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) { - return null; - } - } - KotlinValueParameterInfo valueParameterInfo = - setter.getKotlinMemberInfo().getValueParameterInfo(valueIndex); - KmValueParameter kmValueParameter = toRewrittenKmValueParameter( - valueParameterInfo, valueType, valueSignature, "value", appView, lens); - if (kmValueParameter != null) { - kmProperty.setSetterParameter(kmValueParameter); - } - if (appView.appInfo().isPinned(setter.method)) { - canChangePropertyName = false; - } - DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory()); - if (canChangePropertyName - && renamedSetter.name != setter.method.name - && renamedPropertyName.equals(name)) { - renamedPropertyName = renamedSetter.name.toString(); - } - kmProperty.setSetterFlags(setterFlags); - JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter)); - } else if (field != null && IS_VAR.invoke(flags)) { - // Editable property without setter. - // Even though a setter does not exist, `kotlinc` still set setter flags and use them to - // determine when to direct field access v.s. setter calls for property resolution. - kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags()); + return kmProperty.getName(); } - - // If the property type remains null at the end, bail out to synthesize this property. - if (kmPropertyType == null) { - return null; + assert criteria == GetterSetterCriteria.MET; + assert setter != null; + DexProto proto = setter.method.proto; + assert proto.parameters.size() == (isExtension ? 2 : 1) + : "checkSetterCriteria: " + this.toString(); + MethodTypeSignature signature = + GenericSignature.Parser.toMethodTypeSignature(setter, appView); + List<KmTypeParameter> typeParameters = + KotlinMetadataSynthesizerUtils.convertFormalTypeParameters( + classTypeParameters, + signature.getFormalTypeParameters(), + appView.dexItemFactory(), + kmTypeParameter -> {}); + ClassTypeSignatureToRenamedKmTypeConverter converter = + new ClassTypeSignatureToRenamedKmTypeConverter( + appView, typeParameters, synthesizer::toRenamedClassifier); + int valueIndex = isExtension ? 1 : 0; + DexType valueType = proto.parameters.values[valueIndex]; + TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex); + KmType kmPropertyType = + synthesizer.toRenamedKmType(valueType, valueSignature, setterInfo.returnType, converter); + if (kmProperty.getReturnType() == defaultPropertyType) { + // The property type is not set yet. + kmProperty.setReturnType(kmPropertyType); + } else { + // If property type is set already make sure it's consistent. + if (!getDescriptorFromKmType(kmPropertyType) + .equals(getDescriptorFromKmType(kmProperty.getReturnType()))) { + return null; + } } - // For extension property, if the receiver type remains null at the end, bail out too. - if (isExtension && kmReceiverType == null) { - return null; + assert setter.getKotlinMemberInfo().isPropertyInfo(); + KotlinValueParameterInfo valueParameterInfo = + setter.getKotlinMemberInfo().asPropertyInfo().valueParameterInfo; + KmValueParameter kmValueParameter = + synthesizer.toRewrittenKmValueParameter( + valueParameterInfo, valueType, valueSignature, "value", converter); + if (kmValueParameter != null) { + kmProperty.setSetterParameter(kmValueParameter); + } + DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory()); + kmProperty.setSetterFlags(setterInfo.setterFlags); + JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter)); + return appView.appInfo().isPinned(setter.method) ? null : renamedSetter.name.toString(); + } + + KmProperty toRenamedKmProperty(KotlinMetadataSynthesizer synthesizer) { + AppView<AppInfoWithLiveness> appView = synthesizer.appView; + NamingLens lens = synthesizer.lens; + KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf()); + + // Set default values + KmType defaultPropertyType = new KmType(flagsOf()); + kmProperty.setReturnType(defaultPropertyType); + + String renamedPropertyName = name; + if (getter != null) { + if (checkGetterCriteria() == GetterSetterCriteria.VIOLATE) { + return null; + } + if (!setReceiverParameterTypeForExtensionProperty( + kmProperty, getter, appView, synthesizer)) { + return null; + } + renamedPropertyName = + setGetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer); + } + if (setter != null) { + if (checkSetterCriteria() == GetterSetterCriteria.VIOLATE) { + return null; + } + if (!setReceiverParameterTypeForExtensionProperty( + kmProperty, setter, appView, synthesizer)) { + return null; + } + String renamedName = + setSetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer); + if (renamedPropertyName != null) { + renamedPropertyName = renamedName; + } + } + // Setting the property type from the field has to be done after the getter, otherwise we + // may potentially loose type-argument information. + if (field != null) { + String renamedName = + setFieldForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer); + if (renamedPropertyName != null) { + renamedPropertyName = renamedName; + } } // Rename the property name if and only if none of participating members is pinned, and // any of them is indeed renamed (to a new name). - if (canChangePropertyName && !renamedPropertyName.equals(name)) { + if (renamedPropertyName != null) { kmProperty.setName(renamedPropertyName); } return kmProperty;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java new file mode 100644 index 0000000..9382cd9 --- /dev/null +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
@@ -0,0 +1,174 @@ +// Copyright (c) 2020, 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.kotlin; + +import static com.android.tools.r8.kotlin.Kotlin.NAME; +import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier; +import static kotlinx.metadata.FlagsKt.flagsOf; + +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature; +import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; +import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; +import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter; +import com.android.tools.r8.graph.GenericSignature.TypeVariableSignature; +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; +import kotlinx.metadata.KmTypeParameter; +import kotlinx.metadata.KmTypeVisitor; +import kotlinx.metadata.KmVariance; + +class KotlinMetadataSynthesizerUtils { + + static void populateKmTypeFromSignature( + FieldTypeSignature typeSignature, + Supplier<KmTypeVisitor> typeVisitor, + List<KmTypeParameter> allTypeParameters, + DexItemFactory factory) { + if (typeSignature.isClassTypeSignature()) { + populateKmTypeFromClassTypeSignature( + typeSignature.asClassTypeSignature(), typeVisitor, allTypeParameters, factory); + } else if (typeSignature.isArrayTypeSignature()) { + populateKmTypeFromArrayTypeSignature( + typeSignature.asArrayTypeSignature(), typeVisitor, allTypeParameters, factory); + } else { + assert typeSignature.isTypeVariableSignature(); + populateKmTypeFromTypeVariableSignature( + typeSignature.asTypeVariableSignature(), typeVisitor, allTypeParameters); + } + } + + private static void populateKmTypeFromTypeVariableSignature( + TypeVariableSignature typeSignature, + Supplier<KmTypeVisitor> visitor, + List<KmTypeParameter> allTypeParameters) { + for (KmTypeParameter typeParameter : allTypeParameters) { + if (typeParameter + .getName() + .equals(typeSignature.asTypeVariableSignature().getTypeVariable())) { + visitor.get().visitTypeParameter(typeParameter.getId()); + return; + } + } + } + + private static void populateKmTypeFromArrayTypeSignature( + ArrayTypeSignature typeSignature, + Supplier<KmTypeVisitor> visitor, + List<KmTypeParameter> allTypeParameters, + DexItemFactory factory) { + ArrayTypeSignature arrayTypeSignature = typeSignature.asArrayTypeSignature(); + if (!arrayTypeSignature.elementSignature().isFieldTypeSignature()) { + return; + } + KmTypeVisitor kmType = visitor.get(); + kmType.visitClass(NAME + "/Array"); + populateKmTypeFromSignature( + arrayTypeSignature.elementSignature().asFieldTypeSignature(), + () -> kmType.visitArgument(flagsOf(), KmVariance.INVARIANT), + allTypeParameters, + factory); + } + + private static void populateKmTypeFromClassTypeSignature( + ClassTypeSignature typeSignature, + Supplier<KmTypeVisitor> visitor, + List<KmTypeParameter> allTypeParameters, + DexItemFactory factory) { + // No need to record the trivial argument. + if (factory.objectType == typeSignature.type()) { + return; + } + KmTypeVisitor kmType = visitor.get(); + kmType.visitClass(toClassifier(typeSignature.type(), factory)); + for (FieldTypeSignature typeArgument : typeSignature.typeArguments()) { + populateKmTypeFromSignature( + typeArgument, + () -> kmType.visitArgument(flagsOf(), KmVariance.INVARIANT), + allTypeParameters, + factory); + } + } + + static String toClassifier(DexType type, DexItemFactory factory) { + // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray + if (factory.kotlin.knownTypeConversion.containsKey(type)) { + DexType convertedType = factory.kotlin.knownTypeConversion.get(type); + assert convertedType != null; + return descriptorToKotlinClassifier(convertedType.toDescriptorString()); + } + // E.g., [Ljava/lang/String; -> kotlin/Array + if (type.isArrayType()) { + return NAME + "/Array"; + } + return descriptorToKotlinClassifier(type.toDescriptorString()); + } + + /** + * Utility method building up all type-parameters from {@code classTypeParameters} combined with + * {@code parameters}, where the consumer {@code addedFromParameters} is called for every + * conversion of {@Code FormalTypeParameter} to {@Code KmTypeParameter}. + * + * <pre> + * classTypeParameters: [KmTypeParameter(T), KmTypeParameter(S)] + * parameters: [FormalTypeParameter(R)] + * result: [KmTypeParameter(T), KmTypeParameter(S), KmTypeParameter(R)]. + * </pre> + * + * @param classTypeParameters + * @param parameters + * @param factory + * @param addedFromParameters + * @return + */ + static List<KmTypeParameter> convertFormalTypeParameters( + List<KmTypeParameter> classTypeParameters, + List<FormalTypeParameter> parameters, + DexItemFactory factory, + Consumer<KmTypeParameter> addedFromParameters) { + if (parameters.isEmpty()) { + return classTypeParameters; + } + ImmutableList.Builder<KmTypeParameter> builder = ImmutableList.builder(); + builder.addAll(classTypeParameters); + int idCounter = classTypeParameters.size(); + // Assign type-variables ids to names. All generic signatures has been minified at this point, + // but it may be that type-variables are used before we can see them (is that allowed?). + for (FormalTypeParameter parameter : parameters) { + KmTypeParameter element = + new KmTypeParameter(flagsOf(), parameter.getName(), idCounter++, KmVariance.INVARIANT); + builder.add(element); + addedFromParameters.accept(element); + } + idCounter = 0; + ImmutableList<KmTypeParameter> allTypeParameters = builder.build(); + for (FormalTypeParameter parameter : parameters) { + KmTypeParameter kmTypeParameter = + allTypeParameters.get(classTypeParameters.size() + idCounter++); + visitUpperBound(parameter.getClassBound(), allTypeParameters, kmTypeParameter, factory); + if (parameter.getInterfaceBounds() != null) { + for (FieldTypeSignature interfaceBound : parameter.getInterfaceBounds()) { + visitUpperBound(interfaceBound, allTypeParameters, kmTypeParameter, factory); + } + } + } + return allTypeParameters; + } + + private static void visitUpperBound( + FieldTypeSignature typeSignature, + List<KmTypeParameter> allTypeParameters, + KmTypeParameter parameter, + DexItemFactory factory) { + if (typeSignature.isUnknown()) { + return; + } + populateKmTypeFromSignature( + typeSignature, () -> parameter.visitUpperBound(flagsOf()), allTypeParameters, factory); + } +}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java new file mode 100644 index 0000000..130a660 --- /dev/null +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -0,0 +1,51 @@ +// Copyright (c) 2020, 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.kotlin; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import kotlinx.metadata.KmClassifier; +import kotlinx.metadata.KmType; +import kotlinx.metadata.KmTypeProjection; + +// Provides access to Kotlin information about a kotlin type. +public class KotlinTypeInfo { + + static final List<KotlinTypeProjectionInfo> EMPTY_ARGUMENTS = ImmutableList.of(); + + private final KmClassifier classifier; + private final List<KotlinTypeProjectionInfo> arguments; + + private KotlinTypeInfo(KmClassifier classifier, List<KotlinTypeProjectionInfo> arguments) { + this.classifier = classifier; + this.arguments = arguments; + } + + static KotlinTypeInfo create(KmType kmType) { + if (kmType == null) { + return null; + } + if (kmType.getArguments().isEmpty()) { + return new KotlinTypeInfo(kmType.classifier, EMPTY_ARGUMENTS); + } + ImmutableList.Builder<KotlinTypeProjectionInfo> arguments = new ImmutableList.Builder<>(); + for (KmTypeProjection argument : kmType.getArguments()) { + arguments.add(KotlinTypeProjectionInfo.create(argument)); + } + return new KotlinTypeInfo(kmType.classifier, arguments.build()); + } + + public boolean isTypeAlias() { + return classifier instanceof KmClassifier.TypeAlias; + } + + public KmClassifier.TypeAlias asTypeAlias() { + return (KmClassifier.TypeAlias) classifier; + } + + public List<KotlinTypeProjectionInfo> getArguments() { + return arguments; + } +}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java new file mode 100644 index 0000000..3782bd8 --- /dev/null +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -0,0 +1,25 @@ +// Copyright (c) 2020, 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.kotlin; + +import kotlinx.metadata.KmTypeProjection; +import kotlinx.metadata.KmVariance; + +// Provides access to Kotlin information about the type projection of a type (arguments). +public class KotlinTypeProjectionInfo { + + final KmVariance variance; + final KotlinTypeInfo typeInfo; + + private KotlinTypeProjectionInfo(KmVariance variance, KotlinTypeInfo typeInfo) { + this.variance = variance; + this.typeInfo = typeInfo; + } + + static KotlinTypeProjectionInfo create(KmTypeProjection kmTypeProjection) { + return new KotlinTypeProjectionInfo( + kmTypeProjection.getVariance(), KotlinTypeInfo.create(kmTypeProjection.getType())); + } +}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java index 3134b8a..e358c50 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -19,24 +19,36 @@ final int flag; // Indicates whether the formal parameter is originally `vararg`. final boolean isVararg; + // Original information about the type. + final KotlinTypeInfo type; + // TODO(b/151194869): Should we treat them as normal annotations? E.g., shrinking and renaming? // Annotations on the type of value parameter. final List<KmAnnotation> annotations; private KotlinValueParameterInfo( - String name, int flag, boolean isVararg, List<KmAnnotation> annotations) { + String name, + int flag, + boolean isVararg, + KotlinTypeInfo type, + List<KmAnnotation> annotations) { this.name = name; this.flag = flag; this.isVararg = isVararg; + this.type = type; this.annotations = annotations; } static KotlinValueParameterInfo fromKmValueParameter(KmValueParameter kmValueParameter) { + if (kmValueParameter == null) { + return null; + } KmType kmType = kmValueParameter.getType(); return new KotlinValueParameterInfo( kmValueParameter.getName(), kmValueParameter.getFlags(), kmValueParameter.getVarargElementType() != null, + KotlinTypeInfo.create(kmType), kmType != null ? JvmExtensionsKt.getAnnotations(kmType) : ImmutableList.of()); } }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java index b178579..5506792 100644 --- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -273,7 +273,7 @@ return; } assert definition.field != field; - assert definition.field.holder != field.holder; + assert definition.holder() != field.holder; // If the definition is renamed, if (renaming.containsKey(definition.field)) { // Assign the same, renamed name as the definition to the reference.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java index 9286684..6652d4a 100644 --- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java +++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -52,7 +52,7 @@ public DexString getOrCreateNameFor(DexField field) { DexEncodedField encodedField = appView.appInfo().resolveField(field); if (encodedField != null) { - DexClass clazz = appView.definitionFor(encodedField.field.holder); + DexClass clazz = appView.definitionFor(encodedField.holder()); if (clazz == null) { return field.name; }
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java index d5873a9..332477a 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -148,13 +148,13 @@ } Value in = instruction.value(); if (!in.isConstString()) { - warnUndeterminedIdentifierIfNecessary(field, method.method.holder, instruction, null); + warnUndeterminedIdentifierIfNecessary(field, method.holder(), instruction, null); return iterator; } DexString original = in.getConstInstruction().asConstString().getValue(); DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary(field, method.method.holder, instruction, original); + warnUndeterminedIdentifierIfNecessary(field, method.holder(), instruction, original); return iterator; } // Move the cursor back to $fieldPut @@ -213,7 +213,7 @@ if (isReflectionMethod(appView.dexItemFactory(), invokedMethod) || isClassNameComparison) { DexReference itemBasedString = identifyIdentifier(invoke, appView); if (itemBasedString == null) { - DexType context = method.method.holder; + DexType context = method.holder(); warnUndeterminedIdentifierIfNecessary(invokedMethod, context, invoke, null); return iterator; } @@ -280,14 +280,13 @@ for (int i = 0; i < ins.size(); i++) { Value in = ins.get(i); if (!in.isConstString()) { - warnUndeterminedIdentifierIfNecessary(invokedMethod, method.method.holder, invoke, null); + warnUndeterminedIdentifierIfNecessary(invokedMethod, method.holder(), invoke, null); continue; } DexString original = in.getConstInstruction().asConstString().getValue(); DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary( - invokedMethod, method.method.holder, invoke, original); + warnUndeterminedIdentifierIfNecessary(invokedMethod, method.holder(), invoke, original); continue; } // Move the cursor back to $invoke
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java index da38ed4..d8d62ae 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -5,6 +5,7 @@ import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptorIfValidJavaType; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexEncodedField; @@ -27,6 +28,7 @@ import com.android.tools.r8.ir.code.InvokeVirtual; import com.android.tools.r8.ir.code.NewArrayEmpty; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.List; @@ -282,13 +284,13 @@ } static DexReference inferMemberOrTypeFromNameString( - DexDefinitionSupplier definitions, DexString dexString) { + AppView<AppInfoWithLiveness> appView, DexString dexString) { // "fully.qualified.ClassName.fieldOrMethodName" // "fully.qualified.ClassName#fieldOrMethodName" - DexReference itemBasedString = inferMemberFromNameString(definitions, dexString); + DexReference itemBasedString = inferMemberFromNameString(appView, dexString); if (itemBasedString == null) { // "fully.qualified.ClassName" - return inferTypeFromNameString(definitions, dexString); + return inferTypeFromNameString(appView, dexString); } return itemBasedString; } @@ -320,7 +322,7 @@ } private static DexReference inferMemberFromNameString( - DexDefinitionSupplier definitions, DexString dexString) { + AppView<AppInfoWithLiveness> appView, DexString dexString) { String identifier = dexString.toString(); String typeIdentifier = null; String memberIdentifier = null; @@ -348,8 +350,9 @@ if (maybeDescriptor == null) { return null; } - DexType type = definitions.dexItemFactory().createType(maybeDescriptor); - DexClass holder = definitions.definitionFor(type); + DexType type = appView.dexItemFactory().createType(maybeDescriptor); + // TODO(b/150736225): Should we move the identification of identifiers into the initial tracing? + DexClass holder = appView.appInfo().definitionForWithoutExistenceAssert(type); if (holder == null) { return null; }
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java index 777d006..6c78e0b 100644 --- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java +++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -9,15 +9,13 @@ import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItem; -import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.InnerClassAttribute; -import com.android.tools.r8.ir.desugar.PrefixRewritingMapper; import com.android.tools.r8.utils.InternalOptions; -import com.google.common.collect.ImmutableMap; -import java.util.IdentityHashMap; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -28,9 +26,9 @@ // Naming lens for rewriting type prefixes. public class PrefixRewritingNamingLens extends NamingLens { - final Map<DexType, DexString> classRenaming = new IdentityHashMap<>(); final NamingLens namingLens; final InternalOptions options; + final AppView<?> appView; public static NamingLens createPrefixRewritingNamingLens(AppView<?> appView) { return createPrefixRewritingNamingLens(appView, NamingLens.getIdentityLens()); @@ -45,18 +43,21 @@ } public PrefixRewritingNamingLens(NamingLens namingLens, AppView<?> appView) { + this.appView = appView; this.namingLens = namingLens; this.options = appView.options(); - DexItemFactory itemFactory = options.itemFactory; - PrefixRewritingMapper rewritePrefix = appView.rewritePrefix; - itemFactory.forAllTypes( - type -> { - if (rewritePrefix.hasRewrittenType(type, appView)) { - classRenaming.put(type, rewritePrefix.rewrittenType(type, appView).descriptor); - } - }); - // Verify that no type would have been renamed by both lenses. - assert namingLens.verifyNoOverlap(classRenaming); + } + + private boolean isRenamed(DexType type) { + return getRenaming(type) != null; + } + + private DexString getRenaming(DexType type) { + DexString descriptor = null; + if (appView.rewritePrefix.hasRewrittenType(type, appView)) { + descriptor = appView.rewritePrefix.rewrittenType(type, appView).descriptor; + } + return descriptor; } @Override @@ -66,17 +67,18 @@ @Override public DexString prefixRewrittenType(DexType type) { - return classRenaming.get(type); + return getRenaming(type); } @Override public DexString lookupDescriptor(DexType type) { - return classRenaming.getOrDefault(type, namingLens.lookupDescriptor(type)); + DexString renaming = getRenaming(type); + return renaming != null ? renaming : namingLens.lookupDescriptor(type); } @Override public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) { - if (classRenaming.containsKey(attribute.getInner())) { + if (isRenamed(attribute.getInner())) { // Prefix rewriting does not influence the inner name. return attribute.getInnerName(); } @@ -85,7 +87,7 @@ @Override public DexString lookupName(DexMethod method) { - if (classRenaming.containsKey(method.holder)) { + if (isRenamed(method.holder)) { // Prefix rewriting does not influence the method name. return method.name; } @@ -94,7 +96,7 @@ @Override public DexString lookupMethodName(DexCallSite callSite) { - if (classRenaming.containsKey(callSite.bootstrapMethod.rewrittenTarget.holder)) { + if (isRenamed(callSite.bootstrapMethod.rewrittenTarget.holder)) { // Prefix rewriting does not influence the inner name. return callSite.methodName; } @@ -103,7 +105,7 @@ @Override public DexString lookupName(DexField field) { - if (classRenaming.containsKey(field.holder)) { + if (isRenamed(field.holder)) { // Prefix rewriting does not influence the field name. return field.name; } @@ -127,9 +129,10 @@ } private boolean verifyNotPrefixRewrittenPackage(String packageName) { - for (DexType dexType : classRenaming.keySet()) { - assert !dexType.getPackageDescriptor().equals(packageName); - } + appView.rewritePrefix.forAllRewrittenTypes( + dexType -> { + assert !dexType.getPackageDescriptor().equals(packageName); + }); return true; } @@ -140,7 +143,7 @@ // If compiling the desugared library, the mapping needs to be printed. // When debugging the program, both mapping files need to be merged. if (options.isDesugaredLibraryCompilation()) { - classRenaming.keySet().forEach(consumer); + appView.rewritePrefix.forAllRewrittenTypes(consumer); } namingLens.forAllRenamedTypes(consumer); } @@ -150,13 +153,16 @@ Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) { Map<String, T> renamedItemsPrefixRewritting; if (clazz == DexType.class) { - renamedItemsPrefixRewritting = - classRenaming.keySet().stream() - .filter(item -> predicate.test(clazz.cast(item))) - .map(clazz::cast) - .collect(ImmutableMap.toImmutableMap(namer, i -> i)); + renamedItemsPrefixRewritting = new HashMap<>(); + appView.rewritePrefix.forAllRewrittenTypes( + item -> { + T cast = clazz.cast(item); + if (predicate.test(cast)) { + renamedItemsPrefixRewritting.put(namer.apply(cast), cast); + } + }); } else { - renamedItemsPrefixRewritting = ImmutableMap.of(); + renamedItemsPrefixRewritting = Collections.emptyMap(); } Map<String, T> renamedItemsMinifier = namingLens.getRenamedItems(clazz, predicate, namer); // The Collector throws an exception for duplicated keys.
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java index 9f32739..da62097 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -340,7 +340,8 @@ // have no effect. continue; } - DexClass dexClass = appView.definitionFor(type); + // TODO(b/150736225): Is this sound? What if the type is a library type that has been pruned? + DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type); if (dexClass == null) { computeDefaultInterfaceMethodMappingsForType( type, @@ -411,7 +412,7 @@ // TODO(b/136694827): Check for already used and report an error. It seems like this can be // done already in the reservation step for classes since there is only one 'path', unlike // members that can be reserved differently in the hierarchy. - DexClass clazz = appView.definitionFor(type); + DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(type); if (clazz == null) { return type.descriptor; }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index 4d20f6b..42e03e0 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -142,7 +142,7 @@ // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow // searching in library for methods, but this should be done on classpath instead. if (target != null && target.method != method) { - DexClass targetClass = appView.definitionFor(target.method.holder); + DexClass targetClass = appView.definitionFor(target.holder()); if (originalClass.isProgramClass()) { // In Java bytecode, it is only possible to target interface methods that are in one of // the immediate super-interfaces via a super-invocation (see IndirectSuperInterfaceTest). @@ -159,14 +159,14 @@ // visibility problems when rebinding. final DexEncodedMethod finalTarget = target; Set<DexEncodedMethod> contexts = methodsWithContexts.get(method); - if (contexts.stream().anyMatch(context -> - mayNeedBridgeForVisibility(context.method.holder, finalTarget))) { + if (contexts.stream() + .anyMatch(context -> mayNeedBridgeForVisibility(context.holder(), finalTarget))) { target = insertBridgeForVisibilityIfNeeded( method, target, originalClass, targetClass, lookupTarget); } } - builder.map(method, lense.lookupMethod(validTargetFor(target.method, method))); + builder.map(method, lense.lookupMethod(validTargetFor(target.method, method)), invokeType); } } } @@ -217,7 +217,7 @@ } private boolean mayNeedBridgeForVisibility(DexType context, DexEncodedMethod method) { - DexType holderType = method.method.holder; + DexType holderType = method.holder(); DexClass holder = appView.definitionFor(holderType); if (holder == null) { return false; @@ -315,7 +315,7 @@ .allMatch( context -> isMemberVisibleFromOriginalContext( - appView, context.method.holder, target.field.holder, target.accessFlags))) { + appView, context.holder(), target.holder(), target.accessFlags))) { builder.map( field, lense.lookupField(validTargetFor(target.field, field, DexClass::lookupField))); }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java index a845de2..95e2772 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -9,45 +9,72 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.GraphLense.NestedGraphLense; +import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.code.Invoke.Type; import com.google.common.collect.ImmutableMap; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Map; +import java.util.Set; public class MemberRebindingLense extends NestedGraphLense { - public static class Builder extends NestedGraphLense.Builder { + public static class Builder { private final AppView<?> appView; + private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>(); + private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>(); + protected Builder(AppView<?> appView) { this.appView = appView; } + public void map(DexField from, DexField to) { + if (from == to) { + assert !fieldMap.containsKey(from); + return; + } + fieldMap.put(from, to); + } + + public void map(DexMethod from, DexMethod to, Invoke.Type type) { + if (from == to) { + assert !methodMaps.containsKey(type) || methodMaps.get(type).getOrDefault(from, to) == to; + return; + } + Map<DexMethod, DexMethod> methodMap = + methodMaps.computeIfAbsent(type, ignore -> new IdentityHashMap<>()); + assert methodMap.getOrDefault(from, to) == to; + methodMap.put(from, to); + } + public GraphLense build(GraphLense previousLense) { - assert typeMap.isEmpty(); - if (methodMap.isEmpty() && fieldMap.isEmpty()) { + if (fieldMap.isEmpty() && methodMaps.isEmpty()) { return previousLense; } - return new MemberRebindingLense(appView, methodMap, fieldMap, previousLense); + return new MemberRebindingLense(appView, methodMaps, fieldMap, previousLense); } } private final AppView<?> appView; + private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps; public MemberRebindingLense( AppView<?> appView, - Map<DexMethod, DexMethod> methodMap, + Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps, Map<DexField, DexField> fieldMap, GraphLense previousLense) { super( ImmutableMap.of(), - methodMap, + ImmutableMap.of(), fieldMap, null, null, previousLense, appView.dexItemFactory()); this.appView = appView; + this.methodMaps = methodMaps; } public static Builder builder(AppView<?> appView) { @@ -55,6 +82,28 @@ } @Override + public boolean isLegitimateToHaveEmptyMappings() { + return true; + } + + @Override + public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) { + GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type); + Map<DexMethod, DexMethod> methodMap = methodMaps.getOrDefault(type, Collections.emptyMap()); + DexMethod newMethod = methodMap.get(previous.getMethod()); + if (newMethod != null) { + return new GraphLenseLookupResult( + newMethod, mapInvocationType(newMethod, method, previous.getType())); + } + return previous; + } + + @Override + public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) { + return previousLense.lookupMethodInAllContexts(method); + } + + @Override protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) { return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type); }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java index 6bf54dd..4bb1b2b 100644 --- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java +++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -19,7 +19,6 @@ import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.InnerClassAttribute; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.IdentityHashMap; import java.util.Map; @@ -31,20 +30,18 @@ private final Set<DexAnnotation> annotationsToRetain; private final Set<DexType> classesToRetainInnerClassAttributeFor; private final ProguardKeepAttributes keep; - - public AnnotationRemover( - AppView<AppInfoWithLiveness> appView, Set<DexType> classesToRetainInnerClassAttributeFor) { - this(appView, classesToRetainInnerClassAttributeFor, ImmutableSet.of()); - } + private final Set<DexType> removedClasses; private AnnotationRemover( AppView<AppInfoWithLiveness> appView, Set<DexType> classesToRetainInnerClassAttributeFor, - Set<DexAnnotation> annotationsToRetain) { + Set<DexAnnotation> annotationsToRetain, + Set<DexType> removedClasses) { this.appView = appView; this.annotationsToRetain = annotationsToRetain; this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor; this.keep = appView.options().getProguardConfiguration().getKeepAttributes(); + this.removedClasses = removedClasses; } public static Builder builder() { @@ -230,12 +227,15 @@ private DexEncodedAnnotation rewriteEncodedAnnotation(DexEncodedAnnotation original) { GraphLense graphLense = appView.graphLense(); DexType annotationType = original.type.toBaseType(appView.dexItemFactory()); + if (removedClasses.contains(annotationType)) { + return null; + } DexType rewrittenType = graphLense.lookupType(annotationType); DexEncodedAnnotation rewrite = original.rewrite( graphLense::lookupType, element -> rewriteAnnotationElement(rewrittenType, element)); assert rewrite != null; - DexClass annotationClass = appView.definitionFor(rewrittenType); + DexClass annotationClass = appView.appInfo().definitionFor(rewrittenType); assert annotationClass == null || appView.appInfo().isNonProgramTypeOrLiveProgramType(rewrittenType); return rewrite; @@ -378,8 +378,7 @@ } for (DexProgramClass clazz : appView.appInfo().classes()) { // If [clazz] is mentioned by a keep rule, it could be used for reflection, and we - // therefore - // need to keep the enclosing method and inner classes attributes, if requested. + // therefore need to keep the enclosing method and inner classes attributes, if requested. if (appView.appInfo().isPinned(clazz.type)) { for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) { DexType inner = innerClassAttribute.getInner(); @@ -413,10 +412,11 @@ annotationsToRetain.add(annotation); } - public AnnotationRemover build(AppView<AppInfoWithLiveness> appView) { + public AnnotationRemover build( + AppView<AppInfoWithLiveness> appView, Set<DexType> removedClasses) { assert classesToRetainInnerClassAttributeFor != null; return new AnnotationRemover( - appView, classesToRetainInnerClassAttributeFor, annotationsToRetain); + appView, classesToRetainInnerClassAttributeFor, annotationsToRetain, removedClasses); } } }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index fdd1274..78136a9 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -35,15 +35,16 @@ import com.android.tools.r8.graph.ObjectAllocationInfoCollection; import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl; import com.android.tools.r8.graph.PresortedComparable; -import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.code.Invoke.Type; +import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter; +import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter; import com.android.tools.r8.ir.desugar.LambdaDescriptor; +import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter; import com.android.tools.r8.utils.CollectionUtils; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.PredicateSet; -import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.Visibility; import com.android.tools.r8.utils.WorkList; import com.google.common.collect.ImmutableSet; @@ -56,7 +57,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Deque; -import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -70,7 +70,8 @@ /** Encapsulates liveness and reachability information for an application. */ public class AppInfoWithLiveness extends AppInfoWithSubtyping implements InstantiatedSubTypeInfo { - + /** Set of reachable proto types that will be dead code eliminated. */ + private final Set<DexType> deadProtoTypes; /** Set of types that are mentioned in the program, but for which no definition exists. */ private final Set<DexType> missingTypes; /** @@ -83,9 +84,6 @@ * ServiceLoader.load() or ServiceLoader.loadInstalled(). */ public final Set<DexType> instantiatedAppServices; - /** Cache for {@link #isInstantiatedDirectlyOrIndirectly(DexProgramClass)}. */ - private final IdentityHashMap<DexType, Boolean> indirectlyInstantiatedTypes = - new IdentityHashMap<>(); /** * Set of methods that are the immediate target of an invoke. They might not actually be live but * are required so that invokes can find the method. If such a method is not live (i.e. not @@ -188,14 +186,13 @@ /** A map from enum types to their value types and ordinals. */ final EnumValueInfoMapCollection enumValueInfoMaps; - final Set<DexType> instantiatedLambdas; - /* A cache to improve the lookup performance of lookupSingleVirtualTarget */ private final SingleTargetLookupCache singleTargetLookupCache = new SingleTargetLookupCache(); // TODO(zerny): Clean up the constructors so we have just one. AppInfoWithLiveness( DirectMappedDexApplication application, + Set<DexType> deadProtoTypes, Set<DexType> missingTypes, Set<DexType> liveTypes, Set<DexType> instantiatedAppServices, @@ -233,10 +230,10 @@ Set<DexType> prunedTypes, Map<DexField, Int2ReferenceMap<DexField>> switchMaps, EnumValueInfoMapCollection enumValueInfoMaps, - Set<DexType> instantiatedLambdas, Set<DexType> constClassReferences, Map<DexType, Visibility> initClassReferences) { super(application); + this.deadProtoTypes = deadProtoTypes; this.missingTypes = missingTypes; this.liveTypes = liveTypes; this.instantiatedAppServices = instantiatedAppServices; @@ -274,13 +271,13 @@ this.prunedTypes = prunedTypes; this.switchMaps = switchMaps; this.enumValueInfoMaps = enumValueInfoMaps; - this.instantiatedLambdas = instantiatedLambdas; this.constClassReferences = constClassReferences; this.initClassReferences = initClassReferences; } public AppInfoWithLiveness( AppInfoWithSubtyping appInfoWithSubtyping, + Set<DexType> deadProtoTypes, Set<DexType> missingTypes, Set<DexType> liveTypes, Set<DexType> instantiatedAppServices, @@ -318,10 +315,10 @@ Set<DexType> prunedTypes, Map<DexField, Int2ReferenceMap<DexField>> switchMaps, EnumValueInfoMapCollection enumValueInfoMaps, - Set<DexType> instantiatedLambdas, Set<DexType> constClassReferences, Map<DexType, Visibility> initClassReferences) { super(appInfoWithSubtyping); + this.deadProtoTypes = deadProtoTypes; this.missingTypes = missingTypes; this.liveTypes = liveTypes; this.instantiatedAppServices = instantiatedAppServices; @@ -359,7 +356,6 @@ this.prunedTypes = prunedTypes; this.switchMaps = switchMaps; this.enumValueInfoMaps = enumValueInfoMaps; - this.instantiatedLambdas = instantiatedLambdas; this.constClassReferences = constClassReferences; this.initClassReferences = initClassReferences; } @@ -367,6 +363,7 @@ private AppInfoWithLiveness(AppInfoWithLiveness previous) { this( previous, + previous.deadProtoTypes, previous.missingTypes, previous.liveTypes, previous.instantiatedAppServices, @@ -404,7 +401,6 @@ previous.prunedTypes, previous.switchMaps, previous.enumValueInfoMaps, - previous.instantiatedLambdas, previous.constClassReferences, previous.initClassReferences); copyMetadataFromPrevious(previous); @@ -417,6 +413,7 @@ Collection<DexReference> additionalPinnedItems) { this( application, + previous.deadProtoTypes, previous.missingTypes, previous.liveTypes, previous.instantiatedAppServices, @@ -458,7 +455,6 @@ : CollectionUtils.mergeSets(previous.prunedTypes, removedClasses), previous.switchMaps, previous.enumValueInfoMaps, - previous.instantiatedLambdas, previous.constClassReferences, previous.initClassReferences); copyMetadataFromPrevious(previous); @@ -470,10 +466,10 @@ Map<DexField, Int2ReferenceMap<DexField>> switchMaps, EnumValueInfoMapCollection enumValueInfoMaps) { super(previous); + this.deadProtoTypes = previous.deadProtoTypes; this.missingTypes = previous.missingTypes; this.liveTypes = previous.liveTypes; this.instantiatedAppServices = previous.instantiatedAppServices; - this.instantiatedLambdas = previous.instantiatedLambdas; this.targetedMethods = previous.targetedMethods; this.failedResolutionTargets = previous.failedResolutionTargets; this.bootstrapMethods = previous.bootstrapMethods; @@ -513,21 +509,39 @@ previous.markObsolete(); } - // TODO(b/150736225): Don't disable this assert. - private boolean dontAssertDefinitionFor = true; - public static AppInfoWithLivenessModifier modifier() { return new AppInfoWithLivenessModifier(); } + private boolean assertDefinitionFor = true; + + public void disableDefinitionForAssert() { + assertDefinitionFor = false; + } + + public void enableDefinitionForAssert() { + assertDefinitionFor = true; + } + @Override public DexClass definitionFor(DexType type) { DexClass definition = super.definitionFor(type); - assert dontAssertDefinitionFor - || definition != null - || missingTypes.contains(type) - // TODO(b/149363884): Remove this exception once fixed. - || type.toDescriptorString().endsWith("$Builder;") + assert !assertDefinitionFor + || definition != null + || deadProtoTypes.contains(type) + || missingTypes.contains(type) + // TODO(b/150693139): Remove these exceptions once fixed. + || InterfaceMethodRewriter.isCompanionClassType(type) + || InterfaceMethodRewriter.hasDispatchClassSuffix(type) + || InterfaceMethodRewriter.isEmulatedLibraryClassType(type) + || type.toDescriptorString().startsWith("L$r8$backportedMethods$") + || type.toDescriptorString().startsWith("Lj$/$r8$backportedMethods$") + || type.toDescriptorString().startsWith("Lj$/$r8$retargetLibraryMember$") + || TwrCloseResourceRewriter.isUtilityClassDescriptor(type) + // TODO(b/150736225): Not sure how to remove these. + || DesugaredLibraryAPIConverter.isVivifiedType(type) + // TODO(b/149363884): Handle references to dead proto builders. + || type.toDescriptorString().endsWith("$Builder;") : "Failed lookup of non-missing type: " + type; return definition; } @@ -561,7 +575,7 @@ } for (DexCallSite callSite : callSites) { for (DexEncodedMethod method : lookupLambdaImplementedMethods(callSite)) { - worklist.add(method.method.holder); + worklist.add(method.holder()); } } while (!worklist.isEmpty()) { @@ -735,8 +749,9 @@ return objectAllocationInfoCollection; } - ObjectAllocationInfoCollectionImpl getMutableObjectAllocationInfoCollection() { - return objectAllocationInfoCollection; + void mutateObjectAllocationInfoCollection( + Consumer<ObjectAllocationInfoCollectionImpl.Builder> mutator) { + objectAllocationInfoCollection.mutate(mutator, this); } void removeFromSingleTargetLookupCache(DexClass clazz) { @@ -764,30 +779,14 @@ assert checkIfObsolete(); DexType type = clazz.type; return type.isD8R8SynthesizedClassType() - || objectAllocationInfoCollection.isInstantiatedDirectly(clazz) + || (!clazz.isInterface() && objectAllocationInfoCollection.isInstantiatedDirectly(clazz)) + // TODO(b/145344105): Model annotations in the object allocation info. || (clazz.isAnnotation() && liveTypes.contains(type)); } public boolean isInstantiatedIndirectly(DexProgramClass clazz) { assert checkIfObsolete(); - if (hasAnyInstantiatedLambdas(clazz)) { - return true; - } - DexType type = clazz.type; - synchronized (indirectlyInstantiatedTypes) { - if (indirectlyInstantiatedTypes.containsKey(type)) { - return indirectlyInstantiatedTypes.get(type).booleanValue(); - } - for (DexType directSubtype : allImmediateSubtypes(type)) { - DexProgramClass directSubClass = asProgramClassOrNull(definitionFor(directSubtype)); - if (directSubClass == null || isInstantiatedDirectlyOrIndirectly(directSubClass)) { - indirectlyInstantiatedTypes.put(type, Boolean.TRUE); - return true; - } - } - indirectlyInstantiatedTypes.put(type, Boolean.FALSE); - return false; - } + return objectAllocationInfoCollection.hasInstantiatedStrictSubtype(clazz); } public boolean isInstantiatedDirectlyOrIndirectly(DexProgramClass clazz) { @@ -855,16 +854,16 @@ if (fieldAccessInfo == null || !fieldAccessInfo.isWritten()) { return false; } - DexType holder = field.field.holder; + DexType holder = field.holder(); return fieldAccessInfo.isWrittenOnlyInMethodSatisfying( - method -> method.isInstanceInitializer() && method.method.holder == holder); + method -> method.isInstanceInitializer() && method.holder() == holder); } public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) { assert checkIfObsolete(); assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written"; DexEncodedMethod staticInitializer = - definitionFor(field.field.holder).asProgramClass().getClassInitializer(); + definitionFor(field.holder()).asProgramClass().getClassInitializer(); return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer); } @@ -876,7 +875,7 @@ } private boolean isLibraryOrClasspathField(DexEncodedField field) { - DexClass holder = definitionFor(field.field.holder); + DexClass holder = definitionFor(field.holder()); return holder == null || holder.isLibraryClass() || holder.isClasspathClass(); } @@ -905,9 +904,9 @@ } @Override - public boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) { + public boolean isInstantiatedInterface(DexProgramClass clazz) { assert checkIfObsolete(); - return instantiatedLambdas.contains(clazz.type); + return objectAllocationInfoCollection.isInterfaceWithUnknownSubtypeHierarchy(clazz); } @Override @@ -940,53 +939,6 @@ return false; } - private boolean canVirtualMethodBeImplementedInExtraSubclass( - DexProgramClass clazz, DexMethod method) { - // For functional interfaces that are instantiated by lambdas, we may not have synthesized all - // the lambda classes yet, and therefore the set of subtypes for the holder may still be - // incomplete. - if (hasAnyInstantiatedLambdas(clazz)) { - return true; - } - // If `clazz` is kept and `method` is a library method or a library method override, then it is - // possible to create a class that inherits from `clazz` and overrides the library method. - // Similarly, if `clazz` is kept and `method` is kept directly on `clazz` or indirectly on one - // of its supertypes, then it is possible to create a class that inherits from `clazz` and - // overrides the kept method. - if (isPinned(clazz.type)) { - ResolutionResult resolutionResult = resolveMethod(clazz, method); - if (resolutionResult.isSingleResolution()) { - DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - return !resolutionTarget.isProgramMethod(this) - || resolutionTarget.isLibraryMethodOverride().isPossiblyTrue() - || isVirtualMethodPinnedDirectlyOrInAncestor(clazz, method); - } - } - return false; - } - - private boolean isVirtualMethodPinnedDirectlyOrInAncestor( - DexProgramClass currentClass, DexMethod method) { - // Look in all ancestor types, including `currentClass` itself. - Set<DexProgramClass> visited = SetUtils.newIdentityHashSet(currentClass); - Deque<DexProgramClass> worklist = new ArrayDeque<>(visited); - while (!worklist.isEmpty()) { - DexClass clazz = worklist.removeFirst(); - assert visited.contains(clazz); - DexEncodedMethod methodInClass = clazz.lookupVirtualMethod(method); - if (methodInClass != null && isPinned(methodInClass.method)) { - return true; - } - for (DexType superType : clazz.allImmediateSupertypes()) { - DexProgramClass superClass = asProgramClassOrNull(definitionFor(superType)); - if (superClass != null && visited.add(superClass)) { - worklist.addLast(superClass); - } - } - } - return false; - } - public Set<DexReference> getPinnedItems() { assert checkIfObsolete(); return pinnedItems; @@ -1001,6 +953,10 @@ Collection<DexType> removedClasses, Collection<DexReference> additionalPinnedItems) { assert checkIfObsolete(); + if (!removedClasses.isEmpty()) { + // Rebuild the hierarchy. + objectAllocationInfoCollection.mutate(mutator -> {}, this); + } return new AppInfoWithLiveness(this, application, removedClasses, additionalPinnedItems); } @@ -1037,6 +993,7 @@ return new AppInfoWithLiveness( application, + deadProtoTypes, missingTypes, rewriteItems(liveTypes, lens::lookupType), rewriteItems(instantiatedAppServices, lens::lookupType), @@ -1079,7 +1036,6 @@ prunedTypes, rewriteReferenceKeys(switchMaps, lens::lookupField), enumValueInfoMaps.rewrittenWithLens(lens), - rewriteItems(instantiatedLambdas, lens::lookupType), rewriteItems(constClassReferences, lens::lookupType), rewriteReferenceKeys(initClassReferences, lens::lookupType)); } @@ -1208,7 +1164,7 @@ // TODO(b/148769279): Disable lookup single target on lambda's for now. if (resolvedHolder.isInterface() && resolvedHolder.isProgramClass() - && hasAnyInstantiatedLambdas(resolvedHolder.asProgramClass())) { + && isInstantiatedInterface(resolvedHolder.asProgramClass())) { singleTargetLookupCache.addToCache(refinedReceiverType, method, null); return null; } @@ -1387,9 +1343,7 @@ } private boolean isInstantiatedOrPinned(DexProgramClass clazz) { - return isInstantiatedDirectly(clazz) - || isPinned(clazz.type) - || hasAnyInstantiatedLambdas(clazz); + return isInstantiatedDirectly(clazz) || isPinned(clazz.type) || isInstantiatedInterface(clazz); } public boolean isPinnedNotProgramOrLibraryOverride(DexReference reference) { @@ -1406,7 +1360,7 @@ DexClass clazz = definitionFor(reference.asDexType()); return clazz == null || clazz.isNotProgramClass() - || hasAnyInstantiatedLambdas(clazz.asProgramClass()); + || isInstantiatedInterface(clazz.asProgramClass()); } } }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java index 99f2d84..0f1b348 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl; import com.android.tools.r8.graph.FieldAccessInfoImpl; -import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl; import com.google.common.collect.Sets; import java.util.Set; @@ -34,12 +33,13 @@ public void modify(AppInfoWithLiveness appInfo) { // Instantiated classes. - ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection = - appInfo.getMutableObjectAllocationInfoCollection(); - noLongerInstantiatedClasses.forEach( - clazz -> { - objectAllocationInfoCollection.markNoLongerInstantiated(clazz); - appInfo.removeFromSingleTargetLookupCache(clazz); + appInfo.mutateObjectAllocationInfoCollection( + mutator -> { + noLongerInstantiatedClasses.forEach( + clazz -> { + mutator.markNoLongerInstantiated(clazz); + appInfo.removeFromSingleTargetLookupCache(clazz); + }); }); // Written fields. FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java index db955c9..c04ddf5 100644 --- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java +++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -18,7 +18,7 @@ public class DefaultEnqueuerUseRegistry extends UseRegistry { private final ProgramMethod context; - private final Enqueuer enqueuer; + protected final Enqueuer enqueuer; public DefaultEnqueuerUseRegistry( AppView<?> appView, DexProgramClass holder, DexEncodedMethod method, Enqueuer enqueuer) {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index 20dde1b..f636d05 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -29,6 +29,7 @@ import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexAnnotationSet; +import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndMethod; @@ -220,6 +221,9 @@ */ private final Set<DexClass> liveNonProgramTypes = Sets.newIdentityHashSet(); + /** Set of reachable proto types that will be dead code eliminated. */ + private final Set<DexProgramClass> deadProtoTypeCandidates = Sets.newIdentityHashSet(); + /** Set of missing types. */ private final Set<DexType> missingTypes = Sets.newIdentityHashSet(); @@ -411,26 +415,121 @@ this.annotationRemoverBuilder = annotationRemoverBuilder; } + public void addDeadProtoTypeCandidate(DexType type) { + assert type.isProgramType(appView); + addDeadProtoTypeCandidate(appView.definitionFor(type).asProgramClass()); + } + + public void addDeadProtoTypeCandidate(DexProgramClass clazz) { + deadProtoTypeCandidates.add(clazz); + } + private boolean isProgramClass(DexType type) { return getProgramClassOrNull(type) != null; } + private void recordReference(DexReference r) { + if (r.isDexType()) { + recordTypeReference(r.asDexType()); + } else if (r.isDexField()) { + recordFieldReference(r.asDexField()); + } else { + assert r.isDexMethod(); + recordMethodReference(r.asDexMethod()); + } + } + + private void recordTypeReference(DexType type) { + if (type == null) { + return; + } + if (type.isArrayType()) { + type = type.toBaseType(appView.dexItemFactory()); + } + if (!type.isClassType()) { + return; + } + // Lookup the definition, ignoring the result. This populates the missing and referenced sets. + definitionFor(type); + } + + private void recordMethodReference(DexMethod method) { + recordTypeReference(method.holder); + recordTypeReference(method.proto.returnType); + for (DexType type : method.proto.parameters.values) { + recordTypeReference(type); + } + } + + private void recordFieldReference(DexField field) { + recordTypeReference(field.holder); + recordTypeReference(field.type); + } + private DexClass definitionFor(DexType type) { DexClass clazz = appView.definitionFor(type); if (clazz == null) { reportMissingClass(type); return null; } - if (clazz.isProgramClass()) { - return clazz; + if (clazz.isNotProgramClass()) { + addLiveNonProgramType(clazz); } - if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) { + return clazz; + } + + private void addLiveNonProgramType(DexClass clazz) { + assert clazz.isNotProgramClass(); + // Fast path to avoid the worklist when the class is already seen. + if (!liveNonProgramTypes.add(clazz)) { + return; + } + Deque<DexClass> worklist = new ArrayDeque<>(); + worklist.addLast(clazz); + while (!worklist.isEmpty()) { + DexClass definition = worklist.removeFirst(); + processNewLiveNonProgramType(definition, worklist); + } + } + + private void processNewLiveNonProgramType(DexClass clazz, Deque<DexClass> worklist) { + assert clazz.isNotProgramClass(); + if (clazz.isLibraryClass()) { // TODO(b/149201735): This likely needs to apply to classpath too. ensureMethodsContinueToWidenAccess(clazz); // Only libraries must not derive program. Classpath classes can, assuming correct keep rules. warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass()); } - return clazz; + for (DexEncodedField field : clazz.fields()) { + addNonProgramClassToWorklist(field.field.type, worklist); + } + for (DexEncodedMethod method : clazz.methods()) { + addNonProgramClassToWorklist(method.method.proto.returnType, worklist); + for (DexType param : method.method.proto.parameters.values) { + addNonProgramClassToWorklist(param, worklist); + } + } + for (DexType supertype : clazz.allImmediateSupertypes()) { + addNonProgramClassToWorklist(supertype, worklist); + } + } + + private void addNonProgramClassToWorklist(DexType type, Deque<DexClass> worklist) { + if (type.isArrayType()) { + type = type.toBaseType(appView.dexItemFactory()); + } + if (!type.isClassType()) { + return; + } + DexClass definition = appView.definitionFor(type); + if (definition == null) { + reportMissingClass(type); + return; + } + if (definition.isProgramClass() || !liveNonProgramTypes.add(definition)) { + return; + } + worklist.addLast(definition); } private DexProgramClass getProgramClassOrNull(DexType type) { @@ -495,7 +594,7 @@ } } else if (item.isDexEncodedField()) { DexEncodedField dexEncodedField = item.asDexEncodedField(); - DexProgramClass holder = getProgramClassOrNull(dexEncodedField.field.holder); + DexProgramClass holder = getProgramClassOrNull(dexEncodedField.holder()); if (holder != null) { workList.enqueueMarkFieldKeptAction( holder, @@ -504,7 +603,7 @@ } } else if (item.isDexEncodedMethod()) { DexEncodedMethod encodedMethod = item.asDexEncodedMethod(); - DexProgramClass holder = getProgramClassOrNull(encodedMethod.method.holder); + DexProgramClass holder = getProgramClassOrNull(encodedMethod.holder()); if (holder != null) { workList.enqueueMarkMethodKeptAction( holder, @@ -537,7 +636,7 @@ // Utility to avoid adding to the worklist if already live. private boolean enqueueMarkMethodLiveAction( DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.method.holder == clazz.type; + assert method.holder() == clazz.type; if (liveMethods.add(clazz, method, reason)) { workList.enqueueMarkMethodLiveAction(clazz, method, reason); return true; @@ -650,14 +749,12 @@ } DexEncodedMethod contextMethod = context.getMethod(); - markLambdaAsInstantiated(descriptor, contextMethod); - transitionMethodsForInstantiatedLambda(descriptor); if (lambdaRewriter != null) { assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas"; CfCode code = contextMethod.getCode().asCfCode(); if (code != null) { LambdaClass lambdaClass = - lambdaRewriter.getOrCreateLambdaClass(descriptor, contextMethod.method.holder); + lambdaRewriter.getOrCreateLambdaClass(descriptor, contextMethod.holder()); lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, contextMethod)); lambdaCallSites .computeIfAbsent(contextMethod, k -> new IdentityHashMap<>()) @@ -670,6 +767,8 @@ desugaredLambdaImplementationMethods.add(descriptor.implHandle.asMethod()); } } else { + markLambdaAsInstantiated(descriptor, contextMethod); + transitionMethodsForInstantiatedLambda(descriptor); callSites.add(callSite); } @@ -839,6 +938,7 @@ workList.enqueueTraceInvokeDirectAction( invokedMethod, currentHolder, currentMethod)); if (skipTracing) { + addDeadProtoTypeCandidate(invokedMethod.holder); return false; } @@ -854,7 +954,10 @@ return appView.withGeneratedMessageLiteBuilderShrinker( shrinker -> shrinker.deferDeadProtoBuilders( - clazz, currentMethod, () -> liveTypes.registerDeferredAction(clazz, action)), + clazz, + currentMethod, + () -> liveTypes.registerDeferredAction(clazz, action), + this), false); } return false; @@ -997,6 +1100,7 @@ registerDeferredActionForDeadProtoBuilder( type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, context)); if (skipTracing) { + addDeadProtoTypeCandidate(type); return false; } @@ -1059,7 +1163,7 @@ fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle(); } - DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder); + DexProgramClass clazz = getProgramClassOrNull(encodedField.holder()); if (clazz == null) { return false; } @@ -1108,7 +1212,7 @@ fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle(); } - DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder); + DexProgramClass clazz = getProgramClassOrNull(encodedField.holder()); if (clazz == null) { return false; } @@ -1156,7 +1260,8 @@ fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle(); } - if (!isProgramClass(encodedField.field.holder)) { + DexProgramClass holder = getProgramClassOrNull(encodedField.holder()); + if (holder == null) { // No need to trace into the non-program code. return false; } @@ -1174,6 +1279,7 @@ encodedField, fieldAccessInfoCollection, pinnedItems), false); if (skipTracing) { + addDeadProtoTypeCandidate(holder); return false; } } @@ -1213,7 +1319,8 @@ fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle(); } - if (!isProgramClass(encodedField.field.holder)) { + DexProgramClass holder = getProgramClassOrNull(encodedField.holder()); + if (holder == null) { // No need to trace into the non-program code. return false; } @@ -1231,6 +1338,7 @@ encodedField, fieldAccessInfoCollection, pinnedItems), false); if (skipTracing) { + addDeadProtoTypeCandidate(holder); return false; } } @@ -1255,7 +1363,7 @@ if (methodHolderClass != null && methodHolderClass.isInterface()) { return method; } - DexClass holderClass = appView.definitionFor(currentMethod.method.holder); + DexClass holderClass = appView.definitionFor(currentMethod.holder()); if (holderClass == null || holderClass.superType == null || holderClass.isInterface()) { // We do not know better or this call is made from an interface. return method; @@ -1331,10 +1439,9 @@ } // Mark types in inner-class attributes referenced. - InnerClassAttribute innerClassAttributes = holder.getInnerClassAttributeForThisClass(); - if (innerClassAttributes != null) { - recordTypeReference(innerClassAttributes.getInner()); - recordTypeReference(innerClassAttributes.getOuter()); + for (InnerClassAttribute innerClassAttribute : holder.getInnerClasses()) { + recordTypeReference(innerClassAttribute.getInner()); + recordTypeReference(innerClassAttribute.getOuter()); } if (Log.ENABLED) { @@ -1525,11 +1632,7 @@ private SingleResolutionResult resolveMethod(DexMethod method, KeepReason reason) { // Record the references in case they are not program types. - recordTypeReference(method.holder); - recordTypeReference(method.proto.returnType); - for (DexType param : method.proto.parameters.values) { - recordTypeReference(param); - } + recordMethodReference(method); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); if (resolutionResult.isFailedResolution()) { reportMissingMethod(method); @@ -1541,11 +1644,7 @@ private SingleResolutionResult resolveMethod( DexMethod method, KeepReason reason, boolean interfaceInvoke) { // Record the references in case they are not program types. - recordTypeReference(method.holder); - recordTypeReference(method.proto.returnType); - for (DexType param : method.proto.parameters.values) { - recordTypeReference(param); - } + recordMethodReference(method); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method, interfaceInvoke); if (resolutionResult.isFailedResolution()) { @@ -1617,10 +1716,7 @@ DexType holder = method.holder; DexProgramClass clazz = getProgramClassOrNull(holder); if (clazz == null) { - recordTypeReference(method.proto.returnType); - for (DexType param : method.proto.parameters.values) { - recordTypeReference(param); - } + recordMethodReference(method); return; } // TODO(zerny): Is it ok that we lookup in both the direct and virtual pool here? @@ -1717,7 +1813,7 @@ private void markMethodAsTargeted( DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.method.holder == clazz.type; + assert method.holder() == clazz.type; if (!targetedMethods.add(method, reason)) { // Already targeted. return; @@ -1792,7 +1888,7 @@ void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) { assert !clazz.isAnnotation(); assert clazz.isInterface(); - if (!objectAllocationInfoCollection.recordInstantiatedInterface(clazz)) { + if (!objectAllocationInfoCollection.recordInstantiatedInterface(clazz, appInfo)) { return; } markTypeAsLive(clazz, witness); @@ -1804,12 +1900,11 @@ for (DexType iface : descriptor.interfaces) { checkLambdaInterface(iface, context); objectAllocationInfoCollection.recordInstantiatedLambdaInterface(iface, descriptor, appInfo); - // TODO(b/150277553): Lambdas should be accurately traces and thus not be added here. - if (lambdaRewriter == null) { - DexProgramClass clazz = getProgramClassOrNull(iface); - if (clazz != null) { - objectAllocationInfoCollection.recordInstantiatedInterface(clazz); - } + // TODO(b/150277553): Lambdas should be accurately traced and thus not be added here. + assert lambdaRewriter == null; + DexProgramClass clazz = getProgramClassOrNull(iface); + if (clazz != null) { + objectAllocationInfoCollection.recordInstantiatedInterface(clazz, appInfo); } } } @@ -1820,7 +1915,7 @@ StringDiagnostic message = new StringDiagnostic( "Lambda expression implements missing interface `" + itf.toSourceString() + "`", - appInfo.originFor(context.method.holder)); + appInfo.originFor(context.holder())); options.reporter.warning(message); } else if (!clazz.isInterface()) { StringDiagnostic message = @@ -1829,7 +1924,7 @@ + "`" + itf.toSourceString() + "`", - appInfo.originFor(context.method.holder)); + appInfo.originFor(context.holder())); options.reporter.warning(message); } } @@ -1969,7 +2064,7 @@ && appView.rewritePrefix.hasRewrittenTypeInSignature(method.method.proto, appView)) { DexMethod methodToResolve = DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature( - method.method, method.method.holder, appView); + method.method, method.holder(), appView); assert methodToResolve != method.method; markLibraryOrClasspathOverrideLive( instantiation, @@ -2124,7 +2219,7 @@ DexProgramClass holder, DexEncodedField field, KeepReason reason) { assert field != null; assert field.isProgramField(appView); - markTypeAsLive(field.field.holder, reason); + markTypeAsLive(field.holder(), reason); markTypeAsLive(field.field.type, reason); if (Log.ENABLED) { Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field); @@ -2141,7 +2236,7 @@ private void markDirectStaticOrConstructorMethodAsLive( DexProgramClass clazz, DexEncodedMethod encodedMethod, KeepReason reason) { - assert encodedMethod.method.holder == clazz.type; + assert encodedMethod.holder() == clazz.type; if (!enqueueMarkMethodLiveAction(clazz, encodedMethod, reason)) { // Already marked live. @@ -2196,7 +2291,7 @@ if (info == null) { return false; } - DexClass clazz = appView.definitionFor(field.field.holder); + DexClass clazz = appView.definitionFor(field.holder()); DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer(); return defaultInitializer != null ? info.isWrittenOutside(defaultInitializer) @@ -2211,10 +2306,21 @@ return targetedMethods.contains(method); } + public boolean isTypeLive(DexClass clazz) { + return clazz.isProgramClass() + ? isTypeLive(clazz.asProgramClass()) + : isNonProgramTypeLive(clazz); + } + public boolean isTypeLive(DexProgramClass clazz) { return liveTypes.contains(clazz); } + public boolean isNonProgramTypeLive(DexClass clazz) { + assert !clazz.isProgramClass(); + return liveNonProgramTypes.contains(clazz); + } + // Package protected due to entry point from worklist. void markInstanceFieldAsReachable(DexEncodedField encodedField, KeepReason reason) { DexField field = encodedField.field; @@ -2250,19 +2356,6 @@ } } - private void recordTypeReference(DexType type) { - if (type == null) { - return; - } - if (type.isArrayType()) { - type = type.toBaseType(appView.dexItemFactory()); - } - if (!type.isClassType()) { - return; - } - getProgramClassOrNull(type); - } - private void markVirtualMethodAsReachable( DexMethod method, boolean interfaceInvoke, ProgramMethod contextOrNull, KeepReason reason) { if (method.holder.isArrayType()) { @@ -2281,10 +2374,7 @@ if (holder == null) { // TODO(b/139464956): clean this. // Ensure that the full proto of the targeted method is referenced. - recordTypeReference(method.proto.returnType); - for (DexType type : method.proto.parameters.values) { - recordTypeReference(type); - } + recordMethodReference(method); return; } @@ -2385,7 +2475,7 @@ failedResolutionTargets.add(symbolicMethod); failedResolution.forEachFailureDependency( method -> { - DexProgramClass clazz = getProgramClassOrNull(method.method.holder); + DexProgramClass clazz = getProgramClassOrNull(method.holder()); if (clazz != null) { failedResolutionTargets.add(method.method); markMethodAsTargeted(clazz, method, reason); @@ -2430,14 +2520,14 @@ } // If invoke target is invalid (inaccessible or not an instance-method) record it and stop. // TODO(b/146016987): We should be passing the full program context and not looking it up again. - DexProgramClass fromHolder = appInfo.definitionFor(from.method.holder).asProgramClass(); + DexProgramClass fromHolder = appInfo.definitionFor(from.holder()).asProgramClass(); DexEncodedMethod target = resolution.lookupInvokeSuperTarget(fromHolder, appInfo); if (target == null) { failedResolutionTargets.add(resolution.getResolvedMethod().method); return; } - DexProgramClass clazz = getProgramClassOrNull(target.method.holder); + DexProgramClass clazz = getProgramClassOrNull(target.holder()); if (clazz == null) { return; } @@ -2645,7 +2735,7 @@ // Ensure accessors if needed and mark them live too. DexEncodedMethod accessor = lambdaClass.target.ensureAccessibilityIfNeeded(false); if (accessor != null) { - DexProgramClass clazz = getProgramClassOrNull(accessor.method.holder); + DexProgramClass clazz = getProgramClassOrNull(accessor.holder()); additions.addLiveMethod(new ProgramMethod(clazz, accessor)); } } @@ -2684,50 +2774,52 @@ } private AppInfoWithLiveness createAppInfo(AppInfoWithSubtyping appInfo) { + // Compute the set of dead proto types. + deadProtoTypeCandidates.removeIf(this::isTypeLive); + // Remove the temporary mappings that have been inserted into the field access info collection // and verify that the mapping is then one-to-one. fieldAccessInfoCollection.removeIf( (field, info) -> field != info.getField() || info == MISSING_FIELD_ACCESS_INFO); assert fieldAccessInfoCollection.verifyMappingIsOneToOne(); - // Ensure references from various root set collections. - rootSet - .noSideEffects - .keySet() - .forEach( - r -> { - if (r.isDexType()) { - recordTypeReference(r.asDexType()); - } else if (r.isDexField()) { - recordTypeReference(r.asDexField().holder); - recordTypeReference(r.asDexField().type); - } else { - assert r.isDexMethod(); - recordTypeReference(r.asDexMethod().holder); - recordTypeReference(r.asDexMethod().proto.returnType); - for (DexType param : r.asDexMethod().proto.parameters.values) { - recordTypeReference(param); - } - } - }); + // Verify all references on the input app before synthesizing definitions. + assert verifyReferences(appInfo.app()); + + // Prune the root set items that turned out to be dead. + // TODO(b/150736225): Pruning of dead root set items is still incomplete. + rootSet.pruneDeadItems(appView, this); + + // Ensure references from all hard coded factory items. + appView.dexItemFactory().forEachPossiblyCompilerSynthesizedType(this::recordTypeReference); // Rebuild a new app only containing referenced types. - appView.dexItemFactory().forEachPossiblyCompilerSynthesizedType(this::recordTypeReference); Set<DexLibraryClass> libraryClasses = Sets.newIdentityHashSet(); Set<DexClasspathClass> classpathClasses = Sets.newIdentityHashSet(); for (DexClass clazz : liveNonProgramTypes) { - traverseHierarchy(clazz, libraryClasses, classpathClasses); + if (clazz.isLibraryClass()) { + libraryClasses.add(clazz.asLibraryClass()); + } else if (clazz.isClasspathClass()) { + classpathClasses.add(clazz.asClasspathClass()); + } else { + assert false; + } } + + // Add just referenced non-program types. We can't replace the program classes at this point as + // they are needed in tree pruning. Builder appBuilder = appInfo.app().asDirect().builder(); appBuilder.replaceLibraryClasses(libraryClasses); appBuilder.replaceClasspathClasses(classpathClasses); - // Can't replace the program classes at this point as they are needed in tree pruning. - // Post process the app to add synthetic content. DirectMappedDexApplication app = appBuilder.build(); + // Verify the references on the pruned application after type synthesis. + assert verifyReferences(app); + AppInfoWithLiveness appInfoWithLiveness = new AppInfoWithLiveness( app, + SetUtils.mapIdentityHashSet(deadProtoTypeCandidates, DexProgramClass::getType), missingTypes, SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType), Collections.unmodifiableSet(instantiatedAppServices), @@ -2740,7 +2832,7 @@ toSortedDescriptorSet(liveMethods.getItems()), // Filter out library fields and pinned fields, because these are read by default. fieldAccessInfoCollection, - objectAllocationInfoCollection.build(), + objectAllocationInfoCollection.build(appInfo), // TODO(b/132593519): Do we require these sets to be sorted for determinism? toImmutableSortedMap(virtualInvokes, PresortedComparable::slowCompare), toImmutableSortedMap(interfaceInvokes, PresortedComparable::slowCompare), @@ -2768,54 +2860,72 @@ Collections.emptySet(), Collections.emptyMap(), EnumValueInfoMapCollection.empty(), - // TODO(b/150277553): Remove this once object allocation contains the information. - SetUtils.mapIdentityHashSet( - objectAllocationInfoCollection.unknownInstantiatedInterfaceTypes, - DexProgramClass::getType), constClassReferences, initClassReferences); appInfo.markObsolete(); return appInfoWithLiveness; } - private void traverseHierarchy( - DexClass clazz, - Set<DexLibraryClass> libraryClasses, - Set<DexClasspathClass> classpathClasses) { - if (clazz.isLibraryClass()) { - libraryClasses.add(clazz.asLibraryClass()); - } else if (clazz.isClasspathClass()) { - classpathClasses.add(clazz.asClasspathClass()); + private boolean verifyReferences(DexApplication app) { + WorkList<DexClass> worklist = WorkList.newIdentityWorkList(); + for (DexProgramClass clazz : liveTypes.getItems()) { + worklist.addIfNotSeen(clazz); } - Deque<DexType> worklist = new ArrayDeque<>(); - if (clazz.superType != null) { - worklist.add(clazz.superType); + while (worklist.hasNext()) { + DexClass clazz = worklist.next(); + assert verifyReferencedType(clazz, worklist, app); } - Collections.addAll(worklist, clazz.interfaces.values); - while (!worklist.isEmpty()) { - DexType type = worklist.pop(); - DexClass definition = appView.definitionFor(type); - if (definition == null) { - continue; - } - if (definition.isProgramClass()) { - // TODO(b/120884788): This should assert not possible once fixed. - continue; - } - if (definition.isLibraryClass()) { - if (!libraryClasses.add(definition.asLibraryClass())) { - continue; - } - } else if (definition.isClasspathClass()) { - if (!classpathClasses.add(definition.asClasspathClass())) { - continue; - } - } - if (definition.superType != null) { - worklist.add(definition.superType); - } - Collections.addAll(worklist, definition.interfaces.values); + return true; + } + + private boolean verifyReferencedType( + DexType type, WorkList<DexClass> worklist, DexApplication app) { + if (type.isArrayType()) { + type = type.toBaseType(appView.dexItemFactory()); } + if (!type.isClassType()) { + return true; + } + DexClass clazz = app.definitionFor(type); + if (clazz == null) { + assert missingTypes.contains(type) : "Expected type to be in missing types': " + type; + } else { + assert !missingTypes.contains(type) : "Type with definition also in missing types: " + type; + // Eager assert while the context is still present. + assert clazz.isProgramClass() || liveNonProgramTypes.contains(clazz) + : "Expected type to be in live non-program types: " + clazz; + worklist.addIfNotSeen(clazz); + } + return true; + } + + private boolean verifyReferencedType( + DexClass clazz, WorkList<DexClass> worklist, DexApplication app) { + for (DexType supertype : clazz.allImmediateSupertypes()) { + assert verifyReferencedType(supertype, worklist, app); + } + assert clazz.isProgramClass() || liveNonProgramTypes.contains(clazz) + : "Expected type to be in live non-program types: " + clazz; + for (DexEncodedField field : clazz.fields()) { + if (clazz.isNotProgramClass() || isFieldReferenced(field)) { + assert verifyReferencedType(field.field.type, worklist, app); + } + } + for (DexEncodedMethod method : clazz.methods()) { + if (clazz.isNotProgramClass() || isMethodTargeted(method)) { + assert verifyReferencedMethod(method, worklist, app); + } + } + return true; + } + + private boolean verifyReferencedMethod( + DexEncodedMethod method, WorkList<DexClass> worklist, DexApplication app) { + assert verifyReferencedType(method.method.proto.returnType, worklist, app); + for (DexType param : method.method.proto.parameters.values) { + assert verifyReferencedType(param, worklist, app); + } + return true; } private void synthesizeLibraryConversionWrappers(SyntheticAdditions additions) { @@ -2826,7 +2936,7 @@ // Generate first the callbacks since they may require extra wrappers. List<DexEncodedMethod> callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods(); for (DexEncodedMethod callback : callbacks) { - DexProgramClass clazz = getProgramClassOrNull(callback.method.holder); + DexProgramClass clazz = getProgramClassOrNull(callback.holder()); additions.addLiveMethod(new ProgramMethod(clazz, callback)); } @@ -3111,7 +3221,7 @@ } else { DexEncodedMethod implementation = target.getDefaultInterfaceMethodImplementation(); if (implementation != null) { - DexProgramClass companion = getProgramClassOrNull(implementation.method.holder); + DexProgramClass companion = getProgramClassOrNull(implementation.holder()); markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion)); markVirtualMethodAsLive( companion, @@ -3128,7 +3238,7 @@ // Package protected due to entry point from worklist. void markFieldAsKept(DexProgramClass holder, DexEncodedField target, KeepReason reason) { - assert holder.type == target.field.holder; + assert holder.type == target.holder(); if (target.accessFlags.isStatic()) { markStaticFieldAsLive(target, reason); } else { @@ -3206,7 +3316,7 @@ void markMethodAsLive(DexEncodedMethod method, KeepReason reason) { assert liveMethods.contains(method); - DexProgramClass clazz = getProgramClassOrNull(method.method.holder); + DexProgramClass clazz = getProgramClassOrNull(method.holder()); if (clazz == null) { return; } @@ -3221,7 +3331,7 @@ if (Log.ENABLED) { Log.verbose(getClass(), "Found super invoke constraint on `%s`.", superCallTarget.method); } - DexProgramClass targetClass = getProgramClassOrNull(superCallTarget.method.holder); + DexProgramClass targetClass = getProgramClassOrNull(superCallTarget.holder()); assert targetClass != null; if (targetClass != null) { markMethodAsTargeted( @@ -3246,7 +3356,7 @@ private void markReferencedTypesAsLive(DexEncodedMethod method) { markTypeAsLive( - method.method.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, method)); + method.holder(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method)); markParameterAndReturnTypesAsLive(method); } @@ -3290,7 +3400,7 @@ } private void handleReflectiveBehavior(DexEncodedMethod method) { - DexType originHolder = method.method.holder; + DexType originHolder = method.holder(); Origin origin = appInfo.originFor(originHolder); IRCode code = method.buildIR(appView, origin); InstructionIterator iterator = code.instructionIterator(); @@ -3578,7 +3688,7 @@ DexType type = invoke.inValues().get(0).definition.asConstClass().getValue(); DexProgramClass clazz = getProgramClassOrNull(type); if (clazz != null && clazz.accessFlags.isEnum()) { - DexProgramClass holder = getProgramClassOrNull(method.method.holder); + DexProgramClass holder = getProgramClassOrNull(method.holder()); markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(holder, method)); } } @@ -3603,7 +3713,7 @@ + "` is being passed to the method `" + invoke.getInvokedMethod().toSourceString() + "`, but was not found in `META-INF/services/`.", - appInfo.originFor(method.method.holder))); + appInfo.originFor(method.holder()))); } return; } @@ -3772,10 +3882,7 @@ @Override public boolean addMethod(DexMethod method) { // Record the references in case they are not program types. - recordTypeReference(method.proto.returnType); - for (DexType param : method.proto.parameters.values) { - recordTypeReference(param); - } + recordMethodReference(method); DexProgramClass holder = getProgramClassOrNull(method.holder); if (holder == null) { return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java index c795bcc..2a46ba3 100644 --- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java +++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -264,7 +264,7 @@ public void enqueueMarkReachableFieldAction( DexProgramClass clazz, DexEncodedField field, KeepReason reason) { - assert field.field.holder == clazz.type; + assert field.holder() == clazz.type; queue.add(new MarkReachableFieldAction(field, reason)); } @@ -294,13 +294,13 @@ void enqueueMarkMethodLiveAction( DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.method.holder == clazz.type; + assert method.holder() == clazz.type; queue.add(new MarkMethodLiveAction(method, reason)); } void enqueueMarkMethodKeptAction( DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.method.holder == clazz.type; + assert method.holder() == clazz.type; queue.add(new MarkMethodKeptAction(clazz, method, reason)); } @@ -317,7 +317,7 @@ public void enqueueTraceInvokeDirectAction( DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) { - assert currentMethod.method.holder == currentHolder.type; + assert currentMethod.holder() == currentHolder.type; queue.add(new TraceInvokeDirectAction(invokedMethod, currentHolder, currentMethod)); }
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java index cf863de..6b222d7 100644 --- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java +++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -171,7 +171,7 @@ public KeepReasonWitness reportCompatKeepDefaultInitializer( DexProgramClass holder, DexEncodedMethod defaultInitializer) { - assert holder.type == defaultInitializer.method.holder; + assert holder.type == defaultInitializer.holder(); assert holder.getDefaultInitializer() == defaultInitializer; if (keptGraphConsumer != null) { reportEdge( @@ -183,7 +183,7 @@ } public KeepReasonWitness reportCompatKeepMethod(DexProgramClass holder, DexEncodedMethod method) { - assert holder.type == method.method.holder; + assert holder.type == method.holder(); // TODO(b/141729349): This compat rule is from the method to itself and has not edge. Fix it. // The rule is stating that if the method is targeted it is live. Since such an edge does // not contribute to additional information in the kept graph as it stands (no distinction @@ -234,7 +234,7 @@ public KeepReasonWitness reportReachableClassInitializer( DexProgramClass clazz, DexEncodedMethod initializer) { if (initializer != null) { - assert clazz.type == initializer.method.holder; + assert clazz.type == initializer.holder(); assert initializer.isClassInitializer(); if (keptGraphConsumer != null) { ClassGraphNode source = getClassGraphNode(clazz.type); @@ -284,7 +284,7 @@ public KeepReasonWitness reportCompanionMethod( DexEncodedMethod definition, DexEncodedMethod implementation) { - assert InterfaceMethodRewriter.isCompanionClassType(implementation.method.holder); + assert InterfaceMethodRewriter.isCompanionClassType(implementation.holder()); if (keptGraphConsumer == null) { return KeepReasonWitness.INSTANCE; } @@ -359,7 +359,7 @@ if (skipReporting(reason)) { return KeepReasonWitness.INSTANCE; } - if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.method.holder)) { + if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.holder())) { // Don't report edges to actual library methods. // TODO(b/120959039): This should be dead code once no library classes are ever enqueued. return KeepReasonWitness.INSTANCE; @@ -415,6 +415,11 @@ return reasonInfo.computeIfAbsent(kind, k -> new GraphEdgeInfo(k)); } + private DexClass definitionFor(DexType type) { + // The query of the graph can be outside program referenced types and should not fail. + return appView.appInfo().definitionForWithoutExistenceAssert(type); + } + AnnotationGraphNode getAnnotationGraphNode(DexItem type) { return annotationNodes.computeIfAbsent( type, @@ -431,7 +436,7 @@ return classNodes.computeIfAbsent( type, t -> { - DexClass definition = appView.definitionFor(t); + DexClass definition = definitionFor(t); return new ClassGraphNode( definition != null && definition.isNotProgramClass(), Reference.classFromDescriptor(t.toDescriptorString())); @@ -442,7 +447,7 @@ return methodNodes.computeIfAbsent( context, m -> { - DexClass holderDefinition = appView.definitionFor(context.holder); + DexClass holderDefinition = definitionFor(context.holder); Builder<TypeReference> builder = ImmutableList.builder(); for (DexType param : m.proto.parameters.values) { builder.add(Reference.typeFromDescriptor(param.toDescriptorString())); @@ -463,7 +468,7 @@ return fieldNodes.computeIfAbsent( context, f -> { - DexClass holderDefinition = appView.definitionFor(context.holder); + DexClass holderDefinition = definitionFor(context.holder); return new FieldGraphNode( holderDefinition != null && holderDefinition.isNotProgramClass(), Reference.field(
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java index c66d4a4..dd76e19 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -152,7 +152,7 @@ private InvokedFrom(DexProgramClass holder, DexEncodedMethod method) { super(method); - assert holder.type == method.method.holder; + assert holder.type == method.holder(); } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java index 7e9ed91..5b1f672 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -473,13 +473,21 @@ configurationBuilder.addRule(rule); return true; } + if (acceptString("neverreprocessclassinitializer")) { + configurationBuilder.addRule( + parseReprocessClassInitializerRule( + ReprocessClassInitializerRule.Type.NEVER, optionStart)); + return true; + } if (acceptString("neverreprocessmethod")) { configurationBuilder.addRule( parseReprocessMethodRule(ReprocessMethodRule.Type.NEVER, optionStart)); return true; } if (acceptString("reprocessclassinitializer")) { - configurationBuilder.addRule(parseReprocessClassInitializerRule(optionStart)); + configurationBuilder.addRule( + parseReprocessClassInitializerRule( + ReprocessClassInitializerRule.Type.ALWAYS, optionStart)); return true; } if (acceptString("reprocessmethod")) { @@ -822,10 +830,11 @@ return keepRuleBuilder.build(); } - private ReprocessClassInitializerRule parseReprocessClassInitializerRule(Position start) + private ReprocessClassInitializerRule parseReprocessClassInitializerRule( + ReprocessClassInitializerRule.Type type, Position start) throws ProguardRuleParserException { ReprocessClassInitializerRule.Builder builder = - ReprocessClassInitializerRule.builder().setOrigin(origin).setStart(start); + ReprocessClassInitializerRule.builder().setOrigin(origin).setStart(start).setType(type); parseClassSpec(builder, false); Position end = getPosition(); builder.setSource(getSourceSnippet(contents, start, end));
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java index 41aa9c5..d59c63e 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -65,6 +65,14 @@ return null; } + public boolean isReprocessClassInitializerRule() { + return false; + } + + public ReprocessClassInitializerRule asReprocessClassInitializerRule() { + return null; + } + public boolean isReprocessMethodRule() { return false; }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java index f75a307..590ede0 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -57,7 +57,7 @@ public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) { // TODO(b/122295241): These generated rules should be linked into the graph, eg, the method // using identified reflection should be the source keeping the target alive. - assert clazz.type == method.method.holder; + assert clazz.type == method.holder(); ProguardKeepRule.Builder builder = ProguardKeepRule.builder(); builder.setOrigin(proguardCompatOrigin); builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
diff --git a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java index fa7d352..58e03d9 100644 --- a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java +++ b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
@@ -3,19 +3,32 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; import java.util.List; public class ReprocessClassInitializerRule extends ProguardConfigurationRule { + public enum Type { + ALWAYS, + NEVER + } + public static class Builder extends ProguardConfigurationRule.Builder<ReprocessClassInitializerRule, Builder> { + private Type type; + private Builder() { super(); } + public Builder setType(Type type) { + this.type = type; + return this; + } + @Override public Builder self() { return this; @@ -36,10 +49,13 @@ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, - memberRules); + memberRules, + type); } } + private final Type type; + private ReprocessClassInitializerRule( Origin origin, Position position, @@ -53,7 +69,8 @@ ProguardTypeMatcher inheritanceAnnotation, ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends, - List<ProguardMemberRule> memberRules) { + List<ProguardMemberRule> memberRules, + Type type) { super( origin, position, @@ -68,14 +85,36 @@ inheritanceClassName, inheritanceIsExtends, memberRules); + this.type = type; } public static Builder builder() { return new Builder(); } + public Type getType() { + return type; + } + + @Override + public boolean isReprocessClassInitializerRule() { + return true; + } + + @Override + public ReprocessClassInitializerRule asReprocessClassInitializerRule() { + return this; + } + @Override String typeString() { - return "reprocessclassinitializer"; + switch (type) { + case ALWAYS: + return "reprocessclassinitializer"; + case NEVER: + return "neverreprocessclassinitializer"; + default: + throw new Unreachable(); + } } }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java index 928de3a..c89dd04 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import static com.android.tools.r8.shaking.ReprocessClassInitializerRule.Type.ALWAYS; +import static com.android.tools.r8.shaking.ReprocessClassInitializerRule.Type.NEVER; + import com.android.tools.r8.dex.Constants; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppInfo; @@ -14,6 +17,7 @@ import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexDefinition; +import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMember; import com.android.tools.r8.graph.DexEncodedMethod; @@ -377,7 +381,7 @@ DexEncodedMethod target = appView.appInfo().resolveMethod(subType, referenceInSubType).getSingleTarget(); // But, the resolution should not be landed on the current type we are visiting. - if (target == null || target.method.holder == type) { + if (target == null || target.holder() == type) { continue; } ProguardMemberRule ruleInSubType = assumeRulePool.get(target.method); @@ -598,7 +602,7 @@ private boolean canInsertForwardingMethod(DexClass holder, DexEncodedMethod target) { return appView.options().isGeneratingDex() - || ArrayUtils.contains(holder.interfaces.values, target.method.holder); + || ArrayUtils.contains(holder.interfaces.values, target.holder()); } private void markMatchingOverriddenMethods( @@ -1056,7 +1060,7 @@ if (options.isInterfaceMethodDesugaringEnabled() && encodedMethod.hasCode() && (encodedMethod.isPrivateMethod() || encodedMethod.isStaticMember())) { - DexClass holder = appView.definitionFor(encodedMethod.method.holder); + DexClass holder = appView.definitionFor(encodedMethod.holder()); if (holder != null && holder.isInterface()) { if (rule.isSpecific()) { options.reporter.warning( @@ -1219,7 +1223,16 @@ } else if (context instanceof ReprocessClassInitializerRule) { DexProgramClass clazz = item.asProgramClass(); if (clazz != null && clazz.hasClassInitializer()) { - reprocess.add(clazz.getClassInitializer().method); + switch (context.asReprocessClassInitializerRule().getType()) { + case ALWAYS: + reprocess.add(clazz.getClassInitializer().method); + break; + case NEVER: + neverReprocess.add(clazz.getClassInitializer().method); + break; + default: + throw new Unreachable(); + } context.markAsUsed(); } } else if (context.isReprocessMethodRule()) { @@ -1306,8 +1319,8 @@ this.noObfuscation = noObfuscation; this.reasonAsked = reasonAsked; this.checkDiscarded = checkDiscarded; - this.alwaysInline = Collections.unmodifiableSet(alwaysInline); - this.forceInline = Collections.unmodifiableSet(forceInline); + this.alwaysInline = alwaysInline; + this.forceInline = forceInline; this.neverInline = neverInline; this.bypassClinitForInlining = bypassClinitForInlining; this.whyAreYouNotInlining = whyAreYouNotInlining; @@ -1317,7 +1330,7 @@ this.neverReprocess = neverReprocess; this.alwaysClassInline = alwaysClassInline; this.neverClassInline = neverClassInline; - this.neverMerge = Collections.unmodifiableSet(neverMerge); + this.neverMerge = neverMerge; this.neverPropagateValue = neverPropagateValue; this.mayHaveSideEffects = mayHaveSideEffects; this.noSideEffects = noSideEffects; @@ -1448,6 +1461,45 @@ assumedValues.remove(reference); } + public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) { + pruneDeadReferences(neverMerge, definitions, enqueuer); + pruneDeadReferences(alwaysInline, definitions, enqueuer); + pruneDeadReferences(noSideEffects.keySet(), definitions, enqueuer); + } + + private static void pruneDeadReferences( + Set<? extends DexReference> references, + DexDefinitionSupplier definitions, + Enqueuer enqueuer) { + references.removeIf( + reference -> { + if (reference.isDexField()) { + DexEncodedField definition = definitions.definitionFor(reference.asDexField()); + if (definition == null) { + return true; + } + DexClass holder = definitions.definitionFor(definition.holder()); + if (holder.isProgramClass()) { + return !enqueuer.isFieldReferenced(definition); + } + return !enqueuer.isNonProgramTypeLive(holder); + } else if (reference.isDexMethod()) { + DexEncodedMethod definition = definitions.definitionFor(reference.asDexMethod()); + if (definition == null) { + return true; + } + DexClass holder = definitions.definitionFor(definition.holder()); + if (holder.isProgramClass()) { + return !enqueuer.isMethodLive(definition) && !enqueuer.isMethodTargeted(definition); + } + return !enqueuer.isNonProgramTypeLive(holder); + } else { + DexClass definition = definitions.definitionFor(reference.asDexType()); + return definition == null || !enqueuer.isTypeLive(definition); + } + }); + } + public void move(DexReference original, DexReference rewritten) { copy(original, rewritten); prune(original);
diff --git a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java index 7326fad..4675107 100644 --- a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java +++ b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
@@ -63,8 +63,8 @@ } if (method.accessFlags.isMoreVisibleThan( existing.accessFlags, - method.method.holder.getPackageName(), - existing.method.holder.getPackageName())) { + method.holder().getPackageName(), + existing.holder().getPackageName())) { items.put(wrapped, method); return AddMethodIfMoreVisibleResult.ADDED_MORE_VISIBLE; }
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java index 1fb713e..7c679e0 100644 --- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java +++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -349,8 +349,8 @@ : reachableOrReferencedFields.toArray(DexEncodedField.EMPTY_ARRAY); } - public Collection<DexType> getRemovedClasses() { - return Collections.unmodifiableCollection(prunedTypes); + public Set<DexType> getRemovedClasses() { + return Collections.unmodifiableSet(prunedTypes); } public Collection<DexReference> getMethodsToKeepForConfigurationDebugging() {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index 92fa339..2fb7435 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -29,6 +29,7 @@ import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult; import com.android.tools.r8.graph.LookupResult.LookupResultSuccess; import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.graph.ObjectAllocationInfoCollection; import com.android.tools.r8.graph.ParameterAnnotationsList; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.RewrittenPrototypeDescription; @@ -332,9 +333,9 @@ private boolean isMergeCandidate( DexProgramClass sourceClass, DexProgramClass targetClass, Set<DexType> pinnedTypes) { assert targetClass != null; - - if (appInfo.getObjectAllocationInfoCollection().isInstantiatedDirectly(sourceClass) - || appInfo.instantiatedLambdas.contains(sourceClass.type) + ObjectAllocationInfoCollection allocationInfo = appInfo.getObjectAllocationInfoCollection(); + if (allocationInfo.isInstantiatedDirectly(sourceClass) + || allocationInfo.isInterfaceWithUnknownSubtypeHierarchy(sourceClass) || appInfo.isPinned(sourceClass.type) || pinnedTypes.contains(sourceClass.type) || appInfo.neverMerge.contains(sourceClass.type)) { @@ -736,7 +737,7 @@ // Conservatively find all possible targets for this method. LookupResultSuccess lookupResult = appInfo - .resolveMethodOnInterface(method.method.holder, method.method) + .resolveMethodOnInterface(method.holder(), method.method) .lookupVirtualDispatchTargets(target, appInfo) .asLookupResultSuccess(); assert lookupResult != null; @@ -988,7 +989,7 @@ Rename.ALWAYS, appView .dexItemFactory() - .prependTypeToProto(virtualMethod.method.holder, virtualMethod.method.proto)); + .prependTypeToProto(virtualMethod.holder(), virtualMethod.method.proto)); makeStatic(resultingDirectMethod); // Update method pool collection now that we are adding a new public method. @@ -1327,7 +1328,7 @@ private DexEncodedMethod renameConstructor( DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) { assert method.isInstanceInitializer(); - DexType oldHolder = method.method.holder; + DexType oldHolder = method.holder(); DexMethod newSignature; int count = 1; @@ -1363,7 +1364,7 @@ // renamed already. assert !method.accessFlags.isConstructor() || strategy == Rename.NEVER; DexString oldName = method.method.name; - DexType oldHolder = method.method.holder; + DexType oldHolder = method.holder(); DexMethod newSignature; switch (strategy) { @@ -1398,7 +1399,7 @@ private DexEncodedField renameFieldIfNeeded( DexEncodedField field, Predicate<DexField> availableFieldSignatures) { DexString oldName = field.field.name; - DexType oldHolder = field.field.holder; + DexType oldHolder = field.holder(); DexField newSignature = application.dexItemFactory.createField(target.type, field.field.type, oldName); @@ -1647,7 +1648,7 @@ code.computeInliningConstraint( method, appView, - new SingleTypeMapperGraphLense(method.method.holder, invocationContext), + new SingleTypeMapperGraphLense(method.holder(), invocationContext), invocationContext); if (constraint == ConstraintWithTarget.NEVER) { return AbortReason.UNSAFE_INLINING;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java index eded127..1013539 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -125,7 +125,7 @@ if (virtualToDirectMethodMap != null) { GraphLenseLookupResult lookup = virtualToDirectMethodMap.get(previous.getMethod()); if (lookup != null) { - // If the super class A of the enclosing class B (i.e., context.method.holder) + // If the super class A of the enclosing class B (i.e., context.holder()) // has been merged into B during vertical class merging, and this invoke-super instruction // was resolving to a method in A, then the target method has been changed to a direct // method and moved into B, so that we need to use an invoke-direct instruction instead of
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index f9f955c..ad290b6 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -21,6 +21,7 @@ import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic; import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic; import com.android.tools.r8.errors.InvalidDebugInfoException; +import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic; import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic; import com.android.tools.r8.experimental.graphinfo.GraphConsumer; import com.android.tools.r8.features.FeatureSplitConfiguration; @@ -70,6 +71,7 @@ import java.util.function.BiConsumer; import java.util.function.BiPredicate; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.objectweb.asm.Opcodes; public class InternalOptions { @@ -183,6 +185,7 @@ enableLambdaMerging = false; enableHorizontalClassMerging = false; enableVerticalClassMerging = false; + enableEnumUnboxing = false; enableUninstantiatedTypeOptimization = false; enableUnusedArgumentRemoval = false; outline.enabled = false; @@ -701,6 +704,8 @@ /** A set of dexitems we have reported missing to dedupe warnings. */ private final Set<DexItem> reportedMissingForDesugaring = Sets.newConcurrentHashSet(); + private final Set<DexItem> invalidLibraryClasses = Sets.newConcurrentHashSet(); + public void errorMissingClassMissingNestHost(DexClass compiledClass) { throw reporter.fatalError(messageErrorMissingNestHost(compiledClass)); } @@ -844,6 +849,26 @@ } } + public void warningInvalidLibrarySuperclassForDesugar( + Origin origin, + DexType libraryType, + DexType invalidSuperType, + String message, + Set<DexMethod> retarget, + AppView<?> appView) { + if (invalidLibraryClasses.add(invalidSuperType)) { + reporter.warning( + new InvalidLibrarySuperclassDiagnostic( + origin, + Reference.classFromDescriptor(libraryType.toDescriptorString()), + Reference.classFromDescriptor(invalidSuperType.toDescriptorString()), + message, + retarget.stream() + .map(method -> method.asMethodReference(appView)) + .collect(Collectors.toList()))); + } + } + public void warningMissingEnclosingMember(DexType clazz, Origin origin, int version) { TypeVersionPair pair = new TypeVersionPair(version, clazz); synchronized (missingEnclosingMembers) {
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java index 6bce9c1..9c18d3a 100644 --- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java +++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -185,7 +185,7 @@ if (parsedData != null || parsedKotlinSourceDebugExtensions.containsKey(holder)) { return parsedData; } - DexClass clazz = appView.definitionFor(currentMethod.method.holder); + DexClass clazz = appView.definitionFor(currentMethod.holder()); DexValueString dexValueString = appView.getSourceDebugExtensionForType(clazz); if (dexValueString != null) { parsedData = KotlinSourceDebugExtensionParser.parse(dexValueString.value.toString()); @@ -456,7 +456,7 @@ continue; } // We use the same name for interface names even if it has different types. - DexProgramClass clazz = appView.definitionForProgramType(method.method.holder); + DexProgramClass clazz = appView.definitionForProgramType(method.holder()); DexClassAndMethod lookupResult = appView.appInfo().lookupMaximallySpecificMethod(clazz, method.method); if (lookupResult == null) { @@ -783,7 +783,7 @@ } method.setCode( new CfCode( - method.method.holder, + method.holder(), oldCode.getMaxStack(), oldCode.getMaxLocals(), newInstructions,
diff --git a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java index 2c6fac0..880da5e 100644 --- a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java +++ b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
@@ -16,4 +16,8 @@ } return null; } + + public static <T> Predicate<T> not(Predicate<T> predicate) { + return t -> !predicate.test(t); + } }
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java index 8c659b1..30f6489 100644 --- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java +++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1000,7 +1000,9 @@ "lang.SecurityManager.checkMulticastLjava_net_InetAddress.SecurityManager_checkMulticast_A01", anyDexVm()) .put("lang.SecurityManager.Constructor.SecurityManager_Constructor_A01", anyDexVm()) - .put("lang.SecurityManager.getClassContext.SecurityManager_getClassContext_A01", anyDexVm()) + .put( + "lang.SecurityManager.getClassContext.SecurityManager_getClassContext_A01", + anyDexVm()) .put( "lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A03", anyDexVm()) @@ -1117,7 +1119,9 @@ .put( "lang.SecurityManager.checkLinkLjava_lang_String.SecurityManager_checkLink_A02", anyDexVm()) - .put("lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A01", anyDexVm()) + .put( + "lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A01", + anyDexVm()) .put( "lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A02", anyDexVm()) @@ -1740,22 +1744,25 @@ .put("lang.reflect.Field.toGenericString.Field_toGenericString_A01", cf()) .build(); // end of failuresToTriage - public static final Multimap<String, TestCondition> bugs = new ImmutableListMultimap.Builder<String, TestCondition>() // The following StringBuffer/StringBuilder tests fails because we remove, e.g., // new StringBuffer(-5) if it is dead code (but it should trow), see b/133745205 - .put("lang.StringBuffer.ConstructorLjava_lang_String.StringBuffer_Constructor_A02", + .put( + "lang.StringBuffer.ConstructorLjava_lang_String.StringBuffer_Constructor_A02", match(R8DEX_COMPILER)) - .put("lang.StringBuffer.ConstructorLjava_lang_CharSequence.StringBuffer_Constructor_A02", + .put( + "lang.StringBuffer.ConstructorLjava_lang_CharSequence.StringBuffer_Constructor_A02", match(R8DEX_COMPILER)) - .put("lang.StringBuffer.ConstructorI.StringBuffer_Constructor_A02", + .put("lang.StringBuffer.ConstructorI.StringBuffer_Constructor_A02", match(R8DEX_COMPILER)) + .put( + "lang.StringBuilder.ConstructorI.StringBuilder_Constructor_A02", match(R8DEX_COMPILER)) - .put("lang.StringBuilder.ConstructorI.StringBuilder_Constructor_A02", - match(R8DEX_COMPILER)) - .put("lang.StringBuilder.ConstructorLjava_lang_CharSequence.StringBuilder_Constructor_A02", + .put( + "lang.StringBuilder.ConstructorLjava_lang_CharSequence.StringBuilder_Constructor_A02", match(R8DEX_COMPILER)) - .put("lang.StringBuilder.ConstructorLjava_lang_String.StringBuilder_Constructor_A02", + .put( + "lang.StringBuilder.ConstructorLjava_lang_String.StringBuilder_Constructor_A02", match(R8DEX_COMPILER)) .build(); @@ -1814,9 +1821,11 @@ .put( "util.concurrent.AbstractExecutorService.invokeAllLjava_util_CollectionJLjava_util_concurrent_TimeUnit.AbstractExecutorService_invokeAll_A06", match(runtimes(Runtime.ART_V4_0_4))) + .put( + "util.concurrent.Executors.newCachedThreadPoolLjava_util_concurrent_ThreadFactory.Executors_newCachedThreadPool_A01", + anyDexVm()) .put("lang.ref.SoftReference.get.SoftReference_get_A01", cf()) .put("lang.ref.WeakReference.get.WeakReference_get_A01", cf()) - .build(); // end of flakyWhenRun public static final Multimap<String, TestCondition> timeoutsWhenRun =
diff --git a/src/test/java/com/android/tools/r8/NeverReprocessClassInitializer.java b/src/test/java/com/android/tools/r8/NeverReprocessClassInitializer.java new file mode 100644 index 0000000..d0fbd19 --- /dev/null +++ b/src/test/java/com/android/tools/r8/NeverReprocessClassInitializer.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2020, 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +public @interface NeverReprocessClassInitializer {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java index 569c222..e28b4eb 100644 --- a/src/test/java/com/android/tools/r8/R8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -53,6 +53,7 @@ private boolean enableMemberValuePropagationAnnotations = false; private boolean enableMergeAnnotations = false; private boolean enableNeverClassInliningAnnotations = false; + private boolean enableNeverReprocessClassInitializerAnnotations = false; private boolean enableNeverReprocessMethodAnnotations = false; private boolean enableReprocessClassInitializerAnnotations = false; private boolean enableReprocessMethodAnnotations = false; @@ -72,6 +73,7 @@ || enableMemberValuePropagationAnnotations || enableMergeAnnotations || enableNeverClassInliningAnnotations + || enableNeverReprocessClassInitializerAnnotations || enableNeverReprocessMethodAnnotations || enableReprocessClassInitializerAnnotations || enableReprocessMethodAnnotations @@ -407,6 +409,16 @@ return self(); } + public T enableNeverReprocessClassInitializerAnnotations() { + if (!enableNeverReprocessClassInitializerAnnotations) { + enableNeverReprocessClassInitializerAnnotations = true; + addInternalKeepRules( + "-neverreprocessclassinitializer @com.android.tools.r8.NeverReprocessClassInitializer" + + " class *"); + } + return self(); + } + public T enableReprocessMethodAnnotations() { if (!enableReprocessMethodAnnotations) { enableReprocessMethodAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java new file mode 100644 index 0000000..56cde8f --- /dev/null +++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -0,0 +1,85 @@ +// Copyright (c) 2020, 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.annotations; + +import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRuntime; +import com.android.tools.r8.TestRuntime.CfRuntime; +import com.android.tools.r8.ToolHelper.KotlinTargetVersion; +import com.android.tools.r8.retrace.KotlinInlineFunctionRetraceTest; +import com.android.tools.r8.shaking.ProguardKeepAttributes; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.AnnotationSubject; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.ExecutionException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SourceDebugExtensionTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public SourceDebugExtensionTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws IOException, CompilationFailedException, ExecutionException { + CfRuntime cfRuntime = + parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9(); + Path kotlinSources = + kotlinc(cfRuntime, getStaticTemp(), KOTLINC, KotlinTargetVersion.JAVA_8) + .addSourceFiles( + getFilesInTestFolderRelativeToClass( + KotlinInlineFunctionRetraceTest.class, "kt", ".kt")) + .compile(); + CodeInspector kotlinInspector = new CodeInspector(kotlinSources); + inspectSourceDebugExtension(kotlinInspector); + testForR8(parameters.getBackend()) + .addProgramFiles(kotlinSources) + .addKeepAttributes(ProguardKeepAttributes.SOURCE_DEBUG_EXTENSION) + .addKeepAllClassesRule() + .setMode(CompilationMode.RELEASE) + .setMinApi(parameters.getApiLevel()) + .allowDiagnosticWarningMessages( + parameters.isDexRuntime() + && parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M)) + .compile() + .assertAllWarningMessagesMatch( + containsString( + "Type `kotlin.jvm.internal.Intrinsics` was not found, it is required for default" + + " or static interface methods")) + .inspect(this::inspectSourceDebugExtension); + } + + private void inspectSourceDebugExtension(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz("retrace.InlineFunctionKt"); + assertThat(clazz, isPresent()); + AnnotationSubject sourceDebugExtensions = + clazz.annotation("dalvik.annotation.SourceDebugExtension"); + assertThat(sourceDebugExtensions, isPresent()); + } +}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java index 862bb79..3240d08 100644 --- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java +++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -28,6 +28,7 @@ import java.nio.file.Paths; import java.util.Calendar; import java.util.List; +import java.util.TreeSet; public abstract class MethodGenerationBase extends TestBase { @@ -102,8 +103,7 @@ if (method.isInitializer()) { continue; } - String methodName = - method.method.holder.getName() + "_" + method.method.name.toString(); + String methodName = method.holder().getName() + "_" + method.method.name.toString(); codePrinter.visitMethod(methodName, method.getCode().asCfCode()); } }); @@ -117,8 +117,15 @@ private void generateRawOutput(CfCodePrinter codePrinter, Path tempFile) throws IOException { try (PrintStream printer = new PrintStream(Files.newOutputStream(tempFile))) { printer.print(getHeaderString(this.getClass())); + printer.println("import com.android.tools.r8.graph.DexItemFactory;"); codePrinter.getImports().forEach(i -> printer.println("import " + i + ";")); printer.println("public final class " + getGeneratedClassName() + " {\n"); + printer.println( + "public static void registerSynthesizedCodeReferences(DexItemFactory factory) {"); + for (String type : new TreeSet<>(codePrinter.getSynthesizedTypes())) { + printer.println("factory.createSynthesizedType(\"" + type + "\");"); + } + printer.println("}"); codePrinter.getMethods().forEach(printer::println); printer.println("}"); }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java new file mode 100644 index 0000000..64f7a72 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java
@@ -0,0 +1,178 @@ +// Copyright (c) 2020, 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.desugar.desugaredlibrary; + +import static org.hamcrest.core.StringContains.containsString; + +import com.android.tools.r8.TestDiagnosticMessages; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.StringUtils; +import java.nio.file.Path; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class InvalidLibraryTest extends DesugaredLibraryTestBase { + + private static Path customLib; + private static Path superclassAsClasspath; + private static final String EXPECTED_RESULT = + StringUtils.lines("1970-01-02T10:17:36.789Z", "1970-01-12T10:20:54.321123456Z"); + private static final String INVALID_RESULT = + StringUtils.lines("1970-01-02T10:17:36.789Z", "1970-01-12T10:20:54.321Z"); + + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + @Parameterized.Parameters(name = "{1}, shrinkDesugaredLibrary: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build()); + } + + public InvalidLibraryTest(boolean shrinkDesugaredLibrary, TestParameters parameters) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + @BeforeClass + public static void compileCustomLib() throws Exception { + customLib = + testForD8(getStaticTemp()) + .addProgramClasses(CustomLibraryClass.class) + .setMinApi(AndroidApiLevel.B) + .compile() + .writeToZip(); + superclassAsClasspath = + testForD8(getStaticTemp()) + .addProgramClasses(SuperLibraryClass.class) + .setMinApi(AndroidApiLevel.B) + .compile() + .writeToZip(); + } + + @Test + public void testProgramSupertype() throws Exception { + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8() + .setMinApi(parameters.getApiLevel()) + .addProgramClasses( + Executor.class, SuperLibraryClass.class, LocalClass.class, LocalClassOverride.class) + .addLibraryClasses(CustomLibraryClass.class) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .addRunClasspathFiles(customLib) + .run(parameters.getRuntime(), Executor.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testClasspathSupertype() throws Exception { + Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters)); + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8() + .setMinApi(parameters.getApiLevel()) + .addProgramClasses(Executor.class, LocalClass.class, LocalClassOverride.class) + .addClasspathClasses(SuperLibraryClass.class) + .addLibraryClasses(CustomLibraryClass.class) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .inspectDiagnosticMessages(this::assertWarningInvalidLibrary) + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .addRunClasspathFiles(customLib, superclassAsClasspath) + .run(parameters.getRuntime(), Executor.class) + // The code requires desugaring to be run correctly, but with the classpath superclass, + // desugaring is incorrectly performed. The code therefore falls-backs to the default + // implementation in Date, which happens to be correct in one case, but incorrect + // in the other case (Warning was raised). + .assertSuccessWithOutput(INVALID_RESULT); + } + + @Test + public void testNullSupertype() throws Exception { + Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters)); + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8() + .setMinApi(parameters.getApiLevel()) + .addProgramClasses(Executor.class, LocalClass.class, LocalClassOverride.class) + .addLibraryClasses(CustomLibraryClass.class) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .inspectDiagnosticMessages(this::assertWarningInvalidLibrary) + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .addRunClasspathFiles(customLib, superclassAsClasspath) + .run(parameters.getRuntime(), Executor.class) + // The code requires desugaring to be run correctly, but with the missing supertype, + // desugaring could not be performed and the code cannot simply run (Warning was raised). + .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError")); + } + + private void assertWarningInvalidLibrary(TestDiagnosticMessages testDiagnosticMessages) { + assert testDiagnosticMessages.getWarnings().stream() + .anyMatch(diagnostic -> diagnostic instanceof InvalidLibrarySuperclassDiagnostic); + } + + static class Executor { + public static void main(String[] args) { + System.out.println(new LocalClass(123456789).toInstant()); + System.out.println(getOverrideAsLocalClass().toInstant()); + } + + public static LocalClass getOverrideAsLocalClass() { + return new LocalClassOverride(987654321); + } + } + + static class SuperLibraryClass extends Date { + public SuperLibraryClass(int nanos) { + super(nanos); + } + } + + static class CustomLibraryClass extends SuperLibraryClass { + public CustomLibraryClass(int nanos) { + super(nanos); + } + } + + static class LocalClass extends CustomLibraryClass { + public LocalClass(int nanos) { + super(nanos); + } + } + + static class LocalClassOverride extends LocalClass { + public LocalClassOverride(int nanos) { + super(nanos); + } + + @Override + public Instant toInstant() { + return super.toInstant().plusNanos(123456); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java deleted file mode 100644 index 403ae3c..0000000 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright (c) 2019, 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.desugar.desugaredlibrary.conversiontests; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.fail; - -import com.android.tools.r8.CompilationFailedException; -import com.android.tools.r8.TestDiagnosticMessages; -import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; -import com.android.tools.r8.utils.AndroidApiLevel; -import java.time.Year; -import org.junit.Test; - -public class APIConversionFinalClassErrorTest extends DesugaredLibraryTestBase { - - @Test - public void testFinalMethod() { - try { - testForD8() - .setMinApi(AndroidApiLevel.B) - .addProgramClasses(Executor.class) - .addLibraryClasses(CustomLibClass.class) - .enableCoreLibraryDesugaring(AndroidApiLevel.B) - .compileWithExpectedDiagnostics(this::assertDiagnosis); - fail("Expected compilation error"); - } catch (CompilationFailedException ignored) { - - } - } - - private void assertDiagnosis(TestDiagnosticMessages d) { - assertEquals( - "Cannot generate a wrapper for final class java.time.Year." - + " Add a custom conversion in the desugared library.", - d.getErrors().get(0).getDiagnosticMessage()); - } - - static class Executor { - - public static void main(String[] args) { - System.out.println(CustomLibClass.call(Year.now())); - } - } - - // This class will be put at compilation time as library and on the runtime class path. - // This class is convenient for easy testing. Each method plays the role of methods in the - // platform APIs for which argument/return values need conversion. - static class CustomLibClass { - - public static long call(Year year) { - return 0L; - } - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java new file mode 100644 index 0000000..f9d1828 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -0,0 +1,96 @@ +// Copyright (c) 2019, 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.desugar.desugaredlibrary.conversiontests; + +import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.core.StringContains.containsString; + +import com.android.tools.r8.TestDiagnosticMessages; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BooleanUtils; +import java.nio.file.Path; +import java.time.Year; +import java.util.List; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class APIConversionFinalClassTest extends DesugaredLibraryTestBase { + + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O; + + private static Path customLib; + + @Parameterized.Parameters(name = "{0}, shrinkDesugaredLibrary: {1}") + public static List<Object[]> data() { + return buildParameters( + getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values()); + } + + public APIConversionFinalClassTest(TestParameters parameters, boolean shrinkDesugaredLibrary) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + @BeforeClass + public static void compileCustomLib() throws Exception { + customLib = + testForD8(getStaticTemp()) + .addProgramClasses(CustomLibClass.class) + .setMinApi(MIN_SUPPORTED) + .compile() + .writeToZip(); + } + + @Test + public void testFinalMethod() throws Exception { + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8() + .setMinApi(AndroidApiLevel.B) + .addProgramClasses(Executor.class) + .addLibraryClasses(CustomLibClass.class) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .inspectDiagnosticMessages(this::assertDiagnosis) + .addRunClasspathFiles(customLib) + .run(parameters.getRuntime(), Executor.class) + .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError")); + } + + private void assertDiagnosis(TestDiagnosticMessages d) { + assertTrue(d.getInfos().get(0).getDiagnosticMessage().contains("libCall")); + } + + static class Executor { + + public static void main(String[] args) { + System.out.println(CustomLibClass.libCall(Year.now())); + } + } + + // This class will be put at compilation time as library and on the runtime class path. + // This class is convenient for easy testing. Each method plays the role of methods in the + // platform APIs for which argument/return values need conversion. + static class CustomLibClass { + + // We use Year because Year is a final class with no custom conversion but Year has been + // unused in the Android library so far. + public static long libCall(Year year) { + return 0L; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringImpossibleTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringImpossibleTest.java new file mode 100644 index 0000000..88ede59 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringImpossibleTest.java
@@ -0,0 +1,128 @@ +// Copyright (c) 2020, 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.desugaring.interfacemethods; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestDiagnosticMessages; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class DefaultInterfaceMethodDesugaringImpossibleTest extends TestBase { + + private static final String EXPECTED = StringUtils.lines("I.m()"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public DefaultInterfaceMethodDesugaringImpossibleTest(TestParameters parameters) { + this.parameters = parameters; + } + + private Collection<Class<?>> getProgramClasses() { + return ImmutableList.of(TestClass.class, I.class); + } + + private Collection<byte[]> getProgramClassData() throws Exception { + return ImmutableList.of( + transformer(A.class) + .setAccessFlags( + A.class.getDeclaredMethod("m"), + flags -> { + assert flags.isPublic(); + flags.unsetPublic(); + flags.setPrivate(); + flags.setStatic(); + }) + .transform()); + } + + @Test + public void testJVM() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getProgramClassData()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + try { + testForD8() + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getProgramClassData()) + .setMinApi(parameters.getApiLevel()) + .compileWithExpectedDiagnostics(this::checkDesugarError) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + assertTrue(parameters.canUseDefaultAndStaticInterfaceMethods()); + } catch (CompilationFailedException e) { + assertFalse(parameters.canUseDefaultAndStaticInterfaceMethods()); + } + } + + @Test + public void testR8() throws Exception { + try { + testForR8(parameters.getBackend()) + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getProgramClassData()) + .addKeepAllClassesRule() + .setMinApi(parameters.getApiLevel()) + .compileWithExpectedDiagnostics(this::checkDesugarError) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + assertTrue(parameters.canUseDefaultAndStaticInterfaceMethods()); + } catch (CompilationFailedException e) { + assertFalse(parameters.canUseDefaultAndStaticInterfaceMethods()); + } + } + + private void checkDesugarError(TestDiagnosticMessages diagnostics) { + if (!parameters.canUseDefaultAndStaticInterfaceMethods()) { + diagnostics.assertErrorMessageThatMatches(containsString("forwarding method that conflicts")); + } + } + + static class TestClass { + + public static void main(String[] args) { + I a = new A(); + a.m(); + } + } + + interface I { + + default void m() { + System.out.println("I.m()"); + } + } + + static class A implements I { + + public /* will be: private static */ void m() { + System.out.println("A.m()"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java new file mode 100644 index 0000000..a8c5c6f --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java
@@ -0,0 +1,174 @@ +// Copyright (c) 2020, 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.desugaring.interfacemethods; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRunResult; +import com.android.tools.r8.ToolHelper.DexVm; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.objectweb.asm.Opcodes; + +@RunWith(Parameterized.class) +public class DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest + extends TestBase { + + private static final String EXPECTED = StringUtils.lines("I.m()"); + + private final TestParameters parameters; + private final boolean invalidInvoke; + + @Parameterized.Parameters(name = "{0}, invalid:{1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values()); + } + + public DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest( + TestParameters parameters, boolean invalidInvoke) { + this.parameters = parameters; + this.invalidInvoke = invalidInvoke; + } + + private Collection<Class<?>> getProgramClasses() { + return ImmutableList.of(I.class, A.class, C.class); + } + + private Collection<byte[]> getProgramClassData() throws Exception { + return ImmutableList.of( + transformer(B.class) + .setAccessFlags( + B.class.getDeclaredMethod("m"), + flags -> { + assert flags.isPublic(); + flags.unsetPublic(); + flags.setPrivate(); + flags.setStatic(); + }) + .transform(), + transformer(TestClass.class) + .transformMethodInsnInMethod( + "main", + (opcode, owner, name, descriptor, isInterface, continuation) -> { + if (invalidInvoke && opcode == Opcodes.INVOKEVIRTUAL) { + assertEquals("m", name); + continuation.apply( + opcode, + DescriptorUtils.getBinaryNameFromJavaType(C.class.getTypeName()), + name, + descriptor, + isInterface); + } else { + continuation.apply(opcode, owner, name, descriptor, isInterface); + } + }) + .transform()); + } + + @Test + public void testRuntime() throws Exception { + checkResult( + testForRuntime(parameters) + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getProgramClassData()) + .run(parameters.getRuntime(), TestClass.class)); + } + + @Test + public void testR8() throws Exception { + checkResult( + testForR8(parameters.getBackend()) + .addProgramClasses(getProgramClasses()) + .addProgramClassFileData(getProgramClassData()) + .addKeepAllClassesRule() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class)); + } + + private void checkResult(TestRunResult<?> result) { + // Invalid invoke case is where the invoke-virtual targets C.m. + if (invalidInvoke) { + // Up to 4.4 the exception for targeting a private static was ICCE. + if (isDexOlderThanOrEqual(Version.V4_4_4)) { + result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); + return; + } + // Then up to 6.0 the runtime just ignores privates leading to incorrectly hitting I.m + if (isDexOlderThanOrEqual(Version.V6_0_1)) { + result.assertSuccessWithOutput(EXPECTED); + return; + } + if (!unexpectedArtFailure() && !parameters.canUseDefaultAndStaticInterfaceMethods()) { + assert false : "Dead code until future ART behavior change. See b/152199517"; + // Desugaring will insert a forwarding bridge which will hide the "invalid invoke" case. + // Thus, a future ART runtime that does not have the invalid IAE for the private override + // will end up calling the forward method to I.m. + result.assertSuccessWithOutput(EXPECTED); + } + // The expected behavior is IAE since the resolved method is private. + result.assertFailureWithErrorThatThrows(IllegalAccessError.class); + return; + } + + // The non-invalid case is where the invoke-virtual targets A.m. + + // In the successful case ART since 6.0 incorrectly throws IAE due to the private override. + if (unexpectedArtFailure()) { + result.assertFailureWithErrorThatThrows(IllegalAccessError.class); + return; + } + + // The expected behavior is that the resolution of A.m will resolve and hit I.m. + result.assertSuccessWithOutput(EXPECTED); + } + + private boolean isDexOlderThanOrEqual(Version version) { + return parameters.isDexRuntime() + && parameters.getRuntime().asDex().getVm().getVersion().isOlderThanOrEqual(version); + } + + private boolean unexpectedArtFailure() { + return parameters.isDexRuntime() + && parameters.getRuntime().asDex().getVm().isNewerThan(DexVm.ART_6_0_1_HOST); + } + + static class TestClass { + + public static void main(String[] args) { + // Same as DefaultInterfaceMethodDesugaringWithStaticResolutionTest, but targets a class A. + A /* or C */ a = new C(); + a.m(); + } + } + + interface I { + + default void m() { + System.out.println("I.m()"); + } + } + + static class A implements I {} + + static class B extends A { + + public /* will be: private static */ void m() { + System.out.println("B.m()"); + } + } + + static class C extends B implements I {} +}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java index bdd7960..c649c8d 100644 --- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java
@@ -1,13 +1,11 @@ package com.android.tools.r8.desugaring.interfacemethods; -import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assume.assumeTrue; -import com.android.tools.r8.D8TestRunResult; -import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -15,6 +13,8 @@ @RunWith(Parameterized.class) public class DefaultInterfaceMethodDesugaringWithStaticResolutionTest extends TestBase { + private static final String EXPECTED = StringUtils.lines("I.m()"); + private final TestParameters parameters; @Parameterized.Parameters(name = "{0}") @@ -32,43 +32,29 @@ testForJvm() .addTestClasspath() .run(parameters.getRuntime(), TestClass.class) - .assertSuccessWithOutputLines("I.m()"); + .assertSuccessWithOutput(EXPECTED); } @Test public void testD8() throws Exception { assumeTrue(parameters.isDexRuntime()); - D8TestRunResult result = - testForD8() - .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class) - .setMinApi(parameters.getApiLevel()) - .compile() - .run(parameters.getRuntime(), TestClass.class); - // TODO(b/152163087): Should always succeed with "I.m()". - if (parameters.canUseDefaultAndStaticInterfaceMethods()) { - result.assertSuccessWithOutputLines("I.m()"); - } else { - result.assertFailureWithErrorThatMatches( - containsString(AbstractMethodError.class.getTypeName())); - } + testForD8() + .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); } @Test public void testR8() throws Exception { - R8TestRunResult result = - testForR8(parameters.getBackend()) - .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class) - .addKeepAllClassesRule() - .setMinApi(parameters.getApiLevel()) - .compile() - .run(parameters.getRuntime(), TestClass.class); - // TODO(b/152163087): Should always succeed with "I.m()". - if (parameters.canUseDefaultAndStaticInterfaceMethods()) { - result.assertSuccessWithOutputLines("I.m()"); - } else { - result.assertFailureWithErrorThatMatches( - containsString(AbstractMethodError.class.getTypeName())); - } + testForR8(parameters.getBackend()) + .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class) + .addKeepAllClassesRule() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java new file mode 100644 index 0000000..b4e1ef6 --- /dev/null +++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
@@ -0,0 +1,94 @@ +// Copyright (c) 2020, 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.enumunboxing; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.R8TestCompileResult; +import com.android.tools.r8.R8TestRunResult; +import com.android.tools.r8.TestParameters; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ValueOfEnumUnboxingTest extends EnumUnboxingTestBase { + + private static final Class<?>[] SUCCESSES = { + EnumValueOf.class, + }; + + private final TestParameters parameters; + private final boolean enumValueOptimization; + private final KeepRule enumKeepRules; + + @Parameters(name = "{0} valueOpt: {1} keep: {2}") + public static List<Object[]> data() { + return enumUnboxingTestParameters(); + } + + public ValueOfEnumUnboxingTest( + TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) { + this.parameters = parameters; + this.enumValueOptimization = enumValueOptimization; + this.enumKeepRules = enumKeepRules; + } + + @Test + public void testEnumUnboxing() throws Exception { + R8TestCompileResult compile = + testForR8(parameters.getBackend()) + .addInnerClasses(ValueOfEnumUnboxingTest.class) + .addKeepMainRules(SUCCESSES) + .enableNeverClassInliningAnnotations() + .addKeepRules(enumKeepRules.getKeepRule()) + .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization)) + .allowDiagnosticInfoMessages() + .setMinApi(parameters.getApiLevel()) + .compile(); + for (Class<?> success : SUCCESSES) { + R8TestRunResult run = + compile + .inspectDiagnosticMessages( + m -> + assertEnumIsUnboxed( + success.getDeclaredClasses()[0], success.getSimpleName(), m)) + .run(parameters.getRuntime(), success) + .assertSuccess(); + assertLines2By2Correct(run.getStdOut()); + } + } + + static class EnumValueOf { + + @NeverClassInline + enum MyEnum { + A, + B + } + + public static void main(String[] args) { + System.out.println(Enum.valueOf(EnumValueOf.MyEnum.class, "A").ordinal()); + System.out.println(0); + System.out.println(Enum.valueOf(EnumValueOf.MyEnum.class, "B").ordinal()); + System.out.println(1); + try { + Enum.valueOf(EnumValueOf.MyEnum.class, "C"); + } catch (IllegalArgumentException argException) { + System.out.println(argException.getMessage()); + System.out.println( + "No enum constant" + + " com.android.tools.r8.enumunboxing.ValueOfEnumUnboxingTest.EnumValueOf.MyEnum.C"); + } + try { + Enum.valueOf(EnumValueOf.MyEnum.class, null); + } catch (NullPointerException npe) { + System.out.println(npe.getMessage()); + System.out.println("Name is null"); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java index 33321eb..4d39642 100644 --- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java +++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -8,16 +8,21 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.GenericSignature.ClassSignature; import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; +import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter; import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature; import com.android.tools.r8.graph.GenericSignature.Parser; import com.android.tools.r8.graph.GenericSignature.ReturnType; import com.android.tools.r8.graph.GenericSignature.TypeSignature; +import com.android.tools.r8.graph.GenericSignatureTestClassA.I; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.DescriptorUtils; @@ -28,11 +33,24 @@ import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import org.junit.Ignore; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class GenericSignatureTest extends TestBase { + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public GenericSignatureTest(TestParameters parameters) {} + @Test + @Ignore("b/152709234") public void test() throws Exception { AndroidApp app = testForD8() @@ -62,6 +80,8 @@ assertThat(cy, isPresent()); ClassSubject cyy = inspector.clazz(GenericSignatureTestClassCYY.class); assertThat(cyy, isPresent()); + ClassSubject i = inspector.clazz(I.class); + assertThat(cyy, isPresent()); DexEncodedMethod method; @@ -80,13 +100,22 @@ // Testing ClassSignature // - // class CYY<T extends A<T>.Y> extends CY<T> + // class <T:GenericSignatureTestClassA<T>.Y>CYY<T extends A<T>.Y> extends CY<T> DexClass clazz = cyy.getDexClass(); assertNotNull(clazz); classSignature = Parser.toClassSignature(clazz, appView); assertNotNull(classSignature); - // TODO(b/129925954): test formal type parameter of CYY + assertEquals(1, classSignature.formalTypeParameters.size()); + FormalTypeParameter formalTypeParameter = classSignature.formalTypeParameters.get(0); + assertEquals("T", formalTypeParameter.name); + assertNull(formalTypeParameter.interfaceBounds); + assertTrue(formalTypeParameter.classBound.isClassTypeSignature()); + ClassTypeSignature classBoundSignature = formalTypeParameter.classBound.asClassTypeSignature(); + assertEquals(y.getDexClass().type, classBoundSignature.innerTypeSignature.type); + assertEquals(1, classBoundSignature.typeArguments.size()); + assertEquals( + "T", classBoundSignature.typeArguments.get(0).asTypeVariableSignature().typeVariable); assertTrue(classSignature.superInterfaceSignatures.isEmpty()); classTypeSignature = classSignature.superClassSignature; @@ -126,6 +155,18 @@ methodTypeSignature = Parser.toMethodTypeSignature(method, appView); assertNotNull(methodTypeSignature); + assertEquals(1, methodTypeSignature.formalTypeParameters.size()); + FormalTypeParameter methodFormalParameter = methodTypeSignature.formalTypeParameters.get(0); + assertTrue(methodFormalParameter.classBound.isClassTypeSignature()); + assertEquals( + y.getDexClass().getType(), + methodFormalParameter.classBound.asClassTypeSignature().innerTypeSignature.type); + assertNotNull(methodFormalParameter.interfaceBounds); + assertEquals(1, methodFormalParameter.interfaceBounds.size()); + FieldTypeSignature interfaceBound = methodFormalParameter.interfaceBounds.get(0); + assertTrue(interfaceBound.isClassTypeSignature()); + assertEquals(i.getDexClass().getType(), interfaceBound.asClassTypeSignature().type); + // return type: A$Y$YY returnType = methodTypeSignature.returnType(); assertFalse(returnType.isVoidDescriptor()); @@ -257,6 +298,9 @@ // class GenericSignatureTestClassA<T> { + + interface I {} + class Y { class YY {} @@ -264,7 +308,7 @@ class ZZ<TT> extends YY { public YY yy; - YY newYY(GenericSignatureTestClassB... bs) { + <R extends I> YY newYY(GenericSignatureTestClassB... bs) { return new YY(); }
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java index 07db6bd..2438ab2 100644 --- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -83,12 +83,9 @@ DexEncodedMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of()); assertFalse( - appInfo - .resolveMethod(method.method.holder, method.method) - .getSingleTarget() - .isVirtualMethod()); - assertNull(appInfo.lookupDirectTarget(method.method, method.method.holder)); - assertNotNull(appInfo.lookupStaticTarget(method.method, method.method.holder)); + appInfo.resolveMethod(method.holder(), method.method).getSingleTarget().isVirtualMethod()); + assertNull(appInfo.lookupDirectTarget(method.method, method.holder())); + assertNotNull(appInfo.lookupStaticTarget(method.method, method.holder())); if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) { // Dalvik rejects at verification time instead of producing the
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java index 741be70..20ed6dc 100644 --- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java +++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -94,7 +94,7 @@ .asLookupResultSuccess(); assertNotNull(lookupResult); assertFalse(lookupResult.hasLambdaTargets()); - if (appInfo().subtypes(method.method.holder).stream() + if (appInfo().subtypes(method.holder()).stream() .allMatch(t -> appInfo().definitionFor(t).isInterface())) { Counter counter = new Counter(); lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java b/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java index 0d31dca..f191b3b 100644 --- a/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java +++ b/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
@@ -153,7 +153,7 @@ IRCode code = simpleCode(); InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(1)); Instruction current = it.previous(); - assertTrue(current.isConstNumber() && current.outValue().getType().isReferenceType()); + assertTrue(current.isConstNumber() && current.getOutType().isReferenceType()); it.next(); current = it.next(); assertTrue(current.isArrayGet());
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java index a644df9..bdeda3c 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
@@ -104,7 +104,7 @@ code, instruction -> instruction.isConstNumber() && instruction.asConstNumber().getRawValue() != 0); - assertEquals(getFloat(), constNumberInstruction.outValue().getType()); + assertEquals(getFloat(), constNumberInstruction.getOutType()); } }; }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java index e3b40cb..e421309 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java
@@ -98,7 +98,7 @@ for (Instruction instruction : code.instructions()) { if (instruction.isConstNumber()) { ConstNumber constNumberInstruction = instruction.asConstNumber(); - assertEquals(expectedType, constNumberInstruction.outValue().getType()); + assertEquals(expectedType, constNumberInstruction.getOutType()); } }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java index 0f40b4b..9fb2f9b 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java
@@ -111,7 +111,7 @@ private static Consumer<IRCode> testInspector(TypeElement expectedType) { return code -> { ConstNumber constNumberInstruction = getMatchingInstruction(code, Instruction::isConstNumber); - assertEquals(expectedType, constNumberInstruction.outValue().getType()); + assertEquals(expectedType, constNumberInstruction.getOutType()); }; } }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java index f6ad83a..961827a 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java
@@ -116,7 +116,7 @@ for (Instruction instruction : code.instructions()) { if (instruction.isConstNumber()) { ConstNumber constNumberInstruction = instruction.asConstNumber(); - assertEquals(expectedType, constNumberInstruction.outValue().getType()); + assertEquals(expectedType, constNumberInstruction.getOutType()); } }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java index 2a2ad92..b5997bf 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java
@@ -43,8 +43,8 @@ private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { // TODO(b/139246447): should avoid visiting A#<init>, which is trivial, default init! - assert encodedMethod.method.holder.toSourceString().endsWith("A") - && encodedMethod.toSourceString().contains("<init>") + assert encodedMethod.holder().toSourceString().endsWith("A") + && encodedMethod.toSourceString().contains("<init>") : "Unexpected revisit: " + encodedMethod.toSourceString(); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java index 4890c6a..54a3b8e 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
@@ -64,7 +64,7 @@ assert encodedMethod.method.name.toString().equals("m") : "Unexpected revisit: " + encodedMethod.toSourceString(); CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); - if (encodedMethod.method.holder.toSourceString().endsWith("$C")) { + if (encodedMethod.holder().toSourceString().endsWith("$C")) { assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); } else { assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java index a0a436d..96b85ec 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -61,7 +61,7 @@ assert encodedMethod.method.name.toString().equals("m") : "Unexpected revisit: " + encodedMethod.toSourceString(); CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); - if (encodedMethod.method.holder.toSourceString().endsWith("$C")) { + if (encodedMethod.holder().toSourceString().endsWith("$C")) { assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); } else { assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java index ab5d763..14ff937 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -65,7 +65,7 @@ CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1); - if (encodedMethod.method.holder.toSourceString().endsWith("$A")) { + if (encodedMethod.holder().toSourceString().endsWith("$A")) { assert abstractValue.isSingleStringValue() && abstractValue.asSingleStringValue().getDexString().toString().equals("nul"); } else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java index 8cc1e12..204f41b 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -66,7 +66,7 @@ CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isDefinitelyNotNull(); - if (encodedMethod.method.holder.toSourceString().endsWith("$A")) { + if (encodedMethod.holder().toSourceString().endsWith("$A")) { assert upperBoundType.isClassType() && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Sub1"); } else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java index ad7b3ae..4882c32 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -66,7 +66,7 @@ TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isNullable(); assert upperBoundType.isClassType() - && upperBoundType.asClassType().getClassType().equals(encodedMethod.method.holder); + && upperBoundType.asClassType().getClassType().equals(encodedMethod.holder()); } else { assert methodName.equals("test"); assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java index 0551286..0009024 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -64,10 +64,10 @@ TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isClassType() && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A"); - if (encodedMethod.method.holder.toSourceString().endsWith("$A")) { + if (encodedMethod.holder().toSourceString().endsWith("$A")) { assert upperBoundType.isDefinitelyNotNull(); } else { - assert encodedMethod.method.holder.toSourceString().endsWith("$B"); + assert encodedMethod.holder().toSourceString().endsWith("$B"); assert upperBoundType.isNullable(); } }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFieldLoadEliminationMeetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFieldLoadEliminationMeetTest.java new file mode 100644 index 0000000..776df6b --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFieldLoadEliminationMeetTest.java
@@ -0,0 +1,64 @@ +package com.android.tools.r8.ir.optimize.redundantfieldloadelimination; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import java.io.PrintStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class RedundantFieldLoadEliminationMeetTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public RedundantFieldLoadEliminationMeetTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(RedundantFieldLoadEliminationMeetTest.class) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + + MethodSubject mainMethodSubject = testClassSubject.mainMethod(); + assertThat(mainMethodSubject, isPresent()); + assertEquals( + 1, mainMethodSubject.streamInstructions().filter(InstructionSubject::isStaticGet).count()); + } + + static class TestClass { + + public static void main(String[] args) { + boolean unknown = System.currentTimeMillis() > 0; + PrintStream out = System.out; + String message = unknown ? "Hello world!" : "Unexpected"; + System.out.println(message); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java new file mode 100644 index 0000000..329e574 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
@@ -0,0 +1,147 @@ +package com.android.tools.r8.ir.optimize.redundantfieldloadelimination; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NeverPropagateValue; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ir.optimize.redundantfieldloadelimination.RedundantFinalStaticFieldLoadAfterStoreTest.A; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.FoundFieldSubject; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class RedundantFinalInstanceFieldLoadAfterStoreTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public RedundantFinalInstanceFieldLoadAfterStoreTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(RedundantFinalInstanceFieldLoadAfterStoreTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .enableMemberValuePropagationAnnotations() + .enableNeverClassInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("0", "42", "42", "42"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject aClassSubject = inspector.clazz(A.class); + assertThat(aClassSubject, isPresent()); + + FieldSubject fFieldSubject = aClassSubject.uniqueFieldWithName("f"); + assertThat(fFieldSubject, isPresent()); + + MethodSubject initMethodSubject = aClassSubject.init(); + assertThat(initMethodSubject, isPresent()); + assertEquals( + 0, + countInstanceGetInstructions( + initMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject())); + + MethodSubject mMethodSubject = aClassSubject.uniqueMethodWithName("m"); + assertThat(mMethodSubject, isPresent()); + assertEquals( + 2, + countInstanceGetInstructions( + mMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject())); + } + + private long countInstanceGetInstructions( + FoundMethodSubject methodSubject, FoundFieldSubject fieldSubject) { + return methodSubject + .streamInstructions() + .filter(InstructionSubject::isInstanceGet) + .map(InstructionSubject::getField) + .filter(fieldSubject.getField().field::equals) + .count(); + } + + static class TestClass { + + public static void main(String[] args) { + new A(); + } + } + + @NeverClassInline + static class A { + + @NeverPropagateValue final long f; + + static volatile boolean read; + static volatile boolean initialized; + + A() { + fork(); + waitUntilRead(); + f = System.currentTimeMillis() > 0 ? 42 : 0; + initialized = true; + killNonFinalActiveFields(); + System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized. + killNonFinalActiveFields(); + System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized. + } + + @NeverInline + void m() { + System.out.println(f); + read = true; + waitUntilInitialized(); + System.out.println(f); // Not redundant, since `f` is not guaranteed to be initialized. + } + + @NeverInline + void fork() { + new Thread(this::m).start(); + } + + @NeverInline + void killNonFinalActiveFields() { + if (System.currentTimeMillis() < 0) { + System.out.println(this); + } + } + + @NeverInline + void waitUntilInitialized() { + while (!initialized) { + Thread.yield(); + } + } + + @NeverInline + void waitUntilRead() { + while (!read) { + Thread.yield(); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java new file mode 100644 index 0000000..fe3d79a --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
@@ -0,0 +1,118 @@ +package com.android.tools.r8.ir.optimize.redundantfieldloadelimination; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NeverPropagateValue; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.FoundFieldSubject; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class RedundantFinalStaticFieldLoadAfterStoreTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public RedundantFinalStaticFieldLoadAfterStoreTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(RedundantFinalStaticFieldLoadAfterStoreTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .enableMemberValuePropagationAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("42", "42", "42", "42"); + } + + private void inspect(CodeInspector inspector) { + ClassSubject aClassSubject = inspector.clazz(A.class); + assertThat(aClassSubject, isPresent()); + + FieldSubject fFieldSubject = aClassSubject.uniqueFieldWithName("f"); + assertThat(fFieldSubject, isPresent()); + + MethodSubject initMethodSubject = aClassSubject.clinit(); + assertThat(initMethodSubject, isPresent()); + assertEquals( + 0, + countStaticGetInstructions( + initMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject())); + + MethodSubject mMethodSubject = aClassSubject.uniqueMethodWithName("m"); + assertThat(mMethodSubject, isPresent()); + // TODO(b/152196923): Should be 0. + assertEquals( + 2, + countStaticGetInstructions( + mMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject())); + } + + private long countStaticGetInstructions( + FoundMethodSubject methodSubject, FoundFieldSubject fieldSubject) { + return methodSubject + .streamInstructions() + .filter(InstructionSubject::isStaticGet) + .map(InstructionSubject::getField) + .filter(fieldSubject.getField().field::equals) + .count(); + } + + static class TestClass { + + public static void main(String[] args) { + A.m(); + } + } + + static class A { + + @NeverPropagateValue static final long f; + + static { + f = System.currentTimeMillis() > 0 ? 42 : 0; + killNonFinalActiveFields(); + System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized. + killNonFinalActiveFields(); + System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized. + } + + @NeverInline + static void m() { + System.out.println(A.f); + killNonFinalActiveFields(); + System.out.println(A.f); // Redundant, since `f` is guaranteed to be initialized. + } + + @NeverInline + static void killNonFinalActiveFields() { + if (System.currentTimeMillis() < 0) { + System.out.println(A.class); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java new file mode 100644 index 0000000..74dc3f4 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java
@@ -0,0 +1,85 @@ +package com.android.tools.r8.ir.optimize.staticizer; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +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 com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class CompanionClassWithNewInstanceUserTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public CompanionClassWithNewInstanceUserTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(CompanionClassWithNewInstanceUserTest.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + private void inspect(CodeInspector inspector) { + if (parameters.isCfRuntime()) { + // Class staticizer is disabled when generating class files. + assertThat(inspector.clazz(Companion.class), isPresent()); + } else { + // The companion class has been removed. + assertThat(inspector.clazz(Companion.class), not(isPresent())); + + // The companion method has been moved to the companion host class. + ClassSubject hostClassSubject = inspector.clazz(CompanionHost.class); + assertThat(hostClassSubject, isPresent()); + assertThat(hostClassSubject.uniqueMethodWithName("method"), isPresent()); + } + } + + static class TestClass { + + public static void main(String[] args) { + Companion companion = CompanionHost.COMPANION; + } + } + + static class CompanionHost { + + static final Companion COMPANION; + + static { + Companion companion = new Companion(); + COMPANION = companion; + companion.method(); + } + } + + static class Companion { + + @NeverInline + public void method() { + System.out.println("Hello world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderTest.java new file mode 100644 index 0000000..0dcda2d --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderTest.java
@@ -0,0 +1,60 @@ +package com.android.tools.r8.ir.optimize.string; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class UnusedStringBuilderTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + } + + public UnusedStringBuilderTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(UnusedStringBuilderTest.class) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccess(); + } + + private void inspect(CodeInspector inspector) { + ClassSubject testClassSubject = inspector.clazz(TestClass.class); + assertThat(testClassSubject, isPresent()); + + MethodSubject methodSubject = testClassSubject.mainMethod(); + assertThat(methodSubject, isPresent()); + assertTrue(methodSubject.streamInstructions().allMatch(InstructionSubject::isReturnVoid)); + } + + static class TestClass { + + public static void main(String[] args) { + String.valueOf(new StringBuilder().append("x=").append(42)); + new StringBuilder().append("x=").append(42).toString(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java index 6087795..6fb984f 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -53,7 +53,6 @@ // The Util class is there, but its instance methods have been inlined. ClassSubject utilClass = inspector.clazz("class_staticizer.Util"); assertThat(utilClass, isPresent()); - AtomicInteger nonStaticMethodCount = new AtomicInteger(); assertTrue( utilClass.allMethods().stream() .filter(Predicates.not(FoundMethodSubject::isStatic))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java index 7514ff5..9d75aaa 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -36,7 +36,12 @@ public class MetadataRewriteInCompanionTest extends KotlinMetadataTestBase { private static final String EXPECTED = StringUtils.lines( - "B.Companion::foo", "B.Companion::foo", "B.Companion::foo", "B.Companion::foo"); + "B.Companion::foo", + "B.Companion::foo", + "B.Companion::foo", + "B.Companion::foo", + "B.Companion::bar", + "Hello World!"); private final TestParameters parameters; @@ -125,8 +130,8 @@ // Property in companion with @JvmField is defined in the host class, without accessors. .addKeepRules("-keepclassmembers class **.B { *** elt2; }") .addKeepRules("-keep class **.I { <methods>; }") - // Keep getters for B$Companion.(eltN|foo) which will be referenced at the app. - .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }") + // Keep getters/setters for B$Companion.(eltN|foo) which will be referenced at the app. + .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); *** set*(...); }") // Keep the companion instance in the B class .addKeepRules("-keepclassmembers class **.B { *** Companion; }") // Keep the name of companion class @@ -239,5 +244,9 @@ MethodSubject fooGetter = companion.uniqueMethodWithName("getFoo"); assertThat(fooGetter, isPresent()); assertThat(fooGetter, not(isRenamed())); + + MethodSubject barSetter = companion.uniqueMethodWithName("setBar"); + assertThat(barSetter, isPresent()); + assertThat(barSetter, not(isRenamed())); } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java index 07fbdd9..3d3d935 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -5,13 +5,11 @@ import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertNotEquals; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; -import com.android.tools.r8.ToolHelper.ProcessResult; +import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.Collection; @@ -25,7 +23,28 @@ @RunWith(Parameterized.class) public class MetadataRewriteInTypeArgumentsTest extends KotlinMetadataTestBase { private static final String EXPECTED = - StringUtils.lines("42", "1", "42", "42", "1", "42", "42", "42", "1", "42"); + StringUtils.lines( + "Hello World!", + "42", + "1", + "42", + "42", + "1", + "42", + "42", + "42", + "1", + "42", + "1", + "42", + "42", + "42", + "42", + "42", + "1", + "2", + "7", + "42"); private final TestParameters parameters; @@ -50,6 +69,7 @@ Path typeAliasLibJar = kotlinc(KOTLINC, targetVersion) .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib")) + .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib_minified")) .compile(); jarMap.put(targetVersion, typeAliasLibJar); } @@ -74,26 +94,37 @@ } @Test - public void testMetadataInTypeAlias_renamed() throws Exception { + public void testMetadataInTypeAlias_keepAll() throws Exception { Path libJar = testForR8(parameters.getBackend()) .addProgramFiles(jarMap.get(targetVersion)) .addKeepAllClassesRule() + .addKeepAttributes( + ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS, + ProguardKeepAttributes.SIGNATURE, + ProguardKeepAttributes.INNER_CLASSES, + ProguardKeepAttributes.ENCLOSING_METHOD) .compile() - // TODO(b/151925520): Add inspections when program compiles + // TODO(b/151925520): Add inspections when program compiles correctly. + // TODO(mkroghj): Also inspect the renaming of lib_minified + // (not now, but when program compiles correctly). .writeToZip(); - ProcessResult kotlinTestCompileResult = + Path mainJar = kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main")) - .setOutputPath(temp.newFolder().toPath()) - // TODO(b/151925520): update to just .compile() once fixed. - .compileRaw(); - // TODO(b/151925520): should be able to compile! - assertNotEquals(0, kotlinTestCompileResult.exitCode); - assertThat( - kotlinTestCompileResult.stderr, - containsString("no type arguments expected for constructor Invariant()")); + .compile(); + + // TODO(b/152306391): Reified type-parameters are not flagged correctly. + testForJvm() + .addProgramFiles(mainJar) + .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addRunClasspathFiles(libJar) + .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt") + .assertFailureWithErrorThatMatches( + containsString( + "This function has a reified type parameter and thus can only be inlined at" + + " compilation time, not called directly")); } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt index 403021e..ef4e23f 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt
@@ -10,4 +10,7 @@ B.elt1.doStuff() B.elt2.doStuff() println(B.foo) + println(B.bar) + B.bar = "Hello World!"; + println(B.bar) }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt index 8e69219..54db448 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
@@ -24,5 +24,10 @@ val elt2: Super = B() val foo: String get() = "B.Companion::foo" + var bar : String = "B.Companion::bar" + get() = field + set(value) { + field = value + } } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt index 7fc2b69..c8f4a72 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
@@ -8,7 +8,10 @@ import com.android.tools.r8.kotlin.metadata.typeargument_lib.ContraVariant import com.android.tools.r8.kotlin.metadata.typeargument_lib.Invariant import com.android.tools.r8.kotlin.metadata.typeargument_lib.SomeClass +import com.android.tools.r8.kotlin.metadata.typeargument_lib.asList +import com.android.tools.r8.kotlin.metadata.typeargument_lib.asObfuscatedClass import com.android.tools.r8.kotlin.metadata.typeargument_lib.unBoxAndBox +import com.android.tools.r8.kotlin.metadata.typeargument_lib.unboxAndPutInBox import com.android.tools.r8.kotlin.metadata.typeargument_lib.update class SomeSubClass(val x : Int) : SomeClass(), Comparable<SomeClass> { @@ -24,7 +27,7 @@ fun testInvariant() { val subtype1 = SomeSubClass(42) val subtype2 = SomeSubClass(1) - val inv = Invariant<SomeSubClass>() + val inv = Invariant<SomeSubClass, String>("Hello World!") println(inv.classGenerics(subtype1).x) println(inv.funGenerics(subtype2).x) println(inv.funGenericsWithUpperBound(subtype1).x) @@ -43,6 +46,12 @@ fun testExtension() { println(CoVariant(42).unBoxAndBox().t) println(CoVariant(1).update(42).t) + println(CoVariant(1).unboxAndPutInBox(CoVariant(42)).t) + println(CoVariant(42).asList().t.get(0)) + val asList = CoVariant(42).asList(1, 2) + println(asList.t.get(0)) + println(asList.t.get(1)) + println(CoVariant(7).asObfuscatedClass().t.get(0).get(0).x) } fun main() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt index 7c8b6cb..38234b4 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
@@ -6,7 +6,11 @@ open class SomeClass -class Invariant<T> { +class Invariant<T, C> { + + constructor(someValue : C) { + println(someValue) + } fun classGenerics(t : T) : T { return t @@ -52,7 +56,6 @@ } } - fun <T> CoVariant<T>.unBoxAndBox() : CoVariant<T> { return CoVariant(this.t) } @@ -61,3 +64,24 @@ println(this.t) return CoVariant(t) } + +fun <T> CoVariant<T>.unboxAndPutInBox(box : CoVariant<T>) : CoVariant<T> { + println(this.t) + println(box.t) + return CoVariant(box.t) +} + +inline fun <reified T> CoVariant<T>.asList() : CoVariant<Array<T>> { + println(this.t) + return CoVariant(arrayOf(this.t)) +} + +inline fun <reified T> CoVariant<T>.asList(vararg ts : T) : CoVariant<Array<out T>> { + println(this.t) + return CoVariant(ts) +} + +fun <T> CoVariant<T>.asObfuscatedClass() : CoVariant<Array<Array<ClassThatWillBeObfuscated>>> { + println(this.t) + return CoVariant(arrayOf(arrayOf(ClassThatWillBeObfuscated(42)))) +} \ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt new file mode 100644 index 0000000..78c3742 --- /dev/null +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt
@@ -0,0 +1,3 @@ +package com.android.tools.r8.kotlin.metadata.typeargument_lib + +class ClassThatWillBeObfuscated(val x : Int)
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java index e081a44..b1ae543 100644 --- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java
@@ -4,11 +4,8 @@ package com.android.tools.r8.memberrebinding; -import static org.hamcrest.core.StringContains.containsString; - import com.android.tools.r8.NeverInline; import com.android.tools.r8.NeverMerge; -import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -35,36 +32,28 @@ @Test public void test() throws Exception { - R8TestRunResult result = - testForR8(parameters.getBackend()) - .addProgramClasses(A.class, TestClass.class) - .addProgramClassFileData( - transformer(B.class) - .removeMethods( - (access, name, descriptor, signature, exceptions) -> { - if (name.equals("foo")) { - assert MethodAccessFlags.fromCfAccessFlags(access, false).isSynthetic(); - return true; - } - return false; - }) - .transform()) - .addInnerClasses(MemberRebindingConflictTestClasses.class) - .addKeepMainRule(TestClass.class) - .enableInliningAnnotations() - .enableMergeAnnotations() - .enableNeverClassInliningAnnotations() - .setMinApi(parameters.getApiLevel()) - .compile() - .run(parameters.getRuntime(), TestClass.class); - - if (parameters.isDexRuntime() - && parameters.getRuntime().asDex().getVm().getVersion().isDalvik()) { - result.assertSuccessWithOutputLines("foo", "bar", "foo", "baz"); - } else { - result.assertFailureWithErrorThatMatches( - containsString(IllegalAccessError.class.getTypeName())); - } + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, TestClass.class) + .addProgramClassFileData( + transformer(B.class) + .removeMethods( + (access, name, descriptor, signature, exceptions) -> { + if (name.equals("foo")) { + assert MethodAccessFlags.fromCfAccessFlags(access, false).isSynthetic(); + return true; + } + return false; + }) + .transform()) + .addInnerClasses(MemberRebindingConflictTestClasses.class) + .addKeepMainRule(TestClass.class) + .enableInliningAnnotations() + .enableMergeAnnotations() + .enableNeverClassInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("foo", "bar", "foo", "baz"); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java b/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java index 32c754e..0556439 100644 --- a/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java +++ b/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
@@ -276,12 +276,8 @@ // compilation (R8) will eliminate field loads on non-volatile fields. assertEquals(1, countIget(mfOnA.getMethod().getCode().asDexCode(), fOnA.getField().field)); assertEquals(1, countSget(msfOnA.getMethod().getCode().asDexCode(), sfOnA.getField().field)); - // TODO(111380066). This could be 2 in stead of 4, but right now the optimization tracks the - // combined set of fields for all successors, and for synchronized code all blocks have - // exceptional edges for ensuring monitor exit causing the active load to be invalidated for - // both normal and exceptional successors. - assertEquals(4, - countIget(mfWithMonitorOnA.getMethod().getCode().asDexCode(), fOnA.getField().field)); + assertEquals( + 2, countIget(mfWithMonitorOnA.getMethod().getCode().asDexCode(), fOnA.getField().field)); // For fields on other class both separate compilation (D8) and whole program // compilation (R8) will differ in the eliminated field loads of non-volatile fields.
diff --git a/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java b/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java new file mode 100644 index 0000000..7c8aa34 --- /dev/null +++ b/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java
@@ -0,0 +1,74 @@ +// Copyright (c) 2020, 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.regress.b151964517; + +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; + +@RunWith(Parameterized.class) +public class ConstStringWithMonitorTest extends TestBase { + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimes().build(); + } + + public ConstStringWithMonitorTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void regress() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(ConstStringWithMonitorTest.class) + .noMinification() + .allowAccessModification() + .addKeepMainRule(TestClass.class) + .compile() + .runDex2Oat(parameters.getRuntime()); + // TODO(b/151964517): Should pass verification with assertNoVerificationErrors() + } + + public static class TestClass { + public static void main(String[] args) { + try { + System.out.println(FooBar.tryIt()); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + } + + public static class Value { + public final String value; + + public Value(String value) { + this.value = value; + } + } + + public static class FooBar { + private static Object mLock = new Object(); + + public static String tryIt() { + Value value = synchronizedMethod("foobar"); + return value.value; + } + + private static Value synchronizedMethod(String s) { + synchronized (mLock) { + if (System.currentTimeMillis() < 2) { + s.length(); + throw new IllegalStateException("wrong" + s); + } + return new Value(s); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java index 0d89583..7bd9cc9 100644 --- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -186,8 +186,7 @@ Assert.assertNull(singleVirtualTarget); } else { Assert.assertNotNull(singleVirtualTarget); - Assert.assertEquals( - toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.method.holder); + Assert.assertEquals(toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.holder()); } }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java index a9dc70f..18cbb66 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -133,7 +133,7 @@ DexEncodedMethod targetSuper = resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo); if (inSameNest) { - assertEquals(definingClassDefinition.type, targetSpecial.method.holder); + assertEquals(definingClassDefinition.type, targetSpecial.holder()); assertEquals(targetSpecial, targetSuper); } else { assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java index 490b7dc..58575d8 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -109,7 +109,7 @@ DexEncodedMethod targetSuper = resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo); if (inSameNest) { - assertEquals(definingClassDefinition.type, targetSpecial.method.holder); + assertEquals(definingClassDefinition.type, targetSpecial.holder()); assertEquals(targetSpecial, targetSuper); } else { assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java index 8e21be0..79e8f76 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -142,7 +142,7 @@ DexEncodedMethod targetSuper = resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo); if (inSameNest && symbolicReferenceIsDefiningType) { - assertEquals(definingClassDefinition.type, targetSpecial.method.holder); + assertEquals(definingClassDefinition.type, targetSpecial.holder()); assertEquals(targetSpecial, targetSuper); } else { assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java index 49426c4..db75b6e 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -108,7 +108,7 @@ // Verify that looking up the dispatch target returns the defining method. DexEncodedMethod targetSpecial = resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo); - assertEquals(definingClassDefinition.type, targetSpecial.method.holder); + assertEquals(definingClassDefinition.type, targetSpecial.holder()); DexEncodedMethod targetSuper = resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java index 005f292..b6023ce 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
@@ -48,7 +48,7 @@ DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); // Currently R8 will resolve to L::f as that is the first in the topological search. // Resolution may return any of the matches, so it is valid if this expectation changes. - assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java index 0c3ed4c..2068ef1 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -51,7 +51,7 @@ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java index 42f7600..64908c4 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -51,7 +51,7 @@ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - assertEquals(R.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java index c29509d..4841f77 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -54,7 +54,7 @@ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java index e7c5ab8..6059ea1 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -54,7 +54,7 @@ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - assertEquals(R.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java index 3d7071d..433575f 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -55,8 +55,7 @@ Set<String> holders = new HashSet<>(); resolutionResult .asFailedResolution() - .forEachFailureDependency( - target -> holders.add(target.method.holder.toSourceString())); + .forEachFailureDependency(target -> holders.add(target.holder().toSourceString())); assertEquals(ImmutableSet.of(L.class.getTypeName(), R.class.getTypeName()), holders); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java index b5c2018..e04908b 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -45,7 +45,7 @@ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java index 867cfe9..7472e89 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -45,7 +45,7 @@ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); - assertEquals(R.class.getTypeName(), resolutionTarget.method.holder.toSourceString()); + assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString()); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java index b2a4ce2..5966b9d 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -56,8 +56,7 @@ Set<String> holders = new HashSet<>(); resolutionResult .asFailedResolution() - .forEachFailureDependency( - m -> holders.add(m.method.holder.toSourceString())); + .forEachFailureDependency(m -> holders.add(m.holder().toSourceString())); assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders); }
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java index 91f0c4d..ae4947a 100644 --- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -85,6 +85,7 @@ assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11); assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1); assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), 1); + assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), 1); } else { assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("simple")); assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("local")); @@ -96,7 +97,6 @@ } assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType")); - assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField")); assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi")); } @@ -129,6 +129,7 @@ assertNameReplacedWithConst(clazz.uniqueMethodWithName("inlined"), "TWO"); assertNameReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN"); assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO"); + assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), "TWO"); } else { assertNameWasNotReplaced(clazz.uniqueMethodWithName("simple")); assertNameWasNotReplaced(clazz.uniqueMethodWithName("local")); @@ -141,7 +142,6 @@ // TODO(jakew) this should be allowed! assertNameWasNotReplaced(clazz.uniqueMethodWithName("libraryType")); - assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField")); assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi")); }
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java index 2ecc9eb..5dd5ec0 100644 --- a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java +++ b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
@@ -63,6 +63,7 @@ return Number.DOWN.name(); } + @AssumeMayHaveSideEffects @NeverInline private static String nonValueStaticField() { return Number.DEFAULT.name();
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java index 534b16c..e1eee2c 100644 --- a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java +++ b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
@@ -73,6 +73,7 @@ return Number.DOWN.ordinal(); } + @AssumeMayHaveSideEffects @NeverInline private static long nonValueStaticField() { return Number.DEFAULT.ordinal();
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java new file mode 100644 index 0000000..fe72a94 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java
@@ -0,0 +1,108 @@ +// Copyright (c) 2020, 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.shaking.assumenosideeffects; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class B152492625 extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public B152492625(TestParameters parameters) { + this.parameters = parameters; + } + + private void noCallToWait(CodeInspector inspector) { + ClassSubject classSubject = inspector.clazz(TestClass.class); + assertThat(classSubject, isPresent()); + classSubject.forAllMethods( + foundMethodSubject -> + foundMethodSubject + .instructions(InstructionSubject::isInvokeVirtual) + .forEach( + instructionSubject -> { + Assert.assertNotEquals( + "wait", instructionSubject.getMethod().name.toString()); + })); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(B152492625.class) + .addKeepMainRule(TestClass.class) + .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }") + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::noCallToWait) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello, world"); + } + + @Test + public void testProguardNotRemovingWait() throws Exception { + Assume.assumeTrue(parameters.isCfRuntime()); + + testForProguard() + .addInnerClasses(B152492625.class) + .addKeepMainRule(TestClass.class) + .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }") + .addKeepRules("-dontwarn " + B152492625.class.getTypeName()) + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertFailureWithErrorThatThrows(IllegalMonitorStateException.class); + } + + @Test + public void testProguardRemovingWait() throws Exception { + Assume.assumeTrue(parameters.isCfRuntime()); + + testForProguard() + .addInnerClasses(B152492625.class) + .addKeepMainRule(TestClass.class) + .addKeepRules("-assumenosideeffects class java.lang.Object { void wait(); }") + .addKeepRules("-dontwarn " + B152492625.class.getTypeName()) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::noCallToWait) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello, world"); + } + + static class TestClass { + + public void m() throws Exception { + System.out.println("Hello, world"); + // test fails if wait is not removed. + wait(); + } + + public static void main(String[] args) throws Exception { + new TestClass().m(); + } + } + + static class B {} +}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java index 2387e7c..de762ac 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.utils.StringUtils; -import java.io.ByteArrayOutputStream; import java.nio.file.Path; import java.nio.file.Paths; import org.junit.Test; @@ -46,7 +45,6 @@ @Test public void test() throws Throwable { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); testForR8(Backend.CF) .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR) .addKeepRuleFiles(MAIN_KEEP)
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java index 98f6b1e..3bbea86 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -102,8 +102,14 @@ @Override public void describeMismatchSafely(final Subject subject, Description description) { - description - .appendText(type(subject) + " ").appendValue(name(subject)).appendText(" was not"); + if (subject instanceof ClassSubject || subject instanceof MemberSubject) { + description + .appendText(type(subject) + " ") + .appendValue(name(subject)) + .appendText(" was not"); + } else { + description.appendText(type(subject) + " ").appendText(" was not found"); + } } }; }
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py index 6d6f0fe..9182539 100755 --- a/tools/run_on_as_app.py +++ b/tools/run_on_as_app.py
@@ -737,6 +737,8 @@ '-Pandroid.enableR8.fullMode=' + str(IsR8FullMode(shrinker)).lower()] if app.has_lint_task: args.extend(['-x', app_module + ':lintVital' + app_flavor]) + if options.bot: + args.extend(['--console=plain', '--info']) # Warm up gradle if pre_runs > 0. For posterity we generate the same sequence # as the benchmarking at https://github.com/madsager/santa-tracker-android.
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py index f3a0923..ee2b100 100755 --- a/tools/update_prebuilds_in_android.py +++ b/tools/update_prebuilds_in_android.py
@@ -8,12 +8,8 @@ import os from shutil import copyfile import sys -import tempfile import utils -import urllib - -BUILD_ROOT = "https://storage.googleapis.com/r8-releases/raw/" -MASTER_BUILD_ROOT = "%smaster/" % BUILD_ROOT +import archive JAR_TARGETS_MAP = { 'full': [ @@ -66,8 +62,6 @@ print ('WARNING: Not copying ' + src + ' -> ' + dest + ', as' + dest + ' does not exist already') - - def copy_jar_targets(root, target_root, jar_targets, maps): srcs = map((lambda t: t[0] + '.jar'), jar_targets) dests = map((lambda t: t[1] + '.jar'), jar_targets) @@ -77,19 +71,19 @@ copy_targets(root, target_root, OTHER_TARGETS, OTHER_TARGETS) def download_hash(root, commit_hash, target): - url = MASTER_BUILD_ROOT + commit_hash + '/' + target - download_target(root, url, target) + download_target(root, target, commit_hash, True) def download_version(root, version, target): - url = BUILD_ROOT + version + '/' + target - download_target(root, url, target) + download_target(root, target, version, False) -def download_target(root, url, target): +def download_target(root, target, hash_or_version, is_hash): download_path = os.path.join(root, target) + url = archive.GetUploadDestination( + hash_or_version, + target, + is_hash) print 'Downloading: ' + url + ' -> ' + download_path - result = urllib.urlretrieve(url, download_path) - if 'X-GUploader-Request-Result: success' not in str(result[1]): - raise IOError('Failed to download ' + url) + utils.download_file_from_cloud_storage(url, download_path) def main_download(hash, maps, targets, target_root, version): jar_targets = JAR_TARGETS_MAP[targets]