Merge commit '23d77afb55650c59e5cf224c33c0fbc1cfee76fb' into dev-release
diff --git a/build.gradle b/build.gradle index 92e940a..8d1eb6b 100644 --- a/build.gradle +++ b/build.gradle
@@ -610,6 +610,7 @@ options.errorprone.enabled = true options.errorprone.disableAllChecks = true options.errorprone.check('ClassCanBeStatic', CheckSeverity.ERROR) + options.errorprone.check('CollectionIncompatibleType', CheckSeverity.ERROR) options.errorprone.check('OperatorPrecedence', CheckSeverity.ERROR) options.errorprone.check('RemoveUnusedImports', CheckSeverity.ERROR) options.errorprone.check('MissingOverride', CheckSeverity.ERROR) @@ -695,25 +696,25 @@ } } -task repackageDeps(type: ShadowJar) { +task repackageDepsNew(type: ShadowJar) { configurations = [project.configurations.runtimeClasspath] mergeServiceFiles(it) exclude { it.getRelativePath().getPathString() == "module-info.class" } exclude { it.getRelativePath().getPathString().startsWith("META-INF/maven/") } - baseName 'deps' + baseName 'deps_all' } -task repackageSources(type: ShadowJar) { +task repackageSourcesNew(type: ShadowJar) { from sourceSets.main.output mergeServiceFiles(it) - baseName 'sources' + baseName 'sources_main' } -task repackageSources11(type: ShadowJar) { +task repackageSources11New(type: ShadowJar) { dependsOn compileMainWithJava11 from file(java11ClassFiles) mergeServiceFiles(it) - baseName 'sources11' + baseName 'sources_main_11' } def r8CreateTask(name, baseNameName, sources, includeSwissArmyKnife) { @@ -771,24 +772,26 @@ } task r8WithDeps { - dependsOn repackageSources - dependsOn repackageDeps + dependsOn repackageSourcesNew + dependsOn repackageDepsNew + inputs.files ([repackageSourcesNew.outputs, repackageDepsNew.outputs]) def r8Task = r8CreateTask( 'WithDeps', 'r8_with_deps', - repackageSources.outputs.files + repackageDeps.outputs.files, + repackageSourcesNew.outputs.files + repackageDepsNew.outputs.files, true) dependsOn r8Task outputs.files r8Task.outputs.files } task r8WithDeps11 { - dependsOn repackageSources11 - dependsOn repackageDeps + dependsOn repackageSources11New + dependsOn repackageDepsNew + inputs.files ([repackageSources11New.outputs, repackageDepsNew.outputs]) def r8Task = r8CreateTask( 'WithDeps11', 'r8_with_deps_11', - repackageSources11.outputs.files + repackageDeps.outputs.files, + repackageSources11New.outputs.files + repackageDepsNew.outputs.files, true) dependsOn r8Task outputs.files r8Task.outputs.files @@ -797,21 +800,24 @@ task r8WithRelocatedDeps { def output = "${buildDir}/libs/r8_with_relocated_deps.jar" dependsOn r8RelocateTask(r8WithDeps, output) + inputs.files r8WithDeps.outputs.files outputs.file output } task r8WithRelocatedDeps11 { def output = "${buildDir}/libs/r8_with_relocated_deps_11.jar" dependsOn r8RelocateTask(r8WithDeps11, output) + inputs.files r8WithDeps11.outputs.files outputs.file output } task r8WithoutDeps { - dependsOn repackageSources + dependsOn repackageSourcesNew + inputs.files repackageSourcesNew.outputs def r8Task = r8CreateTask( 'WithoutDeps', 'r8_without_deps', - repackageSources.outputs.files, + repackageSourcesNew.outputs.files, true) dependsOn r8Task outputs.files r8Task.outputs.files @@ -828,22 +834,24 @@ } task r8NoManifestWithoutDeps { - dependsOn repackageSources + dependsOn repackageSourcesNew + inputs.files repackageSourcesNew.outputs def r8Task = r8CreateTask( 'NoManifestWithoutDeps', 'r8_no_manifest_without_deps', - repackageSources.outputs.files, + repackageSourcesNew.outputs.files, false) dependsOn r8Task outputs.files r8Task.outputs.files } task r8NoManifestWithDeps { - dependsOn repackageSources + dependsOn repackageSourcesNew + inputs.files ([repackageSourcesNew.outputs, repackageDepsNew.outputs]) def r8Task = r8CreateTask( 'NoManifestWithDeps', 'r8_no_manifest_with_deps', - repackageSources.outputs.files + repackageDeps.outputs.files, + repackageSourcesNew.outputs.files + repackageDepsNew.outputs.files, false) dependsOn r8Task outputs.files r8Task.outputs.files @@ -852,6 +860,7 @@ task r8NoManifestWithRelocatedDeps { def output = "${buildDir}/libs/r8_no_manifest_with_relocated_deps.jar" dependsOn r8RelocateTask(r8NoManifestWithDeps, output) + inputs.files r8NoManifestWithDeps.outputs.files outputs.file output } @@ -925,7 +934,7 @@ def output = "$buildDir/libs/r8tests.jar" outputs.file output workingDir = projectDir - inputs.files ([testJarSources.outputs, r8WithDeps.outputs]) + inputs.files (testJarSources.outputs.files + r8WithDeps.outputs.files) commandLine baseR8CommandLine([ "relocator", "--input", @@ -942,20 +951,21 @@ // TODO(b/154785341): We should remove this. standardOutput new FileOutputStream(r8LibGeneratedKeepRulesPath) } - dependsOn r8WithRelocatedDeps - dependsOn r8NoManifestWithDeps + // Depend on r8WithDeps for running baseR8CommandLine. + dependsOn r8WithDeps + dependsOn r8NoManifestWithRelocatedDeps dependsOn testJar dependsOn downloadOpenJDKrt inputs.files ([ - r8WithRelocatedDeps.outputs, - r8NoManifestWithDeps.outputs, + r8WithDeps.outputs, + r8NoManifestWithRelocatedDeps.outputs, testJar.outputs]) outputs.file r8LibGeneratedKeepRulesPath commandLine baseR8CommandLine([ "printuses", "--keeprules-allowobfuscation", "third_party/openjdk/openjdk-rt-1.8/rt.jar", - r8NoManifestWithDeps.outputs.files[0], + r8NoManifestWithRelocatedDeps.outputs.files[0], testJar.outputs.files[0]]) workingDir = projectDir } @@ -974,6 +984,7 @@ r8NoManifestWithRelocatedDeps, r8LibPath, ).dependsOn(generateR8LibKeepRules) + inputs.files r8NoManifestWithRelocatedDeps.outputs.files outputs.file r8LibPath } @@ -984,8 +995,9 @@ r8NoManifestWithoutDeps, r8LibExludeDepsPath, "--release", - repackageDeps.outputs.files - ).dependsOn(repackageDeps) + repackageDepsNew.outputs.files + ).dependsOn(repackageDepsNew) + inputs.files ([r8NoManifestWithoutDeps.outputs, repackageDepsNew.outputs]) outputs.file r8LibExludeDepsPath }
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java index e8c6bb1..3cd04b1 100644 --- a/src/main/java/com/android/tools/r8/D8.java +++ b/src/main/java/com/android/tools/r8/D8.java
@@ -14,10 +14,10 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexApplication; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.InitClassLens; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis; import com.android.tools.r8.inspector.internal.InspectorImpl; import com.android.tools.r8.ir.conversion.IRConverter; @@ -183,7 +183,7 @@ ThreadUtils.processItems( appInfo.classes(), clazz -> { - DexEncodedMethod classInitializer = clazz.getClassInitializer(); + ProgramMethod classInitializer = clazz.getProgramClassInitializer(); if (classInitializer != null) { analysis.processNewlyLiveMethod(classInitializer); }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 164c572..5c815bc 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -375,7 +375,7 @@ internal.debug = getMode() == CompilationMode.DEBUG; internal.programConsumer = getProgramConsumer(); if (internal.programConsumer instanceof ClassFileConsumer) { - internal.enableCfInterfaceMethodDesugaring = true; + internal.cfToCfDesugar = true; } internal.mainDexListConsumer = getMainDexListConsumer(); internal.minimalMainDex = internal.debug || minimalMainDex;
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java index cf9684c..dea3bb8 100644 --- a/src/main/java/com/android/tools/r8/L8.java +++ b/src/main/java/com/android/tools/r8/L8.java
@@ -87,11 +87,15 @@ ExecutorService executorService) throws CompilationFailedException { try { + assert !options.cfToCfDesugar; ExceptionUtils.withD8CompilationHandler( options.reporter, () -> { + options.cfToCfDesugar = true; desugar(app, options, executorService); + options.cfToCfDesugar = false; }); + assert !options.cfToCfDesugar; if (shrink) { R8.run(r8Command); } else { @@ -105,6 +109,7 @@ private static void desugar( AndroidApp inputApp, InternalOptions options, ExecutorService executor) throws IOException { Timing timing = Timing.create("L8 desugaring", options); + assert options.cfToCfDesugar; try { // Disable global optimizations. options.disableGlobalOptimizations();
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java index 0b4f28b..9a26221 100644 --- a/src/main/java/com/android/tools/r8/PrintUses.java +++ b/src/main/java/com/android/tools/r8/PrintUses.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.graph.DexProgramClass.asProgramClassOrNull; + import com.android.tools.r8.dex.ApplicationReader; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.DexAnnotation; @@ -19,6 +21,7 @@ import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.DexValue.DexValueArray; import com.android.tools.r8.graph.DirectMappedDexApplication; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.desugar.LambdaDescriptor; @@ -244,18 +247,18 @@ registerTypeReference(field.field.type); } - private void registerMethod(DexEncodedMethod method) { + private void registerMethod(ProgramMethod method) { DexEncodedMethod superTarget = appInfo - .resolveMethod(method.holder(), method.method) + .resolveMethod(method.getHolder(), method.getReference()) .lookupInvokeSpecialTarget(context, appInfo); if (superTarget != null) { addMethod(superTarget.method); } - for (DexType type : method.method.proto.parameters.values) { + for (DexType type : method.getDefinition().parameters().values) { registerTypeReference(type); } - for (DexAnnotation annotation : method.annotations().annotations) { + for (DexAnnotation annotation : method.getDefinition().annotations().annotations) { if (annotation.annotation.type == appInfo.dexItemFactory().annotationThrows) { DexValueArray dexValues = annotation.annotation.elements[0].value.asDexValueArray(); for (DexValue dexValType : dexValues.getValues()) { @@ -263,7 +266,7 @@ } } } - registerTypeReference(method.method.proto.returnType); + registerTypeReference(method.getDefinition().returnType()); method.registerCodeReferences(this); } @@ -289,13 +292,11 @@ List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo); if (directInterfaces != null) { for (DexType directInterface : directInterfaces) { - DexClass clazz = appInfo.definitionFor(directInterface); + DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(directInterface)); if (clazz != null) { - for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) { - if (encodedMethod.method.name.equals(callSite.methodName)) { - registerMethod(encodedMethod); - } - } + clazz.forEachProgramVirtualMethodMatching( + definition -> definition.getReference().name.equals(callSite.methodName), + this::registerMethod); } } } @@ -360,14 +361,14 @@ private void analyze() { UseCollector useCollector = new UseCollector(appInfo.dexItemFactory()); - for (DexProgramClass dexProgramClass : application.classes()) { - useCollector.setContext(dexProgramClass); - useCollector.registerSuperType(dexProgramClass, dexProgramClass.superType); - for (DexType implementsType : dexProgramClass.interfaces.values) { - useCollector.registerSuperType(dexProgramClass, implementsType); + for (DexProgramClass clazz : application.classes()) { + useCollector.setContext(clazz); + useCollector.registerSuperType(clazz, clazz.superType); + for (DexType implementsType : clazz.interfaces.values) { + useCollector.registerSuperType(clazz, implementsType); } - dexProgramClass.forEachMethod(useCollector::registerMethod); - dexProgramClass.forEachField(useCollector::registerField); + clazz.forEachProgramMethod(useCollector::registerMethod); + clazz.forEachField(useCollector::registerField); } }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 30201ce..51b0aaf 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -32,7 +32,6 @@ import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis; import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis; 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; @@ -52,7 +51,6 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.jar.CfApplicationWriter; import com.android.tools.r8.kotlin.KotlinInfoCollector; -import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.Minifier; import com.android.tools.r8.naming.NamingLens; @@ -682,11 +680,6 @@ shrinker.removeDeadBuilderReferencesFromDynamicMethods( appViewWithLiveness, executorService, timing)); - if (Log.ENABLED && Log.isLoggingEnabledFor(GeneratedExtensionRegistryShrinker.class)) { - appView.withGeneratedExtensionRegistryShrinker( - GeneratedExtensionRegistryShrinker::logRemainingProtoExtensionFields); - } - if (options.isShrinking()) { // Mark dead proto extensions fields as neither being read nor written. This step must // run prior to the tree pruner.
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java index 59b3c8b..a2a814a 100644 --- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java +++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -112,28 +112,30 @@ } @Override - void writeMethod(DexEncodedMethod method, PrintStream ps) { + void writeMethod(ProgramMethod method, PrintStream ps) { + DexEncodedMethod definition = method.getDefinition(); ClassNameMapper naming = application.getProguardMap(); - String methodName = naming != null - ? naming.originalSignatureOf(method.method).name - : method.method.name.toString(); + String methodName = + naming != null + ? naming.originalSignatureOf(method.getReference()).name + : method.getReference().name.toString(); ps.println("#"); ps.println("# Method: '" + methodName + "':"); - writeAnnotations(null, method.annotations(), ps); - ps.println("# " + method.accessFlags); + writeAnnotations(null, definition.annotations(), ps); + ps.println("# " + definition.accessFlags); ps.println("#"); ps.println(); - Code code = method.getCode(); + Code code = definition.getCode(); if (code != null) { if (writeIR) { writeIR(method, ps); } else { - ps.println(code.toString(method, naming)); + ps.println(code.toString(definition, naming)); } } } - private void writeIR(DexEncodedMethod method, PrintStream ps) { + private void writeIR(ProgramMethod method, PrintStream ps) { CfgPrinter printer = new CfgPrinter(); new IRConverter(appInfo, options, timing, printer) .processMethod(
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 93ca81d..b68b839 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,6 +5,8 @@ import static com.android.tools.r8.graph.DexCode.FAKE_THIS_PREFIX; import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX; +import static org.objectweb.asm.Opcodes.V1_5; +import static org.objectweb.asm.Opcodes.V1_6; import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame; @@ -211,8 +213,8 @@ } for (CfInstruction instruction : instructions) { if (instruction instanceof CfFrame - && (classFileVersion <= 49 - || (classFileVersion == 50 && !options.shouldKeepStackMapTable()))) { + && (classFileVersion <= V1_5 + || (classFileVersion == V1_6 && !options.shouldKeepStackMapTable()))) { continue; } instruction.write(visitor, initClassLens, namingLens); @@ -287,15 +289,14 @@ } @Override - public IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin) { - return internalBuildPossiblyWithLocals( - encodedMethod, encodedMethod, appView, null, null, origin, null); + public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { + return internalBuildPossiblyWithLocals(method, method, appView, null, null, origin, null); } @Override public IRCode buildInliningIR( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator valueNumberGenerator, Position callerPosition, @@ -304,29 +305,23 @@ assert valueNumberGenerator != null; assert callerPosition != null; return internalBuildPossiblyWithLocals( - context, - encodedMethod, - appView, - valueNumberGenerator, - callerPosition, - origin, - methodProcessor); + context, method, appView, valueNumberGenerator, callerPosition, origin, methodProcessor); } // First build entry. Will either strip locals or build with locals. private IRCode internalBuildPossiblyWithLocals( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator generator, Position callerPosition, Origin origin, MethodProcessor methodProcessor) { - if (!encodedMethod.keepLocals(appView.options())) { + if (!method.getDefinition().keepLocals(appView.options())) { return internalBuild( Collections.emptyList(), context, - encodedMethod, + method, appView, generator, callerPosition, @@ -334,14 +329,14 @@ methodProcessor); } else { return internalBuildWithLocals( - context, encodedMethod, appView, generator, callerPosition, origin, methodProcessor); + context, method, appView, generator, callerPosition, origin, methodProcessor); } } // When building with locals, on invalid debug info, retry build without locals info. private IRCode internalBuildWithLocals( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator generator, Position callerPosition, @@ -351,18 +346,18 @@ return internalBuild( Collections.unmodifiableList(localVariables), context, - encodedMethod, + method, appView, generator, callerPosition, origin, methodProcessor); } catch (InvalidDebugInfoException e) { - appView.options().warningInvalidDebugInfo(encodedMethod, origin, e); + appView.options().warningInvalidDebugInfo(method, origin, e); return internalBuild( Collections.emptyList(), context, - encodedMethod, + method, appView, generator, callerPosition, @@ -374,8 +369,8 @@ // Inner-most subroutine for building. Must only be called by the two internalBuildXYZ above. private IRCode internalBuild( List<LocalVariableInfo> localVariables, - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator generator, Position callerPosition, @@ -385,22 +380,32 @@ new CfSourceCode( this, localVariables, - encodedMethod, - appView.graphLense().getOriginalMethodSignature(encodedMethod.method), + method, + appView.graphLense().getOriginalMethodSignature(method.getReference()), callerPosition, origin, appView); - IRBuilder builder = methodProcessor == null ? - IRBuilder.create(encodedMethod, appView, source, origin) : - IRBuilder - .createForInlining(encodedMethod, appView, source, origin, methodProcessor, generator); + IRBuilder builder = + methodProcessor == null + ? IRBuilder.create(method, appView, source, origin) + : IRBuilder.createForInlining( + method, appView, source, origin, methodProcessor, generator); return builder.build(context); } @Override - public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry registry) { for (CfInstruction instruction : instructions) { - instruction.registerUse(registry, method.holder()); + instruction.registerUse(registry, method.getHolderType()); } for (CfTryCatch tryCatch : tryCatchRanges) { for (DexType guard : tryCatch.guards) { @@ -505,7 +510,7 @@ } public ConstraintWithTarget computeInliningConstraint( - DexEncodedMethod encodedMethod, + ProgramMethod method, AppView<AppInfoWithLiveness> appView, GraphLense graphLense, DexType invocationContext) { @@ -521,7 +526,7 @@ // Model a synchronized method as having a monitor instruction. ConstraintWithTarget constraint = - encodedMethod.accessFlags.isSynchronized() + method.getDefinition().isSynchronized() ? inliningConstraints.forMonitor() : ConstraintWithTarget.ALWAYS;
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java new file mode 100644 index 0000000..9d2a941 --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
@@ -0,0 +1,41 @@ +// 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.graph; + +import com.android.tools.r8.logging.Log; + +/** Type representing a method definition on the classpath and its holder. */ +public final class ClasspathMethod extends DexClassAndMethod { + + public ClasspathMethod(DexClasspathClass holder, DexEncodedMethod method) { + super(holder, method); + } + + public void registerCodeReferencesForDesugaring(UseRegistry registry) { + Code code = getDefinition().getCode(); + if (code != null) { + if (Log.ENABLED) { + Log.verbose(getClass(), "Registering definitions reachable from `%s`.", this); + } + code.registerCodeReferencesForDesugaring(this, registry); + } + } + + @Override + public boolean isClasspathMethod() { + return true; + } + + @Override + public ClasspathMethod asClasspathMethod() { + return this; + } + + @Override + public DexClasspathClass getHolder() { + DexClass holder = super.getHolder(); + assert holder.isClasspathClass(); + return holder.asClasspathClass(); + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java index 8e81c8e..9f384ca 100644 --- a/src/main/java/com/android/tools/r8/graph/Code.java +++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -17,11 +17,11 @@ public abstract class Code extends CachedHashValueDexItem { - public abstract IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin); + public abstract IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin); public IRCode buildInliningIR( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator valueNumberGenerator, Position callerPosition, @@ -31,7 +31,10 @@ + getClass().getCanonicalName()); } - public abstract void registerCodeReferences(DexEncodedMethod method, UseRegistry registry); + public abstract void registerCodeReferences(ProgramMethod method, UseRegistry registry); + + public abstract void registerCodeReferencesForDesugaring( + ClasspathMethod method, UseRegistry registry); public void registerArgumentReferences(DexEncodedMethod method, ArgumentUse registry) { throw new Unreachable();
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java index a81e59c..c83010d 100644 --- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java +++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -88,7 +88,7 @@ clazz.forEachField(field -> writeField(field, ps)); writeFieldsFooter(clazz, ps); writeMethodsHeader(clazz, ps); - clazz.forEachMethod(method -> writeMethod(method, ps)); + clazz.forEachProgramMethod(method -> writeMethod(method, ps)); writeMethodsFooter(clazz, ps); writeClassFooter(clazz, ps); } @@ -111,7 +111,7 @@ // Do nothing. } - abstract void writeMethod(DexEncodedMethod method, PrintStream ps); + abstract void writeMethod(ProgramMethod method, PrintStream ps); void writeMethodsFooter(DexProgramClass clazz, PrintStream ps) { // Do nothing.
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 444459f..5bcb25c 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -511,6 +511,10 @@ return null; } + public boolean isPrivate() { + return accessFlags.isPrivate(); + } + public boolean isPublic() { return accessFlags.isPublic(); } @@ -822,7 +826,7 @@ public boolean hasStaticSynchronizedMethods() { for (DexEncodedMethod encodedMethod : directMethods()) { - if (encodedMethod.accessFlags.isStatic() && encodedMethod.accessFlags.isSynchronized()) { + if (encodedMethod.isStatic() && encodedMethod.isSynchronized()) { return true; } }
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 f77d55b..c6c2f07 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.graph; import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.origin.Origin; public class DexClassAndMethod implements LookupTarget { @@ -12,7 +13,10 @@ private final DexEncodedMethod method; DexClassAndMethod(DexClass holder, DexEncodedMethod method) { + assert holder != null; + assert method != null; assert holder.type == method.holder(); + assert holder.isProgramClass() == (this instanceof ProgramMethod); this.holder = holder; this.method = method; } @@ -20,13 +24,16 @@ public static DexClassAndMethod create(DexClass holder, DexEncodedMethod method) { if (holder.isProgramClass()) { return new ProgramMethod(holder.asProgramClass(), method); - } else { + } else if (holder.isLibraryClass()) { return new DexClassAndMethod(holder, method); + } else { + assert holder.isClasspathClass(); + return new ClasspathMethod(holder.asClasspathClass(), method); } } @Override - public boolean equals(Object obj) { + public boolean equals(Object object) { throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndMethod"); } @@ -49,10 +56,30 @@ return holder; } + public DexType getHolderType() { + return holder.type; + } + public DexEncodedMethod getDefinition() { return method; } + public DexMethod getReference() { + return method.method; + } + + public Origin getOrigin() { + return holder.origin; + } + + public boolean isClasspathMethod() { + return false; + } + + public ClasspathMethod asClasspathMethod() { + return null; + } + public boolean isProgramMethod() { return false; } @@ -60,4 +87,13 @@ public ProgramMethod asProgramMethod() { return null; } + + public String toSourceString() { + return method.method.toSourceString(); + } + + @Override + public String toString() { + return toSourceString(); + } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java index e9e47b6..b4eeb9e 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -214,20 +214,20 @@ } @Override - public IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin) { + public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { DexSourceCode source = new DexSourceCode( this, - encodedMethod, - appView.graphLense().getOriginalMethodSignature(encodedMethod.method), + method, + appView.graphLense().getOriginalMethodSignature(method.getReference()), null); - return IRBuilder.create(encodedMethod,appView,source,origin).build(encodedMethod); + return IRBuilder.create(method, appView, source, origin).build(method); } @Override public IRCode buildInliningIR( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator valueNumberGenerator, Position callerPosition, @@ -236,15 +236,25 @@ DexSourceCode source = new DexSourceCode( this, - encodedMethod, - appView.graphLense().getOriginalMethodSignature(encodedMethod.method), + method, + appView.graphLense().getOriginalMethodSignature(method.getReference()), callerPosition); return IRBuilder.createForInlining( - encodedMethod, appView, source, origin, methodProcessor, valueNumberGenerator).build(context); + method, appView, source, origin, methodProcessor, valueNumberGenerator) + .build(context); } @Override - public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry registry) { for (Instruction insn : instructions) { insn.registerUse(registry); }
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 43536b9..c589524 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -207,7 +207,7 @@ && singleValue.asSingleFieldValue().getField() == field) { return null; } - if (singleValue.isMaterializableInContext(appView, code.method().holder())) { + if (singleValue.isMaterializableInContext(appView, code.context())) { 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 816a515..b81c877 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -9,6 +9,7 @@ import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE; import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS; import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE; +import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO; import com.android.tools.r8.cf.code.CfConstNull; @@ -37,11 +38,8 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Invoke; -import com.android.tools.r8.ir.code.Position; -import com.android.tools.r8.ir.code.ValueNumberGenerator; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.DexBuilder; -import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.Inliner.Reason; @@ -57,12 +55,10 @@ import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode; import com.android.tools.r8.ir.synthetic.SynthesizedCode; import com.android.tools.r8.kotlin.KotlinMethodLevelInfo; -import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.naming.MemberNaming.Signature; import com.android.tools.r8.naming.NamingLens; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AnnotationRemover; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; @@ -257,10 +253,18 @@ assert parameterAnnotationsList != null; } + public DexMethod getReference() { + return method; + } + public DexTypeList parameters() { return method.proto.parameters; } + public DexProto proto() { + return method.proto; + } + public DexType returnType() { return method.proto.returnType; } @@ -298,6 +302,20 @@ return false; } + public ProgramMethod asProgramMethod(DexDefinitionSupplier definitions) { + assert method.holder.isClassType(); + DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(method)); + if (clazz != null) { + return new ProgramMethod(clazz, this); + } + return null; + } + + public static ProgramMethod asProgramMethodOrNull( + DexEncodedMethod method, DexDefinitionSupplier definitions) { + return method != null ? method.asProgramMethod(definitions) : null; + } + public boolean isProcessed() { checkIfObsolete(); return compilationState != CompilationState.NOT_PROCESSED; @@ -315,10 +333,22 @@ return accessFlags.isFinal(); } + public boolean isNative() { + return accessFlags.isNative(); + } + + public boolean isPrivate() { + return accessFlags.isPrivate(); + } + public boolean isPublic() { return accessFlags.isPublic(); } + public boolean isSynchronized() { + return accessFlags.isSynchronized(); + } + public boolean isInitializer() { checkIfObsolete(); return isInstanceInitializer() || isClassInitializer(); @@ -448,13 +478,13 @@ } public boolean isInliningCandidate( - DexEncodedMethod container, + ProgramMethod container, Reason inliningReason, AppInfoWithClassHierarchy appInfo, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { checkIfObsolete(); return isInliningCandidate( - container.holder(), inliningReason, appInfo, whyAreYouNotInliningReporter); + container.getHolderType(), inliningReason, appInfo, whyAreYouNotInliningReporter); } public boolean isInliningCandidate( @@ -552,23 +582,6 @@ compilationState = CompilationState.NOT_PROCESSED; } - public IRCode buildIR(AppView<?> appView, Origin origin) { - checkIfObsolete(); - return code == null ? null : code.buildIR(this, appView, origin); - } - - public IRCode buildInliningIR( - DexEncodedMethod context, - AppView<?> appView, - ValueNumberGenerator valueNumberGenerator, - Position callerPosition, - Origin origin, - MethodProcessor methodProcessor) { - checkIfObsolete(); - return code.buildInliningIR( - context, this, appView, valueNumberGenerator, callerPosition, origin, methodProcessor); - } - public void setCode(Code newCode, AppView<?> appView) { checkIfObsolete(); // If the locals are not kept, we might still need information to satisfy -keepparameternames. @@ -936,7 +949,7 @@ return builder.build(); } - public DexEncodedMethod toInitializerForwardingBridge(DexClass holder, DexMethod newMethod) { + public ProgramMethod toInitializerForwardingBridge(DexProgramClass holder, DexMethod newMethod) { assert accessFlags.isPrivate() : "Expected to create bridge for private constructor as part of nest-based access" + " desugaring"; @@ -958,11 +971,11 @@ builder.accessFlags.unsetPrivate(); builder.accessFlags.setSynthetic(); builder.accessFlags.setConstructor(); - return builder.build(); + return new ProgramMethod(holder, builder.build()); } - public static DexEncodedMethod createFieldAccessorBridge( - DexFieldWithAccess fieldWithAccess, DexClass holder, DexMethod newMethod) { + public static ProgramMethod createFieldAccessorBridge( + DexFieldWithAccess fieldWithAccess, DexProgramClass holder, DexMethod newMethod) { assert holder.type == fieldWithAccess.getHolder(); MethodAccessFlags accessFlags = MethodAccessFlags.fromSharedAccessFlags( @@ -987,13 +1000,15 @@ registry.registerStaticFieldWrite(fieldWithAccess.getField()); } }); - return new DexEncodedMethod( - newMethod, - accessFlags, - DexAnnotationSet.empty(), - ParameterAnnotationsList.empty(), - code, - true); + return new ProgramMethod( + holder, + new DexEncodedMethod( + newMethod, + accessFlags, + DexAnnotationSet.empty(), + ParameterAnnotationsList.empty(), + code, + true)); } public DexEncodedMethod toRenamedHolderMethod(DexType newHolderType, DexItemFactory factory) { @@ -1025,7 +1040,7 @@ true); } - public DexEncodedMethod toStaticForwardingBridge(DexClass holder, DexMethod newMethod) { + public ProgramMethod toStaticForwardingBridge(DexProgramClass holder, DexMethod newMethod) { assert accessFlags.isPrivate() : "Expected to create bridge for private method as part of nest-based access desugaring"; Builder builder = syntheticBuilder(this); @@ -1053,7 +1068,7 @@ if (holder.isInterface()) { builder.accessFlags.setPublic(); } - return builder.build(); + return new ProgramMethod(holder, builder.build()); } public DexEncodedMethod toForwardingMethod(DexClass holder, DexDefinitionSupplier definitions) { @@ -1198,16 +1213,6 @@ return !annotations().isEmpty() || !parameterAnnotationsList.isEmpty(); } - public void registerCodeReferences(UseRegistry registry) { - checkIfObsolete(); - if (code != null) { - if (Log.ENABLED) { - Log.verbose(getClass(), "Registering definitions reachable from `%s`.", method); - } - code.registerCodeReferences(this, registry); - } - } - public static int slowCompare(DexEncodedMethod m1, DexEncodedMethod m2) { return m1.method.slowCompareTo(m2.method); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java index bda291b..e27c7b4 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.graph; import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO; +import static com.google.common.base.Predicates.alwaysTrue; import com.android.tools.r8.ProgramResource; import com.android.tools.r8.ProgramResource.Kind; @@ -14,6 +15,7 @@ import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.TraversalContinuation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -21,6 +23,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> { @@ -128,6 +133,79 @@ synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom); } + public void forEachProgramMethod(Consumer<ProgramMethod> consumer) { + forEachProgramMethodMatching(alwaysTrue(), consumer); + } + + public void forEachProgramMethodMatching( + Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) { + methodCollection.forEachMethodMatching( + predicate, method -> consumer.accept(new ProgramMethod(this, method))); + } + + public void forEachProgramDirectMethod(Consumer<ProgramMethod> consumer) { + forEachProgramDirectMethodMatching(alwaysTrue(), consumer); + } + + public void forEachProgramDirectMethodMatching( + Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) { + methodCollection.forEachDirectMethodMatching( + predicate, method -> consumer.accept(new ProgramMethod(this, method))); + } + + public void forEachProgramVirtualMethod(Consumer<ProgramMethod> consumer) { + forEachProgramVirtualMethodMatching(alwaysTrue(), consumer); + } + + public void forEachProgramVirtualMethodMatching( + Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) { + methodCollection.forEachVirtualMethodMatching( + predicate, method -> consumer.accept(new ProgramMethod(this, method))); + } + + public ProgramMethod getProgramClassInitializer() { + return toProgramMethodOrNull(getClassInitializer()); + } + + public ProgramMethod getProgramDefaultInitializer() { + return getProgramInitializer(DexType.EMPTY_ARRAY); + } + + public ProgramMethod getProgramInitializer(DexType[] types) { + return toProgramMethodOrNull(getInitializer(types)); + } + + public ProgramMethod lookupProgramMethod(DexMethod reference) { + return toProgramMethodOrNull(getMethodCollection().getMethod(reference)); + } + + private ProgramMethod toProgramMethodOrNull(DexEncodedMethod method) { + if (method != null) { + return new ProgramMethod(this, method); + } + return null; + } + + public TraversalContinuation traverseProgramMethods( + Function<ProgramMethod, TraversalContinuation> fn) { + return getMethodCollection().traverse(method -> fn.apply(new ProgramMethod(this, method))); + } + + public TraversalContinuation traverseProgramInstanceInitializers( + Function<ProgramMethod, TraversalContinuation> fn) { + return traverseProgramMethods(fn, DexEncodedMethod::isInstanceInitializer); + } + + public TraversalContinuation traverseProgramMethods( + Function<ProgramMethod, TraversalContinuation> fn, Predicate<DexEncodedMethod> predicate) { + return getMethodCollection() + .traverse( + method -> + predicate.test(method) + ? fn.apply(new ProgramMethod(this, method)) + : TraversalContinuation.CONTINUE); + } + public boolean originatesFromDexResource() { return originKind == Kind.DEX; }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java index 597e6e0..d17a4d2 100644 --- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java +++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -209,14 +209,14 @@ } @Override - public IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin) { - return asCfCode().buildIR(encodedMethod, appView, origin); + public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { + return asCfCode().buildIR(method, appView, origin); } @Override public IRCode buildInliningIR( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator valueNumberGenerator, Position callerPosition, @@ -225,7 +225,7 @@ return asCfCode() .buildInliningIR( context, - encodedMethod, + method, appView, valueNumberGenerator, callerPosition, @@ -234,11 +234,16 @@ } @Override - public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { asCfCode().registerCodeReferences(method, registry); } @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { + asCfCode().registerCodeReferencesForDesugaring(method, registry); + } + + @Override public void registerArgumentReferences(DexEncodedMethod method, ArgumentUse registry) { asCfCode().registerArgumentReferences(method, registry); }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java index 5b05c3f..5a5db62 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java +++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -38,6 +38,16 @@ } @Override + public int numberOfDirectMethods() { + return directMethods.length; + } + + @Override + public int numberOfVirtualMethods() { + return virtualMethods.length; + } + + @Override int size() { return directMethods.length + virtualMethods.length; }
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 ec3e102..ac2685a 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java +++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -1,5 +1,7 @@ package com.android.tools.r8.graph; +import static com.google.common.base.Predicates.alwaysTrue; + import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.TraversalContinuation; @@ -48,6 +50,14 @@ // Nothing to do. } + public int numberOfDirectMethods() { + return backing.numberOfDirectMethods(); + } + + public int numberOfVirtualMethods() { + return backing.numberOfVirtualMethods(); + } + public int size() { return backing.size(); } @@ -57,7 +67,45 @@ } public void forEachMethod(Consumer<DexEncodedMethod> consumer) { - backing.forEachMethod(consumer); + forEachMethodMatching(alwaysTrue(), consumer); + } + + public void forEachMethodMatching( + Predicate<DexEncodedMethod> predicate, Consumer<DexEncodedMethod> consumer) { + backing.forEachMethod( + method -> { + if (predicate.test(method)) { + consumer.accept(method); + } + }); + } + + public void forEachDirectMethod(Consumer<DexEncodedMethod> consumer) { + forEachDirectMethodMatching(alwaysTrue(), consumer); + } + + public void forEachDirectMethodMatching( + Predicate<DexEncodedMethod> predicate, Consumer<DexEncodedMethod> consumer) { + backing.forEachDirectMethod( + method -> { + if (predicate.test(method)) { + consumer.accept(method); + } + }); + } + + public void forEachVirtualMethod(Consumer<DexEncodedMethod> consumer) { + forEachVirtualMethodMatching(alwaysTrue(), consumer); + } + + public void forEachVirtualMethodMatching( + Predicate<DexEncodedMethod> predicate, Consumer<DexEncodedMethod> consumer) { + backing.forEachVirtualMethod( + method -> { + if (predicate.test(method)) { + consumer.accept(method); + } + }); } public Iterable<DexEncodedMethod> methods() {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java index 913a563..56ab228 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java +++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import static com.google.common.base.Predicates.alwaysTrue; + import com.android.tools.r8.utils.TraversalContinuation; import java.util.Collection; import java.util.List; @@ -29,6 +31,10 @@ // Collection methods. + abstract int numberOfDirectMethods(); + + abstract int numberOfVirtualMethods(); + abstract int size(); // Traversal methods. @@ -36,13 +42,27 @@ abstract TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn); void forEachMethod(Consumer<DexEncodedMethod> fn) { + forEachMethod(fn, alwaysTrue()); + } + + void forEachMethod(Consumer<DexEncodedMethod> fn, Predicate<DexEncodedMethod> predicate) { traverse( method -> { - fn.accept(method); + if (predicate.test(method)) { + fn.accept(method); + } return TraversalContinuation.CONTINUE; }); } + void forEachDirectMethod(Consumer<DexEncodedMethod> fn) { + forEachMethod(fn, this::belongsToDirectPool); + } + + void forEachVirtualMethod(Consumer<DexEncodedMethod> fn) { + forEachMethod(fn, this::belongsToVirtualPool); + } + abstract Iterable<DexEncodedMethod> methods(); abstract List<DexEncodedMethod> directMethods();
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java index d03e741..d34ba36 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java +++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -66,6 +66,26 @@ } @Override + public int numberOfDirectMethods() { + return numberOfMethodsMatching(this::belongsToDirectPool); + } + + @Override + public int numberOfVirtualMethods() { + return numberOfMethodsMatching(this::belongsToVirtualPool); + } + + private int numberOfMethodsMatching(Predicate<DexEncodedMethod> predicate) { + int count = 0; + for (DexEncodedMethod method : methodMap.values()) { + if (predicate.test(method)) { + count++; + } + } + return count; + } + + @Override int size() { return methodMap.size(); }
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 34f32ff..1245da9 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -271,7 +271,7 @@ */ public boolean recordDirectAllocationSite( DexProgramClass clazz, - DexEncodedMethod context, + ProgramMethod context, InstantiationReason instantiationReason, KeepReason keepReason, AppInfo appInfo) { @@ -285,7 +285,7 @@ Set<DexEncodedMethod> allocationSitesForClass = classesWithAllocationSiteTracking.computeIfAbsent( clazz, ignore -> Sets.newIdentityHashSet()); - allocationSitesForClass.add(context); + allocationSitesForClass.add(context.getDefinition()); return allocationSitesForClass.size() == 1; } if (classesWithoutAllocationSiteTracking.add(clazz)) {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java index 4924d0c..88c781c 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -3,6 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.Position; +import com.android.tools.r8.ir.code.ValueNumberGenerator; +import com.android.tools.r8.ir.conversion.MethodProcessor; +import com.android.tools.r8.logging.Log; +import com.android.tools.r8.origin.Origin; + /** Type representing a method definition in the programs compilation unit and its holder. */ public final class ProgramMethod extends DexClassAndMethod { @@ -10,6 +17,37 @@ super(holder, method); } + public IRCode buildIR(AppView<?> appView) { + DexEncodedMethod method = getDefinition(); + return method.hasCode() ? method.getCode().buildIR(this, appView, getOrigin()) : null; + } + + public IRCode buildInliningIR( + ProgramMethod context, + AppView<?> appView, + ValueNumberGenerator valueNumberGenerator, + Position callerPosition, + Origin origin, + MethodProcessor methodProcessor) { + Code code = getDefinition().getCode(); + return code.buildInliningIR( + context, this, appView, valueNumberGenerator, callerPosition, origin, methodProcessor); + } + + public boolean isStructurallyEqualTo(ProgramMethod other) { + return getDefinition() == other.getDefinition() && getHolder() == other.getHolder(); + } + + public void registerCodeReferences(UseRegistry registry) { + Code code = getDefinition().getCode(); + if (code != null) { + if (Log.ENABLED) { + Log.verbose(getClass(), "Registering definitions reachable from `%s`.", this); + } + code.registerCodeReferences(this, registry); + } + } + @Override public boolean isProgramMethod() { return true;
diff --git a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java index c632f75..0f63ed1 100644 --- a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java +++ b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
@@ -68,9 +68,9 @@ } @Override - void writeMethod(DexEncodedMethod method, PrintStream ps) { + void writeMethod(ProgramMethod method, PrintStream ps) { ps.append("\n"); - ps.append(method.toSmaliString(application.getProguardMap())); + ps.append(method.getDefinition().toSmaliString(application.getProguardMap())); ps.append("\n"); }
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 b41e8fa..6b03d33 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
@@ -12,9 +12,11 @@ import com.android.tools.r8.cf.code.CfInvoke; import com.android.tools.r8.cf.code.CfLogicalBinop; import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.google.common.collect.ImmutableList; import java.util.List; @@ -33,12 +35,13 @@ } @Override - public void processNewlyLiveMethod(DexEncodedMethod method) { - if (method.isClassInitializer()) { - if (method.getCode().isCfCode()) { - if (hasJavacClinitAssertionCode(method.getCode().asCfCode()) - || hasKotlincClinitAssertionCode(method)) { - feedback.setInitializerEnablingJavaVmAssertions(method); + public void processNewlyLiveMethod(ProgramMethod method) { + DexEncodedMethod definition = method.getDefinition(); + if (definition.isClassInitializer()) { + Code code = definition.getCode(); + if (code.isCfCode()) { + if (hasJavacClinitAssertionCode(code.asCfCode()) || hasKotlincClinitAssertionCode(method)) { + feedback.setInitializerEnablingJavaVmAssertions(definition); } } } @@ -126,9 +129,9 @@ return false; } - private boolean hasKotlincClinitAssertionCode(DexEncodedMethod method) { - if (method.holder() == dexItemFactory.kotlin.assertions.type) { - CfCode code = method.getCode().asCfCode(); + private boolean hasKotlincClinitAssertionCode(ProgramMethod method) { + if (method.getHolderType() == dexItemFactory.kotlin.assertions.type) { + CfCode code = method.getDefinition().getCode().asCfCode(); for (int i = 1; i < code.instructions.size(); i++) { CfInstruction instruction = code.instructions.get(i - 1); if (instruction.isInvoke()) {
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java index 8d61294..96b5de8 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter; import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode; import com.android.tools.r8.utils.OptionalBool; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; @@ -33,7 +34,7 @@ } @Override - public void processNewlyLiveMethod(DexEncodedMethod method) { + public void processNewlyLiveMethod(ProgramMethod method) { converter.registerCallbackIfRequired(method); } @@ -66,7 +67,7 @@ this.traceInvoke(invokedMethod); } - public List<DexEncodedMethod> generateCallbackMethods() { + public ProgramMethodSet generateCallbackMethods() { return converter.generateCallbackMethods(); }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java index 29d7dcc..06f544d 100644 --- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java +++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -5,8 +5,8 @@ package com.android.tools.r8.graph.analysis; import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.shaking.Enqueuer; import com.android.tools.r8.shaking.EnqueuerWorklist; import com.android.tools.r8.utils.Timing; @@ -14,7 +14,7 @@ public abstract class EnqueuerAnalysis { /** Called when a class is found to be instantiated. */ - public void processNewlyInstantiatedClass(DexProgramClass clazz, DexEncodedMethod context) {} + public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {} /** Called when a class is found to be live. */ public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {} @@ -23,7 +23,7 @@ public void processNewlyLiveField(DexEncodedField field) {} /** Called when a method is found to be live. */ - public void processNewlyLiveMethod(DexEncodedMethod method) {} + public void processNewlyLiveMethod(ProgramMethod method) {} /** * Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
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 04dbef2..48518f5 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
@@ -7,9 +7,9 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; -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.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import java.util.IdentityHashMap; import java.util.Map; @@ -62,7 +62,7 @@ } @Override - public void processNewlyInstantiatedClass(DexProgramClass clazz, DexEncodedMethod context) { + public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) { DexType key = clazz.type; DexType objectType = appView.dexItemFactory().objectType; if (context == null) { @@ -74,7 +74,7 @@ // Record that the enclosing class is guaranteed to be initialized at the allocation site. AppInfoWithClassHierarchy appInfo = appView.appInfo(); - DexType guaranteedToBeInitialized = context.holder(); + DexType guaranteedToBeInitialized = context.getHolderType(); DexType existingGuaranteedToBeInitialized = mapping.getOrDefault(key, guaranteedToBeInitialized); mapping.put(
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 75d5423..5e29668 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
@@ -13,6 +13,7 @@ import com.android.tools.r8.graph.FieldAccessInfo; import com.android.tools.r8.graph.FieldAccessInfoCollection; import com.android.tools.r8.graph.ObjectAllocationInfoCollection; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.BottomValue; import com.android.tools.r8.ir.analysis.value.SingleValue; @@ -28,11 +29,11 @@ import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo; import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import java.util.ArrayList; -import java.util.Collection; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; @@ -275,11 +276,11 @@ } } - public void waveDone(Collection<DexEncodedMethod> wave, OptimizationFeedbackDelayed feedback) { + public void waveDone(ProgramMethodSet wave, OptimizationFeedbackDelayed feedback) { // This relies on the instance initializer info in the method optimization feedback. It is // therefore important that the optimization info has been flushed in advance. assert feedback.noUpdatesLeft(); - for (DexEncodedMethod method : wave) { + for (ProgramMethod method : wave) { fieldAccessGraph.markProcessed(method, field -> recordAllFieldPutsProcessed(field, feedback)); objectAllocationGraph.markProcessed( method, clazz -> recordAllAllocationsSitesProcessed(clazz, feedback)); @@ -328,8 +329,8 @@ }); } - void markProcessed(DexEncodedMethod method, Consumer<DexEncodedField> allWritesSeenConsumer) { - List<DexEncodedField> fieldWritesInMethod = fieldWrites.get(method); + void markProcessed(ProgramMethod method, Consumer<DexEncodedField> allWritesSeenConsumer) { + List<DexEncodedField> fieldWritesInMethod = fieldWrites.get(method.getDefinition()); if (fieldWritesInMethod != null) { for (DexEncodedField field : fieldWritesInMethod) { int numberOfPendingFieldWrites = pendingFieldWrites.removeInt(field) - 1; @@ -366,8 +367,8 @@ } void markProcessed( - DexEncodedMethod method, Consumer<DexProgramClass> allAllocationsSitesSeenConsumer) { - List<DexProgramClass> allocationSitesInMethod = objectAllocations.get(method); + ProgramMethod method, Consumer<DexProgramClass> allAllocationsSitesSeenConsumer) { + List<DexProgramClass> allocationSitesInMethod = objectAllocations.get(method.getDefinition()); if (allocationSitesInMethod != null) { for (DexProgramClass type : allocationSitesInMethod) { int numberOfPendingAllocationSites = pendingObjectAllocations.removeInt(type) - 1;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java index 5ddacd6..1a6a707 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.FieldAccessInfo; import com.android.tools.r8.graph.FieldAccessInfoCollection; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.analysis.value.SingleFieldValue; @@ -24,6 +25,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -38,7 +40,7 @@ private final Set<DexEncodedField> fieldsOfInterest = Sets.newConcurrentHashSet(); /** Updated concurrently from {@link #processClass(DexProgramClass)}. */ - private final Set<DexEncodedMethod> methodsToReprocess = Sets.newConcurrentHashSet(); + private final ProgramMethodSet methodsToReprocess = ProgramMethodSet.createConcurrent(); public TrivialFieldAccessReprocessor( AppView<AppInfoWithLiveness> appView, @@ -109,11 +111,9 @@ } private void processClass(DexProgramClass clazz) { - for (DexEncodedMethod method : clazz.methods()) { - if (method.hasCode()) { - method.getCode().registerCodeReferences(method, new TrivialFieldAccessUseRegistry(method)); - } - } + clazz.forEachProgramMethodMatching( + DexEncodedMethod::hasCode, + method -> method.registerCodeReferences(new TrivialFieldAccessUseRegistry(method))); } private static boolean canOptimizeField( @@ -154,9 +154,9 @@ class TrivialFieldAccessUseRegistry extends UseRegistry { - private final DexEncodedMethod method; + private final ProgramMethod method; - TrivialFieldAccessUseRegistry(DexEncodedMethod method) { + TrivialFieldAccessUseRegistry(ProgramMethod method) { super(appView.dexItemFactory()); this.method = method; }
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 3e493b7..4879783 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
@@ -4,19 +4,17 @@ package com.android.tools.r8.ir.analysis.proto; -import static com.google.common.base.Predicates.not; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DefaultUseRegistry; 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.graph.DexField; -import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.FieldAccessInfo; import com.android.tools.r8.graph.FieldAccessInfoCollection; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.IRCodeUtils; import com.android.tools.r8.ir.code.Instruction; @@ -24,27 +22,19 @@ import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; -import com.android.tools.r8.logging.Log; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration; import com.android.tools.r8.shaking.Enqueuer; import com.android.tools.r8.shaking.TreePrunerConfiguration; -import com.android.tools.r8.utils.DescriptorUtils; -import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; -import com.google.common.base.Predicates; import com.google.common.collect.Sets; -import java.io.IOException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; /** * This optimization is responsible for pruning dead proto extensions. @@ -173,18 +163,16 @@ timing.end(); } - private void forEachFindLiteExtensionByNumberMethod(Consumer<DexEncodedMethod> consumer) { + private void forEachFindLiteExtensionByNumberMethod(Consumer<ProgramMethod> consumer) { appView .appInfo() .forEachInstantiatedSubType( references.extensionRegistryLiteType, - clazz -> { - for (DexEncodedMethod method : clazz.methods()) { - if (references.isFindLiteExtensionByNumberMethod(method.method)) { - consumer.accept(method); - } - } - }, + clazz -> + clazz.forEachProgramMethodMatching( + definition -> + references.isFindLiteExtensionByNumberMethod(definition.getReference()), + consumer::accept), lambda -> { assert false; }); @@ -239,93 +227,4 @@ } }); } - - /** For debugging. */ - public void logRemainingProtoExtensionFields() { - Predicate<DexField> skip = getSkipPredicate(null); - - Set<DexField> remainingProtoExtensionFieldReads = Sets.newIdentityHashSet(); - forEachFindLiteExtensionByNumberMethod( - method -> { - Log.info( - GeneratedExtensionRegistryShrinker.class, - "Extracting remaining proto extension field reads from method `%s`", - method.method.toSourceString()); - - assert method.hasCode(); - method - .getCode() - .registerCodeReferences( - method, - new DefaultUseRegistry(appView.dexItemFactory()) { - - @Override - public boolean registerStaticFieldRead(DexField field) { - if (!skip.test(field)) { - remainingProtoExtensionFieldReads.add(field); - } - return true; - } - }); - }); - - Log.info( - GeneratedExtensionRegistryShrinker.class, - "Number of remaining proto extension fields: %s", - remainingProtoExtensionFieldReads.size()); - - FieldAccessInfoCollection<?> fieldAccessInfoCollection = - appView.appInfo().getFieldAccessInfoCollection(); - for (DexField field : remainingProtoExtensionFieldReads) { - StringBuilder message = new StringBuilder(field.toSourceString()); - FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field); - fieldAccessInfo.forEachReadContext( - readContext -> - message - .append(System.lineSeparator()) - .append("- ") - .append(readContext.toSourceString())); - Log.info(GeneratedExtensionRegistryShrinker.class, message.toString()); - } - } - - /** - * Utility to disable logging for proto extensions fields that are expected to be present in the - * output. - * - * <p>Each proto extension field that is expected to be present in the output can be added to the - * given file. Then no logs will be emitted for that field. - * - * <p>Example: File expected-proto-extensions.txt with lines like this: - * - * <pre> - * foo.bar.SomeClass.someField - * foo.bar.SomeOtherClass.someOtherField - * </pre> - */ - private Predicate<DexField> getSkipPredicate(Path file) { - if (file != null) { - try { - DexItemFactory dexItemFactory = appView.dexItemFactory(); - Set<DexField> skipFields = - FileUtils.readAllLines(file).stream() - .map(String::trim) - .filter(not(String::isEmpty)) - .map( - x -> { - int separatorIndex = x.lastIndexOf("."); - return dexItemFactory.createField( - dexItemFactory.createType( - DescriptorUtils.javaTypeToDescriptor(x.substring(0, separatorIndex))), - references.generatedExtensionType, - dexItemFactory.createString(x.substring(separatorIndex + 1))); - }) - .collect(Collectors.toSet()); - return skipFields::contains; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return Predicates.alwaysFalse(); - } }
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 f073108..a11b346 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
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo; import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; @@ -34,7 +35,6 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; 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.utils.PredicateSet; import com.android.tools.r8.utils.ThreadUtils; @@ -54,7 +54,7 @@ private final AppView<? extends AppInfoWithClassHierarchy> appView; private final ProtoReferences references; - private final Map<DexProgramClass, DexEncodedMethod> builders = new IdentityHashMap<>(); + private final Map<DexProgramClass, ProgramMethod> builders = new IdentityHashMap<>(); GeneratedMessageLiteBuilderShrinker( AppView<? extends AppInfoWithClassHierarchy> appView, ProtoReferences references) { @@ -64,11 +64,12 @@ /** Returns true if an action was deferred. */ public boolean deferDeadProtoBuilders( - DexProgramClass clazz, DexEncodedMethod context, BooleanSupplier register) { - if (references.isDynamicMethod(context) && references.isGeneratedMessageLiteBuilder(clazz)) { + DexProgramClass clazz, ProgramMethod method, BooleanSupplier register) { + DexEncodedMethod definition = method.getDefinition(); + if (references.isDynamicMethod(definition) && references.isGeneratedMessageLiteBuilder(clazz)) { if (register.getAsBoolean()) { - assert builders.getOrDefault(clazz, context) == context; - builders.put(clazz, context); + assert !builders.containsKey(clazz) || builders.get(clazz).getDefinition() == definition; + builders.put(clazz, method); return true; } } @@ -104,12 +105,11 @@ private void removeDeadBuilderReferencesFromDynamicMethod( AppView<AppInfoWithLiveness> appView, - DexEncodedMethod dynamicMethod, + ProgramMethod dynamicMethod, IRConverter converter, CodeRewriter codeRewriter, SwitchCaseAnalyzer switchCaseAnalyzer) { - Origin origin = appView.appInfo().originFor(dynamicMethod.holder()); - IRCode code = dynamicMethod.buildIR(appView, origin); + IRCode code = dynamicMethod.buildIR(appView); codeRewriter.rewriteSwitch(code, switchCaseAnalyzer); converter.removeDeadCodeAndFinalizeIR( dynamicMethod, code, OptimizationFeedbackSimple.getInstance(), Timing.empty()); @@ -132,7 +132,7 @@ if (node != null) { List<Node> calleesToBeRemoved = new ArrayList<>(); for (Node callee : node.getCalleesWithDeterministicOrder()) { - if (references.isDynamicMethodBridge(callee.method)) { + if (references.isDynamicMethodBridge(callee.getMethod())) { calleesToBeRemoved.add(callee); } } @@ -143,7 +143,7 @@ } public void inlineCallsToDynamicMethod( - DexEncodedMethod method, + ProgramMethod method, IRCode code, EnumValueOptimizer enumValueOptimizer, OptimizationFeedback feedback,
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 4414507..7d74c63 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
@@ -9,10 +9,9 @@ import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.setObjectsValueForMessageInfoConstructionInvoke; import com.android.tools.r8.graph.AppView; -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.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo; import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject; import com.android.tools.r8.ir.analysis.type.Nullability; @@ -68,8 +67,9 @@ this.stringType = TypeElement.stringClassType(appView, Nullability.definitelyNotNull()); } - public void run(DexEncodedMethod method, IRCode code) { - if (references.isDynamicMethod(method.method)) { + public void run(IRCode code) { + ProgramMethod method = code.context(); + if (references.isDynamicMethod(method.getReference())) { rewriteDynamicMethod(method, code); } } @@ -89,19 +89,19 @@ timing.end(); } - private void forEachDynamicMethod(Consumer<DexEncodedMethod> consumer) { + private void forEachDynamicMethod(Consumer<ProgramMethod> consumer) { DexItemFactory dexItemFactory = appView.dexItemFactory(); appView .appInfo() .forEachInstantiatedSubType( references.generatedMessageLiteType, clazz -> { - DexMethod dynamicMethod = + DexMethod dynamicMethodReference = dexItemFactory.createMethod( clazz.type, references.dynamicMethodProto, references.dynamicMethodName); - DexEncodedMethod encodedDynamicMethod = clazz.lookupVirtualMethod(dynamicMethod); - if (encodedDynamicMethod != null) { - consumer.accept(encodedDynamicMethod); + ProgramMethod dynamicMethod = clazz.lookupProgramMethod(dynamicMethodReference); + if (dynamicMethod != null) { + consumer.accept(dynamicMethod); } }, lambda -> { @@ -117,12 +117,7 @@ * <p>NOTE: This is work in progress. Understanding the full semantics of the arguments passed to * newMessageInfo is still pending. */ - private void rewriteDynamicMethod(DexEncodedMethod method, IRCode code) { - DexClass context = appView.definitionFor(method.holder()); - if (context == null || !context.isProgramClass()) { - return; - } - + private void rewriteDynamicMethod(ProgramMethod method, IRCode code) { InvokeMethod newMessageInfoInvoke = getNewMessageInfoInvoke(code, references); if (newMessageInfoInvoke != null) { Value infoValue = @@ -131,11 +126,10 @@ getObjectsValueFromMessageInfoConstructionInvoke(newMessageInfoInvoke, references); // Decode the arguments passed to newMessageInfo(). - ProtoMessageInfo protoMessageInfo = decoder.run(method, context, infoValue, objectsValue); + ProtoMessageInfo protoMessageInfo = decoder.run(method, infoValue, objectsValue); if (protoMessageInfo != null) { // Rewrite the arguments to newMessageInfo(). - rewriteArgumentsToNewMessageInfo( - method, code, newMessageInfoInvoke, infoValue, protoMessageInfo); + rewriteArgumentsToNewMessageInfo(code, newMessageInfoInvoke, infoValue, protoMessageInfo); // Ensure that the definition of the original `objects` value is removed. IRCodeUtils.removeArrayAndTransitiveInputsIfNotUsed(code, objectsValue.definition); @@ -147,13 +141,12 @@ } private void rewriteArgumentsToNewMessageInfo( - DexEncodedMethod method, IRCode code, InvokeMethod newMessageInfoInvoke, Value infoValue, ProtoMessageInfo protoMessageInfo) { rewriteInfoArgumentToNewMessageInfo(code, infoValue, protoMessageInfo); - rewriteObjectsArgumentToNewMessageInfo(method, code, newMessageInfoInvoke, protoMessageInfo); + rewriteObjectsArgumentToNewMessageInfo(code, newMessageInfoInvoke, protoMessageInfo); } private void rewriteInfoArgumentToNewMessageInfo( @@ -165,7 +158,6 @@ } private void rewriteObjectsArgumentToNewMessageInfo( - DexEncodedMethod method, IRCode code, InvokeMethod newMessageInfoInvoke, ProtoMessageInfo protoMessageInfo) {
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 79dea35..d5fdc33 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
@@ -5,10 +5,9 @@ package com.android.tools.r8.ir.analysis.proto; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.proto.schema.ProtoEnqueuerExtension; import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry; import com.android.tools.r8.shaking.Enqueuer; @@ -21,11 +20,8 @@ private final ProtoReferences references; public ProtoEnqueuerUseRegistry( - AppView<?> appView, - DexProgramClass currentHolder, - DexEncodedMethod currentMethod, - Enqueuer enqueuer) { - super(appView, currentHolder, currentMethod, enqueuer); + AppView<?> appView, ProgramMethod currentMethod, Enqueuer enqueuer) { + super(appView, currentMethod, enqueuer); this.references = appView.protoShrinker().references; }
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 1321984..ca4ca1e 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
@@ -5,9 +5,8 @@ package com.android.tools.r8.ir.analysis.proto; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.proto.ProtoReferences.MethodToInvokeMembers; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeMethod; @@ -35,9 +34,8 @@ @Override public Reason computeInliningReason( - InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { - DexProgramClass enclosingClass = appView.definitionFor(context.holder()).asProgramClass(); - if (references.isAbstractGeneratedMessageLiteBuilder(enclosingClass) + InvokeMethod invoke, ProgramMethod target, ProgramMethod context) { + if (references.isAbstractGeneratedMessageLiteBuilder(context.getHolder()) && invoke.isInvokeSuper()) { // Aggressively inline invoke-super calls inside the GeneratedMessageLite builders. Such // instructions prohibit inlining of the enclosing method into other contexts, and therefore
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java index 014ab85..c2c9a50 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -12,6 +12,7 @@ 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.ProgramMethod; import com.android.tools.r8.ir.code.Value; public class ProtoReferences { @@ -43,6 +44,7 @@ public final DexProto dynamicMethodProto; public final DexProto findLiteExtensionByNumberProto; + public final DexMethod dynamicMethod; public final DexMethod newMessageInfoMethod; public final DexMethod rawMessageInfoConstructor; @@ -80,6 +82,8 @@ factory.createProto(generatedExtensionType, messageLiteType, factory.intType); // Methods. + dynamicMethod = + factory.createMethod(generatedMessageLiteType, dynamicMethodProto, dynamicMethodName); newMessageInfoMethod = factory.createMethod( generatedMessageLiteType, @@ -114,6 +118,10 @@ return isDynamicMethod(encodedMethod.method); } + public boolean isDynamicMethod(ProgramMethod method) { + return isDynamicMethod(method.getReference()); + } + public boolean isDynamicMethodBridge(DexMethod method) { return method == generatedMessageLiteMethods.dynamicMethodBridgeMethod; } @@ -122,6 +130,10 @@ return isDynamicMethodBridge(method.method); } + public boolean isDynamicMethodBridge(ProgramMethod method) { + return isDynamicMethodBridge(method.getReference()); + } + public boolean isFindLiteExtensionByNumberMethod(DexMethod method) { return method.proto == findLiteExtensionByNumberProto && method.name.startsWith(findLiteExtensionByNumberName)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java index 6d404cf..e4741d9 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -7,12 +7,11 @@ import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getInfoValueFromMessageInfoConstructionInvoke; import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getObjectsValueFromMessageInfoConstructionInvoke; -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.graph.DexField; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.proto.schema.DeadProtoFieldObject; import com.android.tools.r8.ir.analysis.proto.schema.LiveProtoFieldObject; import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldInfo; @@ -76,16 +75,14 @@ this.references = references; } - public ProtoMessageInfo run( - DexEncodedMethod dynamicMethod, DexClass context, InvokeMethod invoke) { + public ProtoMessageInfo run(ProgramMethod dynamicMethod, InvokeMethod invoke) { assert references.isMessageInfoConstructionMethod(invoke.getInvokedMethod()); Value infoValue = getInfoValueFromMessageInfoConstructionInvoke(invoke, references); Value objectsValue = getObjectsValueFromMessageInfoConstructionInvoke(invoke, references); - return run(dynamicMethod, context, infoValue, objectsValue); + return run(dynamicMethod, infoValue, objectsValue); } - public ProtoMessageInfo run( - DexEncodedMethod dynamicMethod, DexClass context, Value infoValue, Value objectsValue) { + public ProtoMessageInfo run(ProgramMethod dynamicMethod, Value infoValue, Value objectsValue) { try { ProtoMessageInfo.Builder builder = ProtoMessageInfo.builder(dynamicMethod); ThrowingIntIterator<InvalidRawMessageInfoException> infoIterator = @@ -125,13 +122,13 @@ for (int i = 0; i < numberOfOneOfObjects; i++) { ProtoObject oneOfObject = createProtoObject( - objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context); + objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), dynamicMethod); if (!oneOfObject.isProtoFieldObject()) { throw new InvalidRawMessageInfoException(); } ProtoObject oneOfCaseObject = createProtoObject( - objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context); + objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), dynamicMethod); if (!oneOfCaseObject.isProtoFieldObject()) { throw new InvalidRawMessageInfoException(); } @@ -142,7 +139,7 @@ for (int i = 0; i < numberOfHasBitsObjects; i++) { ProtoObject hasBitsObject = createProtoObject( - objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), context); + objectIterator.computeNextIfAbsent(this::invalidObjectsFailure), dynamicMethod); if (!hasBitsObject.isProtoFieldObject()) { throw new InvalidRawMessageInfoException(); } @@ -168,7 +165,7 @@ try { List<ProtoObject> objects = new ArrayList<>(numberOfObjects); for (Value value : objectIterator.take(numberOfObjects)) { - objects.add(createProtoObject(value, context)); + objects.add(createProtoObject(value, dynamicMethod)); } builder.addField(new ProtoFieldInfo(fieldNumber, fieldType, auxData, objects)); } catch (NoSuchElementException e) { @@ -189,7 +186,7 @@ } } - private ProtoObject createProtoObject(Value value, DexClass context) + private ProtoObject createProtoObject(Value value, ProgramMethod context) throws InvalidRawMessageInfoException { Value root = value.getAliasedValue(); if (!root.isPhi()) { @@ -199,13 +196,14 @@ return new ProtoTypeObject(constClass.getValue()); } else if (definition.isConstString()) { ConstString constString = definition.asConstString(); - DexField field = context.lookupUniqueInstanceFieldWithName(constString.getValue()); + DexField field = + context.getHolder().lookupUniqueInstanceFieldWithName(constString.getValue()); if (field != null) { return new LiveProtoFieldObject(field); } // This const-string refers to a field that no longer exists. In this case, we create a // special dead-object instead of failing with an InvalidRawMessageInfoException below. - return new DeadProtoFieldObject(context.type, constString.getValue()); + return new DeadProtoFieldObject(context.getHolderType(), constString.getValue()); } else if (definition.isDexItemBasedConstString()) { DexItemBasedConstString constString = definition.asDexItemBasedConstString(); DexReference reference = constString.getItem(); @@ -214,13 +212,13 @@ && nameComputationInfo.isFieldNameComputationInfo() && nameComputationInfo.asFieldNameComputationInfo().isForFieldName()) { DexField field = reference.asDexField(); - DexEncodedField encodedField = context.lookupInstanceField(field); + DexEncodedField encodedField = context.getHolder().lookupInstanceField(field); if (encodedField != null) { return new LiveProtoFieldObject(field); } // This const-string refers to a field that no longer exists. In this case, we create a // special dead-object instead of failing with an InvalidRawMessageInfoException below. - return new DeadProtoFieldObject(context.type, field.name); + return new DeadProtoFieldObject(context.getHolderType(), field.name); } } else if (definition.isInvokeStatic()) { InvokeStatic invoke = definition.asInvokeStatic();
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 d6d2990..8001121 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
@@ -8,13 +8,13 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; -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.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.analysis.EnqueuerAnalysis; import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker; import com.android.tools.r8.ir.analysis.proto.ProtoEnqueuerUseRegistry; @@ -38,6 +38,7 @@ import com.android.tools.r8.utils.BitUtils; import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.IdentityHashMap; @@ -79,7 +80,7 @@ Sets.newIdentityHashSet(); // The findLiteExtensionByNumber() methods that have become live since the last fixpoint. - private final Set<DexEncodedMethod> findLiteExtensionByNumberMethods = Sets.newIdentityHashSet(); + private final ProgramMethodSet findLiteExtensionByNumberMethods = ProgramMethodSet.create(); // Mapping from extension container types to the extensions for that type. private final Map<DexType, Set<DexType>> extensionGraph = new IdentityHashMap<>(); @@ -107,7 +108,7 @@ assert clazz.type == references.extendableMessageType; return; } - DexEncodedMethod dynamicMethod = clazz.lookupVirtualMethod(references::isDynamicMethod); + ProgramMethod dynamicMethod = clazz.lookupProgramMethod(references.dynamicMethod); if (dynamicMethod != null) { worklist.enqueueMarkInstantiatedAction( clazz, @@ -125,49 +126,39 @@ * ProtoMessageInfo} object, and create a mapping from the holder to it. */ @Override - public void processNewlyLiveMethod(DexEncodedMethod encodedMethod) { - if (references.isFindLiteExtensionByNumberMethod(encodedMethod.method)) { - findLiteExtensionByNumberMethods.add(encodedMethod); + public void processNewlyLiveMethod(ProgramMethod method) { + if (references.isFindLiteExtensionByNumberMethod(method.getReference())) { + findLiteExtensionByNumberMethods.add(method); return; } - if (!references.isDynamicMethod(encodedMethod)) { + if (!references.isDynamicMethod(method)) { return; } - DexType holder = encodedMethod.holder(); - if (seenButNotLiveProtos.containsKey(holder)) { + DexType holderType = method.getHolderType(); + if (seenButNotLiveProtos.containsKey(holderType)) { // The proto is now live instead of dead. - liveProtos.put(holder, seenButNotLiveProtos.remove(holder)); + liveProtos.put(holderType, seenButNotLiveProtos.remove(holderType)); return; } // Since this dynamicMethod() only becomes live once, and it has just become live, it must be // the case that the proto is not already live. - assert !liveProtos.containsKey(holder); - createProtoMessageInfoFromDynamicMethod(encodedMethod, liveProtos); + assert !liveProtos.containsKey(holderType); + createProtoMessageInfoFromDynamicMethod(method, liveProtos); } private void createProtoMessageInfoFromDynamicMethod( - DexEncodedMethod dynamicMethod, Map<DexType, ProtoMessageInfo> protos) { - DexType holder = dynamicMethod.holder(); + ProgramMethod dynamicMethod, Map<DexType, ProtoMessageInfo> protos) { + DexType holder = dynamicMethod.getHolderType(); assert !protos.containsKey(holder); - DexClass context = appView.definitionFor(holder); - if (context == null || !context.isProgramClass()) { - // TODO(b/112437944): What if a proto message references a proto message on the classpath or - // library path? We should treat them as having a map/required field to be conservative. - assert false; // Should generally not happen. - return; - } - - IRCode code = dynamicMethod.buildIR(appView, context.origin); + IRCode code = dynamicMethod.buildIR(appView); InvokeMethod newMessageInfoInvoke = GeneratedMessageLiteShrinker.getNewMessageInfoInvoke(code, references); ProtoMessageInfo protoMessageInfo = - newMessageInfoInvoke != null - ? decoder.run(dynamicMethod, context, newMessageInfoInvoke) - : null; + newMessageInfoInvoke != null ? decoder.run(dynamicMethod, newMessageInfoInvoke) : null; protos.put(holder, protoMessageInfo); } @@ -221,13 +212,13 @@ collectExtensionFields() .forEach( (clazz, extensionFields) -> { - DexEncodedMethod clinit = clazz.getClassInitializer(); + ProgramMethod clinit = clazz.getProgramClassInitializer(); if (clinit == null) { assert false; // Should generally not happen. return; } - IRCode code = clinit.buildIR(appView, appView.appInfo().originFor(clazz.type)); + IRCode code = clinit.buildIR(appView); Map<DexEncodedField, StaticPut> uniqueStaticPuts = IRCodeUtils.findUniqueStaticPuts(appView, code, extensionFields); for (DexEncodedField extensionField : extensionFields) { @@ -251,10 +242,8 @@ */ private Map<DexProgramClass, Set<DexEncodedField>> collectExtensionFields() { Map<DexProgramClass, Set<DexEncodedField>> extensionFieldsByClass = new IdentityHashMap<>(); - for (DexEncodedMethod findLiteExtensionByNumberMethod : findLiteExtensionByNumberMethods) { - IRCode code = - findLiteExtensionByNumberMethod.buildIR( - appView, appView.appInfo().originFor(findLiteExtensionByNumberMethod.holder())); + for (ProgramMethod findLiteExtensionByNumberMethod : findLiteExtensionByNumberMethods) { + IRCode code = findLiteExtensionByNumberMethod.buildIR(appView); for (BasicBlock block : code.blocks(BasicBlock::isReturnBlock)) { Value returnValue = block.exit().asReturn().returnValue().getAliasedValue(); if (returnValue.isPhi()) { @@ -364,9 +353,7 @@ continue; } - DexEncodedMethod dynamicMethod = protoMessageInfo.getDynamicMethod(); - DexProgramClass clazz = appView.definitionFor(dynamicMethod.holder()).asProgramClass(); - + ProgramMethod dynamicMethod = protoMessageInfo.getDynamicMethod(); for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) { DexEncodedField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo); if (valueStorage == null) { @@ -390,7 +377,7 @@ // written such that we cannot optimize any field reads or writes. enqueuer.registerReflectiveFieldAccess(valueStorage.field, dynamicMethod); worklist.enqueueMarkReachableFieldAction( - clazz, valueStorage, KeepReason.reflectiveUseIn(dynamicMethod)); + dynamicMethod.getHolder(), valueStorage, KeepReason.reflectiveUseIn(dynamicMethod)); valueStorageIsLive = true; } else { valueStorageIsLive = false; @@ -438,10 +425,13 @@ if (newlyLiveField != null) { // Mark hazzer and one-of proto fields as read from dynamicMethod() if they are written in // the app. This is needed to ensure that field writes are not removed from the app. - DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer(); + ProgramMethod defaultInitializer = + dynamicMethod.getHolder().getProgramDefaultInitializer(); assert defaultInitializer != null; Predicate<DexEncodedMethod> neitherDefaultConstructorNorDynamicMethod = - writer -> writer != defaultInitializer && writer != dynamicMethod; + writer -> + writer != defaultInitializer.getDefinition() + && writer != dynamicMethod.getDefinition(); if (enqueuer.isFieldWrittenInMethodSatisfying( newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) { enqueuer.registerReflectiveFieldRead(newlyLiveField.field, dynamicMethod); @@ -451,7 +441,9 @@ // dynamicMethod(). if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.field, dynamicMethod)) { worklist.enqueueMarkReachableFieldAction( - clazz, newlyLiveField, KeepReason.reflectiveUseIn(dynamicMethod)); + dynamicMethod.getHolder(), + newlyLiveField, + KeepReason.reflectiveUseIn(dynamicMethod)); } } } @@ -467,8 +459,8 @@ continue; } - DexEncodedMethod dynamicMethod = protoMessageInfo.getDynamicMethod(); - if (!dynamicMethodsWithTracedProtoObjects.add(dynamicMethod)) { + ProgramMethod dynamicMethod = protoMessageInfo.getDynamicMethod(); + if (!dynamicMethodsWithTracedProtoObjects.add(dynamicMethod.getDefinition())) { continue; } @@ -526,13 +518,14 @@ return; } - DexClass clazz = appView.definitionFor(encodedOneOfCaseField.holder()); - if (clazz == null || !clazz.isProgramClass()) { + DexProgramClass clazz = + asProgramClassOrNull(appView.definitionFor(encodedOneOfCaseField.holder())); + if (clazz == null) { assert false; return; } - DexEncodedMethod dynamicMethod = clazz.lookupVirtualMethod(references::isDynamicMethod); + ProgramMethod dynamicMethod = clazz.lookupProgramMethod(references.dynamicMethod); if (dynamicMethod == null) { assert false; return; @@ -651,13 +644,13 @@ return seenButNotLiveProtos.get(type); } - DexClass clazz = appView.definitionFor(type); - if (clazz == null || !clazz.isProgramClass()) { + DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type)); + if (clazz == null) { seenButNotLiveProtos.put(type, null); return null; } - DexEncodedMethod dynamicMethod = clazz.lookupVirtualMethod(references::isDynamicMethod); + ProgramMethod dynamicMethod = clazz.lookupProgramMethod(references.dynamicMethod); if (dynamicMethod == null) { seenButNotLiveProtos.put(type, null); return null;
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 f026295..28de572 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
@@ -4,8 +4,8 @@ package com.android.tools.r8.ir.analysis.proto.schema; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.proto.ProtoUtils; import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; @@ -22,7 +22,7 @@ public static class Builder { - private final DexEncodedMethod dynamicMethod; + private final ProgramMethod dynamicMethod; private int flags; @@ -30,7 +30,7 @@ private LinkedList<ProtoFieldObject> hasBitsObjects; private LinkedList<ProtoOneOfObjectPair> oneOfObjects; - private Builder(DexEncodedMethod dynamicMethod) { + private Builder(ProgramMethod dynamicMethod) { this.dynamicMethod = dynamicMethod; } @@ -171,7 +171,7 @@ } } - private final DexEncodedMethod dynamicMethod; + private final ProgramMethod dynamicMethod; private final int flags; private final LinkedList<ProtoFieldInfo> fields; @@ -179,7 +179,7 @@ private final LinkedList<ProtoOneOfObjectPair> oneOfObjects; private ProtoMessageInfo( - DexEncodedMethod dynamicMethod, + ProgramMethod dynamicMethod, int flags, LinkedList<ProtoFieldInfo> fields, LinkedList<ProtoFieldObject> hasBitsObjects, @@ -191,7 +191,7 @@ this.oneOfObjects = oneOfObjects; } - public static ProtoMessageInfo.Builder builder(DexEncodedMethod dynamicMethod) { + public static ProtoMessageInfo.Builder builder(ProgramMethod dynamicMethod) { return new ProtoMessageInfo.Builder(dynamicMethod); } @@ -199,7 +199,7 @@ return ProtoUtils.isProto2(flags); } - public DexEncodedMethod getDynamicMethod() { + public ProgramMethod getDynamicMethod() { return dynamicMethod; } @@ -220,7 +220,7 @@ } public DexType getType() { - return dynamicMethod.holder(); + return dynamicMethod.getHolderType(); } public boolean hasFields() {
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 b45ec56..76213c6 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
@@ -14,6 +14,7 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.ConstClass; import com.android.tools.r8.ir.code.IRCode; @@ -79,16 +80,15 @@ } @Override - public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) { + public boolean isMaterializableInContext( + AppView<AppInfoWithLiveness> appView, ProgramMethod context) { DexType baseType = type.toBaseType(appView.dexItemFactory()); if (baseType.isClassType()) { DexClass clazz = appView.definitionFor(type); return clazz != null && clazz.isResolvable(appView) && AccessControl.isClassAccessible( - clazz, - appView.definitionFor(context).asProgramClass(), - appView.options().featureSplitConfiguration) + clazz, context.getHolder(), appView.options().featureSplitConfiguration) .isTrue(); } assert baseType.isPrimitiveType();
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 46261f5..7bccd30 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
@@ -15,6 +15,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.graph.GraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.IRCode; @@ -77,11 +78,12 @@ } @Override - public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) { + public boolean isMaterializableInContext( + AppView<AppInfoWithLiveness> appView, ProgramMethod context) { return AccessControl.isFieldAccessible( appView.appInfo().resolveField(field), appView.definitionForHolder(field), - appView.definitionFor(context).asProgramClass(), + context.getHolder(), appView.appInfo()) .isTrue(); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java index 1982181..67a0858 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -7,8 +7,8 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; -import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.ConstNumber; import com.android.tools.r8.ir.code.IRCode; @@ -80,7 +80,8 @@ } @Override - public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) { + public boolean isMaterializableInContext( + AppView<AppInfoWithLiveness> appView, ProgramMethod context) { return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java index 01f80cc..457d49b 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -11,8 +11,8 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; import com.android.tools.r8.graph.DexString; -import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo; import com.android.tools.r8.ir.code.ConstString; @@ -81,7 +81,8 @@ } @Override - public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) { + public boolean isMaterializableInContext( + AppView<AppInfoWithLiveness> appView, ProgramMethod context) { return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java index 61426a0..f0425b0 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -6,8 +6,8 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier; @@ -41,7 +41,7 @@ TypeAndLocalInfoSupplier info); public abstract boolean isMaterializableInContext( - AppView<AppInfoWithLiveness> appView, DexType context); + AppView<AppInfoWithLiveness> appView, ProgramMethod context); public abstract boolean isMaterializableInAllContexts(AppView<?> appView);
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java index 53b9336..a37e369 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; import com.android.tools.r8.ir.analysis.TypeChecker; import com.android.tools.r8.ir.analysis.ValueMayDependOnEnvironmentAnalysis; @@ -104,7 +105,7 @@ // use odd instruction numbers for the insertion of moves during spilling. public static final int INSTRUCTION_NUMBER_DELTA = 2; - private final DexEncodedMethod method; + private final ProgramMethod method; public LinkedList<BasicBlock> blocks; public final ValueNumberGenerator valueNumberGenerator; @@ -124,7 +125,7 @@ public IRCode( InternalOptions options, - DexEncodedMethod method, + ProgramMethod method, LinkedList<BasicBlock> blocks, ValueNumberGenerator valueNumberGenerator, IRMetadata metadata, @@ -145,10 +146,15 @@ return metadata; } - public DexEncodedMethod method() { + public ProgramMethod context() { return method; } + @Deprecated + public DexEncodedMethod method() { + return method.getDefinition(); + } + public BasicBlock entryBlock() { return blocks.getFirst(); }
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 78d3a69..6c9fb6f 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
@@ -13,6 +13,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.LookupResult; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet; @@ -78,6 +79,11 @@ public abstract DexEncodedMethod lookupSingleTarget( AppView<?> appView, DexType invocationContext); + public final ProgramMethod lookupSingleProgramTarget(AppView<?> appView, ProgramMethod context) { + DexEncodedMethod singleTarget = lookupSingleTarget(appView, context.getHolderType()); + return singleTarget != null ? singleTarget.asProgramMethod(appView) : null; + } + // TODO(b/140204899): Refactor lookup methods to be defined in a single place. public Collection<DexEncodedMethod> lookupTargets( AppView<AppInfoWithLiveness> appView, DexType invocationContext) { @@ -135,7 +141,7 @@ } public abstract InlineAction computeInlining( - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, DefaultInliningOracle decider, ClassInitializationAnalysis classInitializationAnalysis,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java index 5ed1539..00ffbe1 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -5,10 +5,10 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; @@ -43,7 +43,7 @@ @Override public final InlineAction computeInlining( - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, DefaultInliningOracle decider, ClassInitializationAnalysis classInitializationAnalysis,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java index 41b1124..8d46da1 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.conversion.CfBuilder; import com.android.tools.r8.ir.conversion.DexBuilder; @@ -139,7 +140,7 @@ @Override public InlineAction computeInlining( - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, DefaultInliningOracle decider, ClassInitializationAnalysis classInitializationAnalysis, @@ -148,7 +149,7 @@ if (singleTarget != null) { throw new Unreachable( "Unexpected invoke-polymorphic with `" - + singleTarget.method.toSourceString() + + singleTarget.toSourceString() + "` as single target"); } throw new Unreachable("Unexpected attempt to inline invoke that does not have a single target");
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 9a9f727..5ea6e46 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
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query; @@ -130,7 +131,7 @@ @Override public InlineAction computeInlining( - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, DefaultInliningOracle decider, ClassInitializationAnalysis classInitializationAnalysis,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java index 2b05e0c..6ed94b4 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Position.java +++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -4,9 +4,9 @@ package com.android.tools.r8.ir.code; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.ProgramMethod; import com.google.common.annotations.VisibleForTesting; import java.util.Objects; @@ -79,15 +79,15 @@ } public static Position getPositionForInlining( - AppView<?> appView, InvokeMethod invoke, DexEncodedMethod context) { + AppView<?> appView, InvokeMethod invoke, ProgramMethod context) { Position position = invoke.getPosition(); if (position.method == null) { assert position.isNone(); - position = Position.noneWithMethod(context.method, null); + position = Position.noneWithMethod(context.getReference(), null); } assert position.callerPosition == null || position.getOutermostCaller().method - == appView.graphLense().getOriginalMethodSignature(context.method); + == appView.graphLense().getOriginalMethodSignature(context.getReference()); return position; }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java index 6f3bf92..a5842de 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -6,9 +6,11 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.CallGraphBuilderBase.CycleEliminator.CycleEliminationResult; import com.android.tools.r8.ir.conversion.CallSiteInformation.CallGraphBasedCallSiteInformation; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.Iterator; import java.util.Set; @@ -38,7 +40,7 @@ public static Node[] EMPTY_ARRAY = {}; - public final DexEncodedMethod method; + private final ProgramMethod method; private int numberOfCallSites = 0; // Outgoing calls from this method. @@ -55,7 +57,7 @@ // by the current method). private final Set<Node> writers = new TreeSet<>(); - public Node(DexEncodedMethod method) { + public Node(ProgramMethod method) { this.method = method; } @@ -201,14 +203,16 @@ @Override public int compareTo(Node other) { - return method.method.slowCompareTo(other.method.method); + return getProgramMethod() + .getReference() + .slowCompareTo(other.getProgramMethod().getReference()); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("MethodNode for: "); - builder.append(method.toSourceString()); + builder.append(getProgramMethod().toSourceString()); builder.append(" ("); builder.append(callees.size()); builder.append(" callees, "); @@ -222,7 +226,7 @@ builder.append(System.lineSeparator()); for (Node call : callees) { builder.append(" "); - builder.append(call.method.toSourceString()); + builder.append(call.getProgramMethod().toSourceString()); builder.append(System.lineSeparator()); } } @@ -231,12 +235,20 @@ builder.append(System.lineSeparator()); for (Node caller : callers) { builder.append(" "); - builder.append(caller.method.toSourceString()); + builder.append(caller.getProgramMethod().toSourceString()); builder.append(System.lineSeparator()); } } return builder.toString(); } + + public DexEncodedMethod getMethod() { + return method.getDefinition(); + } + + public ProgramMethod getProgramMethod() { + return method; + } } final Set<Node> nodes; @@ -266,22 +278,22 @@ return nodes.isEmpty(); } - public Set<DexEncodedMethod> extractLeaves() { + public ProgramMethodSet extractLeaves() { return extractNodes(Node::isLeaf, Node::cleanCallersAndReadersForRemoval); } - public Set<DexEncodedMethod> extractRoots() { + public ProgramMethodSet extractRoots() { return extractNodes(Node::isRoot, Node::cleanCalleesAndWritersForRemoval); } - private Set<DexEncodedMethod> extractNodes(Predicate<Node> predicate, Consumer<Node> clean) { - Set<DexEncodedMethod> result = Sets.newIdentityHashSet(); + private ProgramMethodSet extractNodes(Predicate<Node> predicate, Consumer<Node> clean) { + ProgramMethodSet result = ProgramMethodSet.create(); Set<Node> removed = Sets.newIdentityHashSet(); Iterator<Node> nodeIterator = nodes.iterator(); while (nodeIterator.hasNext()) { Node node = nodeIterator.next(); if (predicate.test(node)) { - result.add(node.method); + result.add(node.getProgramMethod()); nodeIterator.remove(); removed.add(node); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java index 5b94efb..e6f7cb6 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java
@@ -4,12 +4,14 @@ package com.android.tools.r8.ir.conversion; +import static com.google.common.base.Predicates.alwaysTrue; + 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.ProgramMethod; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; -import com.google.common.base.Predicates; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -25,21 +27,18 @@ } private void processClass(DexProgramClass clazz) { - clazz.forEachMethod(this::processMethod); + clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod); } - private void processMethod(DexEncodedMethod method) { - if (method.hasCode()) { - method.registerCodeReferences( - new InvokeExtractor(getOrCreateNode(method), Predicates.alwaysTrue())); - } + private void processMethod(ProgramMethod method) { + method.registerCodeReferences(new InvokeExtractor(getOrCreateNode(method), alwaysTrue())); } @Override boolean verifyAllMethodsWithCodeExists() { for (DexProgramClass clazz : appView.appInfo().classes()) { for (DexEncodedMethod method : clazz.methods()) { - assert !method.hasCode() || nodes.get(method.method) != null; + assert method.hasCode() == (nodes.get(method.method) != null); } } return true;
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 49ea06a..e6bd8bf 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
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; @@ -19,6 +20,7 @@ import com.android.tools.r8.graph.FieldAccessInfoCollection; import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult; import com.android.tools.r8.graph.LookupResult; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.Invoke; @@ -26,15 +28,13 @@ import com.android.tools.r8.ir.conversion.CallGraphBuilderBase.CycleEliminator.CycleEliminationResult; import com.android.tools.r8.logging.Log; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.Timing; -import com.google.common.collect.ImmutableSet; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashSet; @@ -52,7 +52,7 @@ final AppView<AppInfoWithLiveness> appView; private final FieldAccessInfoCollection<?> fieldAccessInfoCollection; final Map<DexMethod, Node> nodes = new IdentityHashMap<>(); - private final Map<DexMethod, Set<DexEncodedMethod>> possibleTargetsCache = + private final Map<DexMethod, ProgramMethodSet> possibleProgramTargetsCache = new ConcurrentHashMap<>(); CallGraphBuilderBase(AppView<AppInfoWithLiveness> appView) { @@ -97,9 +97,9 @@ return true; } - Node getOrCreateNode(DexEncodedMethod method) { + Node getOrCreateNode(ProgramMethod method) { synchronized (nodes) { - return nodes.computeIfAbsent(method.method, ignore -> new Node(method)); + return nodes.computeIfAbsent(method.getReference(), ignore -> new Node(method)); } } @@ -108,9 +108,9 @@ class InvokeExtractor extends UseRegistry { private final Node currentMethod; - private final Predicate<DexEncodedMethod> targetTester; + private final Predicate<ProgramMethod> targetTester; - InvokeExtractor(Node currentMethod, Predicate<DexEncodedMethod> targetTester) { + InvokeExtractor(Node currentMethod, Predicate<ProgramMethod> targetTester) { super(appView.dexItemFactory()); this.currentMethod = currentMethod; this.targetTester = targetTester; @@ -119,7 +119,7 @@ private void addClassInitializerTarget(DexProgramClass clazz) { assert clazz != null; if (clazz.hasClassInitializer()) { - addCallEdge(clazz.getClassInitializer(), false); + addCallEdge(clazz.getProgramClassInitializer(), false); } } @@ -131,36 +131,42 @@ } } - private void addCallEdge(DexEncodedMethod callee, boolean likelySpuriousCallEdge) { + private void addCallEdge(ProgramMethod callee, boolean likelySpuriousCallEdge) { if (!targetTester.test(callee)) { return; } - if (callee.accessFlags.isAbstract()) { + if (callee.getDefinition().isAbstract()) { // Not a valid target. return; } - if (appView.appInfo().isPinned(callee.method)) { + if (callee.getDefinition().isNative()) { + // We don't care about calls to native methods. + return; + } + if (appView.appInfo().isPinned(callee.getReference())) { // Since the callee is kept, we cannot inline it into the caller, and we also cannot collect // any optimization info for the method. Therefore, we drop the call edge to reduce the // total number of call graph edges, which should lead to fewer call graph cycles. return; } - assert callee.isProgramMethod(appView); getOrCreateNode(callee).addCallerConcurrently(currentMethod, likelySpuriousCallEdge); } private void addFieldReadEdge(DexEncodedMethod writer) { - assert !writer.accessFlags.isAbstract(); + addFieldReadEdge(writer.asProgramMethod(appView)); + } + + private void addFieldReadEdge(ProgramMethod writer) { + assert !writer.getDefinition().isAbstract(); if (!targetTester.test(writer)) { return; } - assert writer.isProgramMethod(appView); getOrCreateNode(writer).addReaderConcurrently(currentMethod); } private void processInvoke(Invoke.Type originalType, DexMethod originalMethod) { - DexEncodedMethod source = currentMethod.method; - DexMethod context = source.method; + ProgramMethod source = currentMethod.getProgramMethod(); + DexMethod context = source.getReference(); GraphLenseLookupResult result = appView.graphLense().lookupMethod(originalMethod, context, originalType); DexMethod method = result.getMethod(); @@ -173,19 +179,16 @@ processInvokeWithDynamicDispatch(type, target, context.holder); } } else { - DexEncodedMethod singleTarget = - appView.appInfo().lookupSingleTarget(type, method, context.holder, appView); + ProgramMethod singleTarget = + appView.appInfo().lookupSingleProgramTarget(type, method, context.holder, appView); if (singleTarget != null) { - assert !source.accessFlags.isBridge() || singleTarget != currentMethod.method; - DexProgramClass clazz = - asProgramClassOrNull(appView.definitionFor(singleTarget.holder())); - if (clazz != null) { - // For static invokes, the class could be initialized. - if (type == Invoke.Type.STATIC) { - addClassInitializerTarget(clazz); - } - addCallEdge(singleTarget, false); + assert !source.getDefinition().isBridge() + || singleTarget.getDefinition() != source.getDefinition(); + // For static invokes, the class could be initialized. + if (type == Invoke.Type.STATIC) { + addClassInitializerTarget(singleTarget.getHolder()); } + addCallEdge(singleTarget, false); } } } @@ -207,8 +210,8 @@ } boolean isInterface = type == Invoke.Type.INTERFACE; - Set<DexEncodedMethod> possibleTargets = - possibleTargetsCache.computeIfAbsent( + ProgramMethodSet possibleProgramTargets = + possibleProgramTargetsCache.computeIfAbsent( target, method -> { ResolutionResult resolution = @@ -218,27 +221,34 @@ resolution.lookupVirtualDispatchTargets( appView.definitionForProgramType(context), appView.appInfo()); if (lookupResult.isLookupResultSuccess()) { - Set<DexEncodedMethod> targets = new HashSet<>(); + ProgramMethodSet targets = ProgramMethodSet.create(); lookupResult .asLookupResultSuccess() .forEach( - methodTarget -> targets.add(methodTarget.getDefinition()), - lambdaTarget -> - // The call target will ultimately be the implementation method. - targets.add( - lambdaTarget.getImplementationMethod().getDefinition())); + methodTarget -> { + if (methodTarget.isProgramMethod()) { + targets.add(methodTarget.asProgramMethod()); + } + }, + lambdaTarget -> { + // The call target will ultimately be the implementation method. + DexClassAndMethod implementationMethod = + lambdaTarget.getImplementationMethod(); + if (implementationMethod.isProgramMethod()) { + targets.add(implementationMethod.asProgramMethod()); + } + }); return targets; } } return null; }); - if (possibleTargets != null) { + if (possibleProgramTargets != null) { boolean likelySpuriousCallEdge = - possibleTargets.size() >= appView.options().callGraphLikelySpuriousCallEdgeThreshold; - for (DexEncodedMethod possibleTarget : possibleTargets) { - if (possibleTarget.isProgramMethod(appView)) { - addCallEdge(possibleTarget, likelySpuriousCallEdge); - } + possibleProgramTargets.size() + >= appView.options().callGraphLikelySpuriousCallEdgeThreshold; + for (ProgramMethod possibleTarget : possibleProgramTargets) { + addCallEdge(possibleTarget, likelySpuriousCallEdge); } } } @@ -401,19 +411,19 @@ static class CycleEliminationResult { - private Map<DexEncodedMethod, Set<DexEncodedMethod>> removedCallEdges; + private Map<DexEncodedMethod, ProgramMethodSet> removedCallEdges; - CycleEliminationResult(Map<DexEncodedMethod, Set<DexEncodedMethod>> removedCallEdges) { + CycleEliminationResult(Map<DexEncodedMethod, ProgramMethodSet> removedCallEdges) { this.removedCallEdges = removedCallEdges; } - void forEachRemovedCaller(DexEncodedMethod callee, Consumer<DexEncodedMethod> fn) { - removedCallEdges.getOrDefault(callee, ImmutableSet.of()).forEach(fn); + void forEachRemovedCaller(ProgramMethod callee, Consumer<ProgramMethod> fn) { + removedCallEdges.getOrDefault(callee.getDefinition(), ProgramMethodSet.empty()).forEach(fn); } int numberOfRemovedCallEdges() { int numberOfRemovedCallEdges = 0; - for (Set<DexEncodedMethod> nodes : removedCallEdges.values()) { + for (ProgramMethodSet nodes : removedCallEdges.values()) { numberOfRemovedCallEdges += nodes.size(); } return numberOfRemovedCallEdges; @@ -445,7 +455,7 @@ private Map<Node, Set<Node>> writersToBeRemoved = new IdentityHashMap<>(); // Mapping from callee to the set of callers that were removed from the callee. - private Map<DexEncodedMethod, Set<DexEncodedMethod>> removedCallEdges = new IdentityHashMap<>(); + private Map<DexEncodedMethod, ProgramMethodSet> removedCallEdges = new IdentityHashMap<>(); // Set of nodes from which cycle elimination must be rerun to ensure that all cycles will be // removed. @@ -747,13 +757,13 @@ // All call edges where the callee is a method that should be force inlined must be kept, // to guarantee that the IR converter will process the callee before the caller. assert calleeOrWriter.hasCaller(callerOrReader); - return !calleeOrWriter.method.getOptimizationInfo().forceInline(); + return !calleeOrWriter.getMethod().getOptimizationInfo().forceInline(); } private void recordCallEdgeRemoval(Node caller, Node callee) { removedCallEdges - .computeIfAbsent(callee.method, ignore -> SetUtils.newIdentityHashSet(2)) - .add(caller.method); + .computeIfAbsent(callee.getMethod(), ignore -> ProgramMethodSet.create(2)) + .add(caller.getProgramMethod()); } private void recoverStack(LinkedList<Node> extractedCycle) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java index 937b0ca..993315b 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java
@@ -4,8 +4,8 @@ package com.android.tools.r8.ir.conversion; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.CallGraph.Node; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.google.common.collect.Sets; @@ -15,13 +15,15 @@ /** * Check if the <code>method</code> is guaranteed to only have a single call site. - * <p> - * For pinned methods (methods kept through Proguard keep rules) this will always answer - * <code>false</code>. + * + * <p>For pinned methods (methods kept through Proguard keep rules) this will always answer <code> + * false</code>. + * + * @param method */ - public abstract boolean hasSingleCallSite(DexMethod method); + public abstract boolean hasSingleCallSite(ProgramMethod method); - public abstract boolean hasDoubleCallSite(DexMethod method); + public abstract boolean hasDoubleCallSite(ProgramMethod method); public static CallSiteInformation empty() { return EmptyCallSiteInformation.EMPTY_INFO; @@ -32,12 +34,12 @@ private static final EmptyCallSiteInformation EMPTY_INFO = new EmptyCallSiteInformation(); @Override - public boolean hasSingleCallSite(DexMethod method) { + public boolean hasSingleCallSite(ProgramMethod method) { return false; } @Override - public boolean hasDoubleCallSite(DexMethod method) { + public boolean hasDoubleCallSite(ProgramMethod method) { return false; } } @@ -49,25 +51,25 @@ CallGraphBasedCallSiteInformation(AppView<AppInfoWithLiveness> appView, CallGraph graph) { for (Node node : graph.nodes) { - DexEncodedMethod encodedMethod = node.method; - DexMethod method = encodedMethod.method; + ProgramMethod method = node.getProgramMethod(); + DexMethod reference = method.getReference(); // For non-pinned methods and methods that override library methods we do not know the exact // number of call sites. - if (appView.appInfo().isPinned(method)) { + if (appView.appInfo().isPinned(reference)) { continue; } if (appView.options().disableInliningOfLibraryMethodOverrides - && encodedMethod.isLibraryMethodOverride().isTrue()) { + && method.getDefinition().isLibraryMethodOverride().isTrue()) { continue; } int numberOfCallSites = node.getNumberOfCallSites(); if (numberOfCallSites == 1) { - singleCallSite.add(method); + singleCallSite.add(reference); } else if (numberOfCallSites == 2) { - doubleCallSite.add(method); + doubleCallSite.add(reference); } } } @@ -79,8 +81,8 @@ * library method this always returns false. */ @Override - public boolean hasSingleCallSite(DexMethod method) { - return singleCallSite.contains(method); + public boolean hasSingleCallSite(ProgramMethod method) { + return singleCallSite.contains(method.getReference()); } /** @@ -90,8 +92,8 @@ * library method this always returns false. */ @Override - public boolean hasDoubleCallSite(DexMethod method) { - return doubleCallSite.contains(method); + public boolean hasDoubleCallSite(ProgramMethod method) { + return doubleCallSite.contains(method.getReference()); } } }
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 f13653a..d4c8040 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
@@ -26,6 +26,7 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.CanonicalPositions; import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.Monitor; @@ -201,7 +202,7 @@ private CfState state; private final List<CfCode.LocalVariableInfo> localVariables; private final CfCode code; - private final DexEncodedMethod method; + private final ProgramMethod method; private final Origin origin; private final AppView<?> appView; @@ -219,7 +220,7 @@ public CfSourceCode( CfCode code, List<CfCode.LocalVariableInfo> localVariables, - DexEncodedMethod method, + ProgramMethod method, DexMethod originalMethod, Position callerPosition, Origin origin, @@ -244,9 +245,13 @@ internalOutputMode = appView.options().getInternalOutputMode(); needsGeneratedMethodSynchronization = - !method.isProcessed() + !getMethod().isProcessed() && internalOutputMode.isGeneratingDex() - && method.accessFlags.isSynchronized(); + && getMethod().isSynchronized(); + } + + private DexEncodedMethod getMethod() { + return method.getDefinition(); } public Origin getOrigin() { @@ -385,15 +390,15 @@ inPrelude = true; state.buildPrelude(canonicalPositions.getPreamblePosition()); setLocalVariableLists(); - builder.buildArgumentsWithRewrittenPrototypeChanges(0, method, state::write); + builder.buildArgumentsWithRewrittenPrototypeChanges(0, getMethod(), state::write); // Add debug information for all locals at the initial label. Int2ReferenceMap<DebugLocalInfo> locals = getLocalVariables(0).locals; if (!locals.isEmpty()) { int firstLocalIndex = 0; - if (!method.isStatic()) { + if (!getMethod().isStatic()) { firstLocalIndex++; } - for (DexType value : method.method.proto.parameters.values) { + for (DexType value : getMethod().proto().parameters.values) { firstLocalIndex++; if (value.isLongType() || value.isDoubleType()) { firstLocalIndex++; @@ -412,10 +417,6 @@ inPrelude = false; } - private boolean isStatic() { - return method.accessFlags.isStatic(); - } - private boolean isCurrentlyGeneratingMethodSynchronization() { return currentlyGeneratingMethodSynchronization; } @@ -427,9 +428,9 @@ private void buildMethodEnterSynchronization(IRBuilder builder) { assert needsGeneratedMethodSynchronization; currentlyGeneratingMethodSynchronization = true; - DexType type = method.holder(); + DexType type = method.getHolderType(); int monitorRegister; - if (isStatic()) { + if (getMethod().isStatic()) { monitorRegister = state.push(type).register; state.pop(); builder.addConstClass(monitorRegister, type); @@ -633,7 +634,7 @@ return ((CfNew) instruction).getType(); } if (type.isUninitializedThis()) { - return method.holder(); + return method.getHolderType(); } assert type.isTop(); return null; @@ -777,7 +778,7 @@ .filter(insn -> insn instanceof CfPosition) .map(insn -> ((CfPosition) insn).getPosition()) .collect(Collectors.toList()), - method.method); + method.getReference()); } while (offset + 1 < code.getInstructions().size()) { CfInstruction insn = code.getInstructions().get(offset);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java index f19ab4d..cd9bcd5 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -36,10 +36,10 @@ import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair; import com.android.tools.r8.graph.DexDebugEntry; import com.android.tools.r8.graph.DexDebugInfo; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.CanonicalPositions; import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.Position; @@ -54,7 +54,7 @@ public class DexSourceCode implements SourceCode { private final DexCode code; - private final DexEncodedMethod method; + private final ProgramMethod method; // Mapping from instruction offset to instruction index in the DexCode instruction array. private final Map<Integer, Integer> offsetToInstructionIndex = new HashMap<>(); @@ -75,7 +75,7 @@ private final DexMethod originalMethod; public DexSourceCode( - DexCode code, DexEncodedMethod method, DexMethod originalMethod, Position callerPosition) { + DexCode code, ProgramMethod method, DexMethod originalMethod, Position callerPosition) { this.code = code; this.method = method; this.originalMethod = originalMethod; @@ -139,7 +139,7 @@ } builder.buildArgumentsWithRewrittenPrototypeChanges( code.registerSize - code.incomingRegisterSize, - method, + method.getDefinition(), DexSourceCode::doNothingWriteConsumer); }
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 35250d1..0dd98e2 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
@@ -23,7 +23,6 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DebugLocalInfo; import com.android.tools.r8.graph.DexCallSite; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItem; @@ -33,6 +32,7 @@ import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection; @@ -392,8 +392,8 @@ private int currentInstructionOffset = -1; final private ValueNumberGenerator valueNumberGenerator; - private final DexEncodedMethod method; - private DexEncodedMethod context; + private final ProgramMethod method; + private ProgramMethod context; public final AppView<?> appView; private final Origin origin; private final RewrittenPrototypeDescription prototypeChanges; @@ -429,18 +429,18 @@ private final IRMetadata metadata = new IRMetadata(); public static IRBuilder create( - DexEncodedMethod method, AppView<?> appView, SourceCode source, Origin origin) { + ProgramMethod method, AppView<?> appView, SourceCode source, Origin origin) { return new IRBuilder( method, appView, source, origin, - lookupPrototypeChanges(appView, method.method), + lookupPrototypeChanges(appView, method), new ValueNumberGenerator()); } public static IRBuilder createForInlining( - DexEncodedMethod method, + ProgramMethod method, AppView<?> appView, SourceCode source, Origin origin, @@ -448,15 +448,15 @@ ValueNumberGenerator valueNumberGenerator) { RewrittenPrototypeDescription protoChanges = processor.shouldApplyCodeRewritings(method) - ? lookupPrototypeChanges(appView, method.method) + ? lookupPrototypeChanges(appView, method) : RewrittenPrototypeDescription.none(); return new IRBuilder(method, appView, source, origin, protoChanges, valueNumberGenerator); } private static RewrittenPrototypeDescription lookupPrototypeChanges( - AppView<?> appView, DexMethod method) { + AppView<?> appView, ProgramMethod method) { RewrittenPrototypeDescription prototypeChanges = - appView.graphLense().lookupPrototypeChanges(method); + appView.graphLense().lookupPrototypeChanges(method.getReference()); if (Log.ENABLED && prototypeChanges.getArgumentInfoCollection().hasRemovedArguments()) { Log.info( IRBuilder.class, @@ -469,7 +469,7 @@ } private IRBuilder( - DexEncodedMethod method, + ProgramMethod method, AppView<?> appView, SourceCode source, Origin origin, @@ -486,7 +486,7 @@ } public DexEncodedMethod getMethod() { - return method; + return method.getDefinition(); } public RewrittenPrototypeDescription getPrototypeChanges() { @@ -494,7 +494,7 @@ } public boolean isDebugMode() { - return appView.options().debug || method.getOptimizationInfo().isReachabilitySensitive(); + return appView.options().debug || getMethod().getOptimizationInfo().isReachabilitySensitive(); } public Int2ReferenceSortedMap<BlockInfo> getCFG() { @@ -586,7 +586,7 @@ * @param context Under what context this IRCode is built. Either the current method or caller. * @return The list of basic blocks. First block is the main entry. */ - public IRCode build(DexEncodedMethod context) { + public IRCode build(ProgramMethod context) { assert source != null; source.setUp(); @@ -705,7 +705,7 @@ // types, those could be still less precise at one single call site, where specific arguments // will be passed during (double) inlining. Instead of adding assumptions and removing invalid // ones, it's better not to insert assumptions for inlinee in the beginning. - CallSiteOptimizationInfo callSiteOptimizationInfo = method.getCallSiteOptimizationInfo(); + CallSiteOptimizationInfo callSiteOptimizationInfo = getMethod().getCallSiteOptimizationInfo(); if (method == context && appView.callSiteOptimizationInfoPropagator() != null) { appView.callSiteOptimizationInfoPropagator() .applyCallSiteOptimizationInfo(ir, callSiteOptimizationInfo); @@ -724,7 +724,7 @@ } public void constrainType(Value value, ValueTypeConstraint constraint) { - value.constrainType(constraint, method.method, origin, appView.options().reporter); + value.constrainType(constraint, method.getReference(), origin, appView.options().reporter); } private void addImpreciseInstruction(ImpreciseMemberTypeInstruction instruction) { @@ -932,7 +932,8 @@ void addThisArgument(int register) { boolean receiverCouldBeNull = context != null && context != method; Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull(); - TypeElement receiverType = TypeElement.fromDexType(method.holder(), nullability, appView); + TypeElement receiverType = + TypeElement.fromDexType(method.getHolderType(), nullability, appView); addThisArgument(register, receiverType); } @@ -1463,16 +1464,11 @@ // 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.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.holder(); - type = Type.DIRECT; - } + if (invocationMethod.holder == method.getHolderType()) { + DexEncodedMethod directTarget = method.getHolder().lookupDirectMethod(invocationMethod); + if (directTarget != null && !directTarget.isStatic()) { + assert invocationMethod.holder == directTarget.holder(); + type = Type.DIRECT; } } } @@ -1764,7 +1760,8 @@ } public void addReturn(int value) { - if (method.method.proto.returnType == appView.dexItemFactory().voidType) { + DexType returnType = method.getDefinition().returnType(); + if (returnType.isVoidType()) { assert prototypeChanges.hasBeenChangedToReturnVoid(appView); addReturn(); } else { @@ -1772,7 +1769,7 @@ prototypeChanges.hasRewrittenReturnInfo() ? ValueTypeConstraint.fromDexType( prototypeChanges.getRewrittenReturnInfo().getOldType()) - : ValueTypeConstraint.fromDexType(method.method.proto.returnType); + : ValueTypeConstraint.fromDexType(returnType); Value in = readRegister(value, returnTypeConstraint); addReturn(new Return(in)); }
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 7b15fcc..31fd3c0 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
@@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.conversion; -import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources; import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources; @@ -16,7 +15,6 @@ import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexApplication.Builder; -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.DexMethod; @@ -25,6 +23,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.GraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.TypeChecker; import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation; import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis; @@ -90,7 +89,6 @@ import com.android.tools.r8.ir.regalloc.RegisterAllocator; import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.IdentifierNameStringMarker; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.MethodPosition; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis; @@ -105,11 +103,11 @@ import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Suppliers; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ListMultimap; -import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -589,33 +587,34 @@ private void convertMethods(DexProgramClass clazz) { boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory); // When converting all methods on a class always convert <clinit> first. - for (DexEncodedMethod method : clazz.directMethods()) { - if (method.isClassInitializer()) { - method.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive); - convertMethod(method); - break; - } + DexEncodedMethod classInitializer = clazz.getClassInitializer(); + if (classInitializer != null) { + classInitializer + .getMutableOptimizationInfo() + .setReachabilitySensitive(isReachabilitySensitive); + convertMethod(new ProgramMethod(clazz, classInitializer)); } - clazz.forEachMethod( + clazz.forEachProgramMethodMatching( + definition -> !definition.isClassInitializer(), method -> { - if (!method.isClassInitializer()) { - method.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive); - convertMethod(method); - } + DexEncodedMethod definition = method.getDefinition(); + definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive); + convertMethod(method); }); } - private void convertMethod(DexEncodedMethod method) { - if (method.getCode() != null) { - boolean matchesMethodFilter = options.methodMatchesFilter(method); + private void convertMethod(ProgramMethod method) { + DexEncodedMethod definition = method.getDefinition(); + if (definition.getCode() != null) { + boolean matchesMethodFilter = options.methodMatchesFilter(definition); if (matchesMethodFilter) { if (appView.options().enableNeverMergePrefixes) { for (DexString neverMergePrefix : neverMergePrefixes) { // Synthetic classes will always be merged. - if (method.holder().isD8R8SynthesizedClassType()) { + if (method.getHolderType().isD8R8SynthesizedClassType()) { continue; } - if (method.holder().descriptor.startsWith(neverMergePrefix)) { + if (method.getHolderType().descriptor.startsWith(neverMergePrefix)) { seenNeverMergePrefix.getAndSet(true); } else { seenNotNeverMergePrefix.getAndSet(true); @@ -639,17 +638,15 @@ } } if (options.isGeneratingClassFiles() - || !(options.passthroughDexCode && method.getCode().isDexCode())) { + || !(options.passthroughDexCode && definition.getCode().isDexCode())) { // We do not process in call graph order, so anything could be a leaf. rewriteCode( - method, - simpleOptimizationFeedback, - OneTimeMethodProcessor.getInstance(ImmutableList.of(method))); + method, simpleOptimizationFeedback, OneTimeMethodProcessor.getInstance(method)); } else { - assert method.getCode().isDexCode(); + assert definition.getCode().isDexCode(); } if (!options.isGeneratingClassFiles()) { - updateHighestSortingStrings(method); + updateHighestSortingStrings(definition); } } } @@ -815,7 +812,7 @@ outliner.applyOutliningCandidate(code); printMethod(code, "IR after outlining (SSA)", null); removeDeadCodeAndFinalizeIR( - code.method(), code, OptimizationFeedbackIgnore.getInstance(), Timing.empty()); + code.context(), code, OptimizationFeedbackIgnore.getInstance(), Timing.empty()); }, executorService); feedback.updateVisibleOptimizationInfo(); @@ -863,11 +860,11 @@ return builder.build(); } - private void waveStart(Collection<DexEncodedMethod> wave) { + private void waveStart(ProgramMethodSet wave) { onWaveDoneActions = Collections.synchronizedList(new ArrayList<>()); } - private void waveDone(Collection<DexEncodedMethod> wave) { + private void waveDone(ProgramMethodSet wave) { delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness()); delayedOptimizationFeedback.updateVisibleOptimizationInfo(); if (options.enableFieldAssignmentTracker) { @@ -904,13 +901,12 @@ Consumer<IRCode> consumer, ExecutorService executorService) throws ExecutionException { assert !options.skipIR; - Set<DexEncodedMethod> methods = outliner.getMethodsSelectedForOutlining(); ThreadUtils.processItems( - methods, + outliner.buildMethodsSelectedForOutlining(), method -> { - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); + IRCode code = method.buildIR(appView); assert code != null; - assert !method.getCode().isOutlineCode(); + assert !method.getDefinition().getCode().isOutlineCode(); // Instead of repeating all the optimizations of rewriteCode(), only run the // optimizations needed for outlining: rewriteMoveResult() to remove out-values on // StringBuilder/StringBuffer method invocations, and removeDeadCode() to remove @@ -924,15 +920,15 @@ } private void processSynthesizedServiceLoaderMethods( - DexClass synthesizedClass, ExecutorService executorService) throws ExecutionException { + DexProgramClass synthesizedClass, ExecutorService executorService) throws ExecutionException { ThreadUtils.processItems( - synthesizedClass.methods(), + synthesizedClass::forEachProgramMethod, this::forEachSynthesizedServiceLoaderMethod, executorService); } - private void forEachSynthesizedServiceLoaderMethod(DexEncodedMethod method) { - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); + private void forEachSynthesizedServiceLoaderMethod(ProgramMethod method) { + IRCode code = method.buildIR(appView); assert code != null; codeRewriter.rewriteMoveResult(code); removeDeadCodeAndFinalizeIR( @@ -1017,8 +1013,8 @@ public void optimizeSynthesizedClass( DexProgramClass clazz, ExecutorService executorService) throws ExecutionException { - Set<DexEncodedMethod> methods = Sets.newIdentityHashSet(); - clazz.forEachMethod(methods::add); + ProgramMethodSet methods = ProgramMethodSet.create(); + clazz.forEachProgramMethod(methods::add); // Process the generated class, but don't apply any outlining. processMethodsConcurrently(methods, executorService); } @@ -1026,15 +1022,15 @@ public void optimizeSynthesizedClasses( Collection<DexProgramClass> classes, ExecutorService executorService) throws ExecutionException { - Set<DexEncodedMethod> methods = Sets.newIdentityHashSet(); + ProgramMethodSet methods = ProgramMethodSet.create(); for (DexProgramClass clazz : classes) { - clazz.forEachMethod(methods::add); + clazz.forEachProgramMethod(methods::add); } processMethodsConcurrently(methods, executorService); } - public void optimizeSynthesizedMethod(DexEncodedMethod method) { - if (!method.isProcessed()) { + public void optimizeSynthesizedMethod(ProgramMethod method) { + if (!method.getDefinition().isProcessed()) { // Process the generated method, but don't apply any outlining. processMethod( method, @@ -1043,12 +1039,13 @@ } } - public void processMethodsConcurrently( - Collection<DexEncodedMethod> methods, ExecutorService executorService) + public void processMethodsConcurrently(ProgramMethodSet methods, ExecutorService executorService) throws ExecutionException { - OneTimeMethodProcessor processor = OneTimeMethodProcessor.getInstance(methods); - processor.forEachWave( - method -> processMethod(method, delayedOptimizationFeedback, processor), executorService); + if (!methods.isEmpty()) { + OneTimeMethodProcessor processor = OneTimeMethodProcessor.getInstance(methods); + processor.forEachWave( + method -> processMethod(method, delayedOptimizationFeedback, processor), executorService); + } } private String logCode(InternalOptions options, DexEncodedMethod method) { @@ -1067,14 +1064,15 @@ // TODO(b/140766440): Make this receive a list of CodeOptimizations to conduct. public Timing processMethod( - DexEncodedMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) { - Code code = method.getCode(); - boolean matchesMethodFilter = options.methodMatchesFilter(method); + ProgramMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) { + DexEncodedMethod definition = method.getDefinition(); + Code code = definition.getCode(); + boolean matchesMethodFilter = options.methodMatchesFilter(definition); if (code != null && matchesMethodFilter) { return rewriteCode(method, feedback, methodProcessor); } else { // Mark abstract methods as processed as well. - method.markProcessed(ConstraintWithTarget.NEVER); + definition.markProcessed(ConstraintWithTarget.NEVER); } return Timing.empty(); } @@ -1088,35 +1086,33 @@ } private Timing rewriteCode( - DexEncodedMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) { - Origin origin = appView.appInfo().originFor(method.holder()); + ProgramMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) { return ExceptionUtils.withOriginAttachmentHandler( - origin, - new MethodPosition(method.method), - () -> rewriteCodeInternal(method, feedback, methodProcessor, origin)); + method.getOrigin(), + new MethodPosition(method.getReference()), + () -> rewriteCodeInternal(method, feedback, methodProcessor)); } private Timing rewriteCodeInternal( - DexEncodedMethod method, - OptimizationFeedback feedback, - MethodProcessor methodProcessor, - Origin origin) { - + ProgramMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) { if (options.verbose) { options.reporter.info( new StringDiagnostic("Processing: " + method.toSourceString())); } if (Log.ENABLED) { - Log.debug(getClass(), "Original code for %s:\n%s", - method.toSourceString(), logCode(options, method)); + Log.debug( + getClass(), + "Original code for %s:\n%s", + method.toSourceString(), + logCode(options, method.getDefinition())); } if (options.skipIR) { - feedback.markProcessed(method, ConstraintWithTarget.NEVER); + feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER); return Timing.empty(); } - IRCode code = method.buildIR(appView, origin); + IRCode code = method.buildIR(appView); if (code == null) { - feedback.markProcessed(method, ConstraintWithTarget.NEVER); + feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER); return Timing.empty(); } return optimize(code, feedback, methodProcessor); @@ -1125,8 +1121,9 @@ // TODO(b/140766440): Convert all sub steps an implementer of CodeOptimization private Timing optimize( IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) { - DexEncodedMethod method = code.method(); - DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(method)); + ProgramMethod context = code.context(); + DexEncodedMethod method = context.getDefinition(); + DexProgramClass holder = context.getHolder(); assert holder != null; Timing timing = Timing.create(method.qualifiedName(), options); @@ -1157,7 +1154,7 @@ if (appView.graphLense().hasCodeRewritings()) { assert lensCodeRewriter != null; timing.begin("Lens rewrite"); - lensCodeRewriter.rewrite(code, method); + lensCodeRewriter.rewrite(code, context); timing.end(); } @@ -1175,7 +1172,7 @@ if (lambdaMerger != null) { timing.begin("Merge lambdas"); - lambdaMerger.rewriteCode(method, code, inliner, methodProcessor); + lambdaMerger.rewriteCode(code.context(), code, inliner, methodProcessor); timing.end(); assert code.isConsistentSSA(); } @@ -1211,7 +1208,7 @@ if (identifierNameStringMarker != null) { timing.begin("Decouple identifier-name strings"); - identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(method, code); + identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(code); timing.end(); assert code.isConsistentSSA(); } @@ -1242,14 +1239,14 @@ previous = printMethod(code, "IR after generated extension registry shrinking (SSA)", previous); - appView.withGeneratedMessageLiteShrinker(shrinker -> shrinker.run(method, code)); + appView.withGeneratedMessageLiteShrinker(shrinker -> shrinker.run(code)); timing.end(); previous = printMethod(code, "IR after generated message lite shrinking (SSA)", previous); if (!isDebugMode && options.enableInlining && inliner != null) { timing.begin("Inlining"); - inliner.performInlining(method, code, feedback, methodProcessor); + inliner.performInlining(code.context(), code, feedback, methodProcessor); timing.end(); assert code.verifyTypes(appView); } @@ -1428,7 +1425,7 @@ codeRewriter, stringOptimizer, enumValueOptimizer, - method, + code.context(), code, feedback, methodProcessor, @@ -1436,8 +1433,7 @@ Suppliers.memoize( () -> inliner.createDefaultOracle( - method, - code, + code.context(), methodProcessor, options.classInliningInstructionLimit, // Inlining instruction allowance is not needed for the class inliner since it @@ -1452,7 +1448,7 @@ if (d8NestBasedAccessDesugaring != null) { timing.begin("Desugar nest access"); - d8NestBasedAccessDesugaring.rewriteNestBasedAccesses(method, code, appView); + d8NestBasedAccessDesugaring.rewriteNestBasedAccesses(context, code, appView); timing.end(); assert code.isConsistentSSA(); } @@ -1491,7 +1487,7 @@ if (lambdaMerger != null) { timing.begin("Analyze lambda merging"); - lambdaMerger.analyzeCode(method, code); + lambdaMerger.analyzeCode(code.context(), code); timing.end(); assert code.isConsistentSSA(); } @@ -1515,7 +1511,7 @@ // Remove string switches prior to canonicalization to ensure that the constants that are // being introduced will be canonicalized if possible. timing.begin("Remove string switch"); - stringSwitchRemover.run(method, code); + stringSwitchRemover.run(code); timing.end(); } @@ -1549,7 +1545,7 @@ if (classStaticizer != null) { timing.begin("Identify staticizing candidates"); - classStaticizer.examineMethodCode(method, code); + classStaticizer.examineMethodCode(code); timing.end(); } @@ -1591,7 +1587,7 @@ printMethod(code, "Optimized IR (SSA)", previous); timing.begin("Finalize IR"); - finalizeIR(method, code, feedback, timing); + finalizeIR(code, feedback, timing); timing.end(); return timing; } @@ -1653,22 +1649,21 @@ } public void removeDeadCodeAndFinalizeIR( - DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) { + ProgramMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) { if (stringSwitchRemover != null) { - stringSwitchRemover.run(method, code); + stringSwitchRemover.run(code); } deadCodeRemover.run(code, timing); - finalizeIR(method, code, feedback, timing); + finalizeIR(code, feedback, timing); } - public void finalizeIR( - DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) { + public void finalizeIR(IRCode code, OptimizationFeedback feedback, Timing timing) { code.traceBlocks(); if (options.isGeneratingClassFiles()) { - finalizeToCf(method, code, feedback); + finalizeToCf(code, feedback); } else { assert options.isGeneratingDex(); - finalizeToDex(method, code, feedback, timing); + finalizeToDex(code, feedback, timing); } } @@ -1682,16 +1677,17 @@ feedback.markProcessed(method, ConstraintWithTarget.ALWAYS); } - private void finalizeToCf(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) { + private void finalizeToCf(IRCode code, OptimizationFeedback feedback) { + DexEncodedMethod method = code.method(); assert !method.getCode().isDexCode(); CfBuilder builder = new CfBuilder(appView, method, code); CfCode result = builder.build(deadCodeRemover); method.setCode(result, appView); - markProcessed(method, code, feedback); + markProcessed(code, feedback); } - private void finalizeToDex( - DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) { + private void finalizeToDex(IRCode code, OptimizationFeedback feedback, Timing timing) { + DexEncodedMethod method = code.method(); // Workaround massive dex2oat memory use for self-recursive methods. CodeRewriter.disableDex2OatInliningForSelfRecursiveMethods(appView, code); // Perform register allocation. @@ -1706,28 +1702,31 @@ } printMethod(code, "Final IR (non-SSA)", null); timing.begin("Marking processed"); - markProcessed(method, code, feedback); + markProcessed(code, feedback); timing.end(); } - private void markProcessed(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) { + private void markProcessed(IRCode code, OptimizationFeedback feedback) { // After all the optimizations have take place, we compute whether method should be inlined. + ProgramMethod method = code.context(); ConstraintWithTarget state = shouldComputeInliningConstraint(method) ? inliner.computeInliningConstraint(code, method) : ConstraintWithTarget.NEVER; - feedback.markProcessed(method, state); + feedback.markProcessed(method.getDefinition(), state); } - private boolean shouldComputeInliningConstraint(DexEncodedMethod method) { + private boolean shouldComputeInliningConstraint(ProgramMethod method) { if (!options.enableInlining || inliner == null) { return false; } - if (method.isClassInitializer() || method.getOptimizationInfo().isReachabilitySensitive()) { + DexEncodedMethod definition = method.getDefinition(); + if (definition.isClassInitializer() + || definition.getOptimizationInfo().isReachabilitySensitive()) { return false; } if (appView.appInfo().hasLiveness() - && appView.appInfo().withLiveness().isPinned(method.method)) { + && appView.appInfo().withLiveness().isPinned(method.getReference())) { return false; } return true;
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 e6f5e43..e94d07e 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
@@ -35,7 +35,6 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; @@ -49,6 +48,7 @@ import com.android.tools.r8.graph.DexValue.DexValueType; import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo; import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection; @@ -121,7 +121,7 @@ } /** Replace type appearances, invoke targets and field accesses with actual definitions. */ - public void rewrite(IRCode code, DexEncodedMethod method) { + public void rewrite(IRCode code, ProgramMethod method) { Set<Phi> affectedPhis = enumUnboxer != null ? enumUnboxer.rewriteCode(code) : Sets.newIdentityHashSet(); GraphLense graphLense = appView.graphLense(); @@ -214,10 +214,10 @@ continue; } if (invoke.isInvokeDirect()) { - checkInvokeDirect(method.method, invoke.asInvokeDirect()); + checkInvokeDirect(method.getReference(), invoke.asInvokeDirect()); } GraphLenseLookupResult lenseLookup = - graphLense.lookupMethod(invokedMethod, method.method, invoke.getType()); + graphLense.lookupMethod(invokedMethod, method.getReference(), invoke.getType()); DexMethod actualTarget = lenseLookup.getMethod(); Invoke.Type actualInvokeType = lenseLookup.getType(); if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) { @@ -334,7 +334,7 @@ DexField field = instanceGet.getField(); DexField actualField = graphLense.lookupField(field); DexMethod replacementMethod = - graphLense.lookupGetFieldForMethod(actualField, method.method); + graphLense.lookupGetFieldForMethod(actualField, method.getReference()); if (replacementMethod != null) { Value newOutValue = makeOutValue(current, code); iterator.replaceCurrentInstruction( @@ -359,7 +359,7 @@ DexField field = instancePut.getField(); DexField actualField = graphLense.lookupField(field); DexMethod replacementMethod = - graphLense.lookupPutFieldForMethod(actualField, method.method); + graphLense.lookupPutFieldForMethod(actualField, method.getReference()); if (replacementMethod != null) { iterator.replaceCurrentInstruction( new InvokeStatic(replacementMethod, null, current.inValues())); @@ -381,7 +381,7 @@ DexField field = staticGet.getField(); DexField actualField = graphLense.lookupField(field); DexMethod replacementMethod = - graphLense.lookupGetFieldForMethod(actualField, method.method); + graphLense.lookupGetFieldForMethod(actualField, method.getReference()); if (replacementMethod != null) { Value newOutValue = makeOutValue(current, code); iterator.replaceCurrentInstruction( @@ -405,7 +405,7 @@ DexField field = staticPut.getField(); DexField actualField = graphLense.lookupField(field); DexMethod replacementMethod = - graphLense.lookupPutFieldForMethod(actualField, method.method); + graphLense.lookupPutFieldForMethod(actualField, method.getReference()); if (replacementMethod != null) { iterator.replaceCurrentInstruction( new InvokeStatic(replacementMethod, current.outValue(), current.inValues())); @@ -582,7 +582,7 @@ return TypeElement.getNull(); } - public DexCallSite rewriteCallSite(DexCallSite callSite, DexEncodedMethod context) { + public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) { DexItemFactory dexItemFactory = appView.dexItemFactory(); DexProto newMethodProto = dexItemFactory.applyClassMappingToProto( @@ -682,7 +682,7 @@ } private List<DexValue> rewriteBootstrapArgs( - List<DexValue> bootstrapArgs, DexEncodedMethod method, MethodHandleUse use) { + List<DexValue> bootstrapArgs, ProgramMethod method, MethodHandleUse use) { List<DexValue> newBootstrapArgs = null; boolean changed = false; for (int i = 0; i < bootstrapArgs.size(); i++) { @@ -719,19 +719,21 @@ } private DexValueMethodHandle rewriteDexValueMethodHandle( - DexValueMethodHandle methodHandle, DexEncodedMethod context, MethodHandleUse use) { + DexValueMethodHandle methodHandle, ProgramMethod context, MethodHandleUse use) { DexMethodHandle oldHandle = methodHandle.value; DexMethodHandle newHandle = rewriteDexMethodHandle(oldHandle, context, use); return newHandle != oldHandle ? new DexValueMethodHandle(newHandle) : methodHandle; } private DexMethodHandle rewriteDexMethodHandle( - DexMethodHandle methodHandle, DexEncodedMethod context, MethodHandleUse use) { + DexMethodHandle methodHandle, ProgramMethod context, MethodHandleUse use) { if (methodHandle.isMethodHandle()) { DexMethod invokedMethod = methodHandle.asMethod(); MethodHandleType oldType = methodHandle.type; GraphLenseLookupResult lenseLookup = - appView.graphLense().lookupMethod(invokedMethod, context.method, oldType.toInvokeType()); + appView + .graphLense() + .lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType()); DexMethod rewrittenTarget = lenseLookup.getMethod(); DexMethod actualTarget; MethodHandleType newType;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java index 0c2eb60..b7ee7e1 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.conversion; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; public interface MethodProcessor { @@ -15,7 +15,7 @@ Phase getPhase(); - boolean shouldApplyCodeRewritings(DexEncodedMethod method); + boolean shouldApplyCodeRewritings(ProgramMethod method); default boolean isPrimary() { return getPhase() == Phase.PRIMARY; @@ -29,5 +29,5 @@ return CallSiteInformation.empty(); } - boolean isProcessedConcurrently(DexEncodedMethod method); + boolean isProcessedConcurrently(ProgramMethod method); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java index 939395f..071bfcb 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -3,10 +3,10 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.conversion; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.ThrowingConsumer; -import java.util.Collection; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -16,9 +16,9 @@ */ public class OneTimeMethodProcessor implements MethodProcessor { - private Collection<DexEncodedMethod> wave; + private ProgramMethodSet wave; - private OneTimeMethodProcessor(Collection<DexEncodedMethod> methodsToProcess) { + private OneTimeMethodProcessor(ProgramMethodSet methodsToProcess) { this.wave = methodsToProcess; } @@ -26,12 +26,16 @@ return new OneTimeMethodProcessor(null); } - public static OneTimeMethodProcessor getInstance(Collection<DexEncodedMethod> methodsToProcess) { + public static OneTimeMethodProcessor getInstance(ProgramMethod methodToProcess) { + return new OneTimeMethodProcessor(ProgramMethodSet.create(methodToProcess)); + } + + public static OneTimeMethodProcessor getInstance(ProgramMethodSet methodsToProcess) { return new OneTimeMethodProcessor(methodsToProcess); } @Override - public boolean shouldApplyCodeRewritings(DexEncodedMethod method) { + public boolean shouldApplyCodeRewritings(ProgramMethod method) { return true; } @@ -41,12 +45,12 @@ } @Override - public boolean isProcessedConcurrently(DexEncodedMethod method) { + public boolean isProcessedConcurrently(ProgramMethod method) { return wave != null && wave.contains(method); } public <E extends Exception> void forEachWave( - ThrowingConsumer<DexEncodedMethod, E> consumer, ExecutorService executorService) + ThrowingConsumer<ProgramMethod, E> consumer, ExecutorService executorService) throws ExecutionException { ThreadUtils.processItems(wave, consumer, executorService); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PartialCallGraphBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/PartialCallGraphBuilder.java index bfaf62c..e094867 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PartialCallGraphBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PartialCallGraphBuilder.java
@@ -4,17 +4,18 @@ package com.android.tools.r8.ir.conversion; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; -import java.util.Set; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; public class PartialCallGraphBuilder extends CallGraphBuilderBase { - private final Set<DexEncodedMethod> seeds; - PartialCallGraphBuilder(AppView<AppInfoWithLiveness> appView, Set<DexEncodedMethod> seeds) { + private final ProgramMethodSet seeds; + + PartialCallGraphBuilder(AppView<AppInfoWithLiveness> appView, ProgramMethodSet seeds) { super(appView); assert seeds != null && !seeds.isEmpty(); this.seeds = seeds; @@ -25,17 +26,14 @@ ThreadUtils.processItems(seeds, this::processMethod, executorService); } - private void processMethod(DexEncodedMethod method) { - if (method.hasCode()) { - method.registerCodeReferences( - new InvokeExtractor(getOrCreateNode(method), seeds::contains)); - } + private void processMethod(ProgramMethod method) { + method.registerCodeReferences(new InvokeExtractor(getOrCreateNode(method), seeds::contains)); } @Override boolean verifyAllMethodsWithCodeExists() { - for (DexEncodedMethod method : seeds) { - assert !method.hasCode() || nodes.get(method.method) != null; + for (ProgramMethod method : seeds) { + assert method.getDefinition().hasCode() == (nodes.get(method.getReference()) != null); } return true; }
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 328365e..7c136f6 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
@@ -6,36 +6,34 @@ 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.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.logging.Log; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.IROrdering; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.Map; -import java.util.Objects; -import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.stream.Collectors; public class PostMethodProcessor implements MethodProcessor { private final AppView<AppInfoWithLiveness> appView; private final Map<DexEncodedMethod, Collection<CodeOptimization>> methodsMap; - private final Deque<Set<DexEncodedMethod>> waves; - private Set<DexEncodedMethod> wave; - private final Set<DexEncodedMethod> processed = Sets.newIdentityHashSet(); + private final Deque<ProgramMethodSet> waves; + private ProgramMethodSet wave; + private final ProgramMethodSet processed = ProgramMethodSet.create(); private PostMethodProcessor( AppView<AppInfoWithLiveness> appView, @@ -52,7 +50,7 @@ } @Override - public boolean shouldApplyCodeRewritings(DexEncodedMethod method) { + public boolean shouldApplyCodeRewritings(ProgramMethod method) { assert !wave.contains(method); return !processed.contains(method); } @@ -60,30 +58,33 @@ public static class Builder { private final Collection<CodeOptimization> defaultCodeOptimizations; - private final Map<DexEncodedMethod, Collection<CodeOptimization>> methodsMap = - Maps.newIdentityHashMap(); + private final LongLivedProgramMethodSetBuilder methodsMap = + new LongLivedProgramMethodSetBuilder(); + private final Map<DexEncodedMethod, Collection<CodeOptimization>> optimizationsMap = + new IdentityHashMap<>(); Builder(Collection<CodeOptimization> defaultCodeOptimizations) { this.defaultCodeOptimizations = defaultCodeOptimizations; } private void put( - Set<DexEncodedMethod> methodsToRevisit, Collection<CodeOptimization> codeOptimizations) { + ProgramMethodSet methodsToRevisit, Collection<CodeOptimization> codeOptimizations) { if (codeOptimizations.isEmpty()) { // Nothing to conduct. return; } - for (DexEncodedMethod method : methodsToRevisit) { - methodsMap + for (ProgramMethod method : methodsToRevisit) { + methodsMap.add(method); + optimizationsMap .computeIfAbsent( - method, + method.getDefinition(), // Optimization order might matter, hence a collection that preserves orderings. k -> new LinkedHashSet<>()) .addAll(codeOptimizations); } } - public void put(Set<DexEncodedMethod> methodsToRevisit) { + public void put(ProgramMethodSet methodsToRevisit) { put(methodsToRevisit, defaultCodeOptimizations); } @@ -100,45 +101,54 @@ // new signature. The compiler needs to update the set of methods that must be reprocessed // according to the graph lens. public void mapDexEncodedMethods(AppView<?> appView) { - Map<DexEncodedMethod, Collection<CodeOptimization>> newMethodsMap = new IdentityHashMap<>(); - methodsMap.forEach( - (dexEncodedMethod, optimizations) -> { - newMethodsMap.put( - appView.graphLense().mapDexEncodedMethod(dexEncodedMethod, appView), optimizations); - }); - methodsMap.clear(); - methodsMap.putAll(newMethodsMap); + Map<DexEncodedMethod, Collection<CodeOptimization>> newOptimizationsMap = + new IdentityHashMap<>(); + optimizationsMap.forEach( + (method, optimizations) -> + newOptimizationsMap.put( + appView.graphLense().mapDexEncodedMethod(method, appView), optimizations)); + optimizationsMap.clear(); + optimizationsMap.putAll(newOptimizationsMap); } PostMethodProcessor build( AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing) throws ExecutionException { if (!appView.appInfo().reprocess.isEmpty()) { - put( - appView.appInfo().reprocess.stream() - .map(appView::definitionFor) - .filter(Objects::nonNull) - .collect(Collectors.toSet())); + ProgramMethodSet set = ProgramMethodSet.create(); + appView + .appInfo() + .reprocess + .forEach( + reference -> { + DexEncodedMethod definition = appView.definitionFor(reference); + if (definition != null) { + DexProgramClass clazz = + appView.definitionForHolder(definition).asProgramClass(); + set.createAndAdd(clazz, definition); + } + }); + put(set); } - if (methodsMap.keySet().isEmpty()) { + if (methodsMap.isEmpty()) { // Nothing to revisit. return null; } CallGraph callGraph = - new PartialCallGraphBuilder(appView, methodsMap.keySet()) + new PartialCallGraphBuilder(appView, methodsMap.build(appView)) .build(executorService, timing); - return new PostMethodProcessor(appView, methodsMap, callGraph); + return new PostMethodProcessor(appView, optimizationsMap, callGraph); } } - private Deque<Set<DexEncodedMethod>> createWaves(AppView<?> appView, CallGraph callGraph) { + private Deque<ProgramMethodSet> createWaves(AppView<?> appView, CallGraph callGraph) { IROrdering shuffle = appView.options().testing.irOrdering; - Deque<Set<DexEncodedMethod>> waves = new ArrayDeque<>(); + Deque<ProgramMethodSet> waves = new ArrayDeque<>(); int waveCount = 1; while (!callGraph.isEmpty()) { - Set<DexEncodedMethod> wave = callGraph.extractRoots(); - waves.addLast(shuffle.order(wave)); + ProgramMethodSet wave = callGraph.extractRoots(); + waves.addLast(wave); if (Log.ENABLED && Log.isLoggingEnabledFor(PostMethodProcessor.class)) { Log.info(getClass(), "Wave #%d: %d", waveCount++, wave.size()); } @@ -148,7 +158,7 @@ } @Override - public boolean isProcessedConcurrently(DexEncodedMethod method) { + public boolean isProcessedConcurrently(ProgramMethod method) { return wave != null && wave.contains(method); } @@ -160,7 +170,7 @@ ThreadUtils.processItems( wave, method -> { - Collection<CodeOptimization> codeOptimizations = methodsMap.get(method); + Collection<CodeOptimization> codeOptimizations = methodsMap.get(method.getDefinition()); assert codeOptimizations != null && !codeOptimizations.isEmpty(); forEachMethod(method, codeOptimizations, feedback); }, @@ -170,19 +180,18 @@ } private void forEachMethod( - DexEncodedMethod method, + ProgramMethod method, Collection<CodeOptimization> codeOptimizations, 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.holder()); if (appView.options().skipIR) { - feedback.markProcessed(method, ConstraintWithTarget.NEVER); + feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER); return; } - IRCode code = method.buildIR(appView, origin); + IRCode code = method.buildIR(appView); if (code == null) { - feedback.markProcessed(method, ConstraintWithTarget.NEVER); + feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER); return; } // TODO(b/140768815): Reprocessing may trigger more methods to revisit. Update waves on-the-fly.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java b/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java index 5ab18b3..7f06207 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PostOptimization.java
@@ -3,19 +3,16 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.conversion; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.Collection; -import java.util.Set; /** * An abstraction of optimizations that require post processing of methods. */ public interface PostOptimization { - /** - * @return a set of methods that need post processing. - */ - Set<DexEncodedMethod> methodsToRevisit(); + /** @return a set of methods that need post processing. */ + ProgramMethodSet methodsToRevisit(); // TODO(b/127694949): different CodeOptimization for primary processor v.s. post processor? // In that way, instead of internal state changes, such as COLLECT v.s. APPLY or REVISIT,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java index 5b26255..2a138cb 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -5,19 +5,17 @@ package com.android.tools.r8.ir.conversion; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.CallGraph.Node; import com.android.tools.r8.logging.Log; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.IROrdering; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.ThrowingFunction; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.Timing.TimingMerger; -import com.google.common.collect.Sets; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.ArrayDeque; -import java.util.Collection; import java.util.Deque; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -32,13 +30,13 @@ interface WaveStartAction { - void notifyWaveStart(Collection<DexEncodedMethod> wave); + void notifyWaveStart(ProgramMethodSet wave); } private final CallSiteInformation callSiteInformation; private final PostMethodProcessor.Builder postMethodProcessorBuilder; - private final Deque<Collection<DexEncodedMethod>> waves; - private Collection<DexEncodedMethod> wave; + private final Deque<ProgramMethodSet> waves; + private ProgramMethodSet wave; private PrimaryMethodProcessor( AppView<AppInfoWithLiveness> appView, @@ -65,9 +63,9 @@ } @Override - public boolean shouldApplyCodeRewritings(DexEncodedMethod method) { + public boolean shouldApplyCodeRewritings(ProgramMethod method) { assert !wave.contains(method); - return !method.isProcessed(); + return !method.getDefinition().isProcessed(); } @Override @@ -75,23 +73,22 @@ return callSiteInformation; } - private Deque<Collection<DexEncodedMethod>> createWaves( + private Deque<ProgramMethodSet> createWaves( AppView<?> appView, CallGraph callGraph, CallSiteInformation callSiteInformation) { InternalOptions options = appView.options(); - IROrdering shuffle = options.testing.irOrdering; - Deque<Collection<DexEncodedMethod>> waves = new ArrayDeque<>(); - + Deque<ProgramMethodSet> waves = new ArrayDeque<>(); Set<Node> nodes = callGraph.nodes; - Set<DexEncodedMethod> reprocessing = Sets.newIdentityHashSet(); + ProgramMethodSet reprocessing = ProgramMethodSet.create(); int waveCount = 1; while (!nodes.isEmpty()) { - Set<DexEncodedMethod> wave = callGraph.extractLeaves(); - for (DexEncodedMethod method : wave) { - if (callSiteInformation.hasSingleCallSite(method.method)) { - callGraph.cycleEliminationResult.forEachRemovedCaller(method, reprocessing::add); - } - } - waves.addLast(shuffle.order(wave)); + ProgramMethodSet wave = callGraph.extractLeaves(); + wave.forEach( + method -> { + if (callSiteInformation.hasSingleCallSite(method)) { + callGraph.cycleEliminationResult.forEachRemovedCaller(method, reprocessing::add); + } + }); + waves.addLast(wave); if (Log.ENABLED && Log.isLoggingEnabledFor(PrimaryMethodProcessor.class)) { Log.info(getClass(), "Wave #%d: %d", waveCount++, wave.size()); } @@ -104,7 +101,7 @@ } @Override - public boolean isProcessedConcurrently(DexEncodedMethod method) { + public boolean isProcessedConcurrently(ProgramMethod method) { return wave != null && wave.contains(method); } @@ -115,9 +112,9 @@ * processed at the same time is passed. This can be used to avoid races in concurrent processing. */ <E extends Exception> void forEachMethod( - ThrowingFunction<DexEncodedMethod, Timing, E> consumer, + ThrowingFunction<ProgramMethod, Timing, E> consumer, WaveStartAction waveStartAction, - Consumer<Collection<DexEncodedMethod>> waveDone, + Consumer<ProgramMethodSet> waveDone, Timing timing, ExecutorService executorService) throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java index ebce25c..52a7d0a 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement; @@ -60,7 +59,7 @@ this.throwingInfo = ThrowingInfo.defaultForConstString(appView.options()); } - void run(DexEncodedMethod method, IRCode code) { + void run(IRCode code) { if (!code.metadata().mayHaveStringSwitch()) { assert Streams.stream(code.instructions()).noneMatch(Instruction::isStringSwitch); return; @@ -98,8 +97,7 @@ } if (identifierNameStringMarker != null) { - identifierNameStringMarker.decoupleIdentifierNameStringsInBlocks( - method, code, newBlocksWithStrings); + identifierNameStringMarker.decoupleIdentifierNameStringsInBlocks(code, newBlocksWithStrings); } assert code.isConsistentSSA();
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 56c1ff5..47ee95f 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
@@ -18,11 +18,13 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; 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.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -172,7 +174,7 @@ private final AppView<? extends AppInfoWithClassHierarchy> appView; private final DexItemFactory dexItemFactory; private final InterfaceMethodRewriter rewriter; - private final Consumer<DexEncodedMethod> newSynthesizedMethodConsumer; + private final Consumer<ProgramMethod> newSynthesizedMethodConsumer; private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get(); private final boolean needsLibraryInfo; @@ -186,13 +188,13 @@ private final Map<DexClass, MethodSignatures> interfaceInfo = new IdentityHashMap<>(); // Mapping from actual program classes to the synthesized forwarding methods to be created. - private final Map<DexProgramClass, List<DexEncodedMethod>> newSyntheticMethods = + private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods = new IdentityHashMap<>(); ClassProcessor( AppView<? extends AppInfoWithClassHierarchy> appView, InterfaceMethodRewriter rewriter, - Consumer<DexEncodedMethod> newSynthesizedMethodConsumer) { + Consumer<ProgramMethod> newSynthesizedMethodConsumer) { this.appView = appView; this.dexItemFactory = appView.dexItemFactory(); this.rewriter = rewriter; @@ -219,13 +221,11 @@ } final void addSyntheticMethods() { - for (DexProgramClass clazz : newSyntheticMethods.keySet()) { - List<DexEncodedMethod> newForwardingMethods = newSyntheticMethods.get(clazz); - if (newForwardingMethods != null) { - clazz.addVirtualMethods(newForwardingMethods); - newForwardingMethods.forEach(newSynthesizedMethodConsumer); - } - } + newSyntheticMethods.forEach( + (clazz, newForwardingMethods) -> { + clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet()); + newForwardingMethods.forEach(newSynthesizedMethodConsumer); + }); } // Computes the set of method signatures that may need forwarding methods on derived classes. @@ -352,8 +352,10 @@ // Construction of actual forwarding methods. - private void addSyntheticMethod(DexProgramClass clazz, DexEncodedMethod newMethod) { - newSyntheticMethods.computeIfAbsent(clazz, key -> new ArrayList<>()).add(newMethod); + private void addSyntheticMethod(DexProgramClass clazz, DexEncodedMethod method) { + newSyntheticMethods + .computeIfAbsent(clazz, key -> ProgramMethodSet.create()) + .createAndAdd(clazz, method); } private void addICCEThrowingMethod(DexMethod method, DexClass clazz) {
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 b5c3f3e..4d5c7be 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
@@ -13,6 +13,7 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; 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.DexType; import com.android.tools.r8.graph.DexValue; @@ -20,6 +21,7 @@ import com.android.tools.r8.graph.DexValue.DexValueArray; import com.android.tools.r8.graph.DexValue.DexValueType; import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode; @@ -63,7 +65,7 @@ // List of methods that should be added to the next class. List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation = new LinkedList<>(); List<DexEncodedMethod> covariantReturnTypeMethods = new LinkedList<>(); - for (DexClass clazz : builder.getProgramClasses()) { + for (DexProgramClass clazz : builder.getProgramClasses()) { // Construct the methods that should be added to clazz. buildCovariantReturnTypeMethodsForClass( clazz, methodsWithCovariantReturnTypeAnnotation, covariantReturnTypeMethods); @@ -106,7 +108,7 @@ // CovariantReturnTypes annotations in the given DexClass. Adds the newly constructed, synthetic // methods to the list covariantReturnTypeMethods. private void buildCovariantReturnTypeMethodsForClass( - DexClass clazz, + DexProgramClass clazz, List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation, List<DexEncodedMethod> covariantReturnTypeMethods) { for (DexEncodedMethod method : clazz.virtualMethods()) { @@ -130,7 +132,9 @@ // variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic // methods to the list covariantReturnTypeMethods. private void buildCovariantReturnTypeMethodsForMethod( - DexClass clazz, DexEncodedMethod method, List<DexEncodedMethod> covariantReturnTypeMethods) { + DexProgramClass clazz, + DexEncodedMethod method, + List<DexEncodedMethod> covariantReturnTypeMethods) { assert methodHasCovariantReturnTypeAnnotation(method); for (DexType covariantReturnType : getCovariantReturnTypes(clazz, method)) { DexEncodedMethod covariantReturnTypeMethod = @@ -145,7 +149,7 @@ // // Note: any "synchronized" or "strictfp" modifier could be dropped safely. private DexEncodedMethod buildCovariantReturnTypeMethod( - DexClass clazz, DexEncodedMethod method, DexType covariantReturnType) { + DexProgramClass clazz, DexEncodedMethod method, DexType covariantReturnType) { DexProto newProto = factory.createProto( covariantReturnType, method.method.proto.parameters, method.method.proto.shorty); @@ -170,7 +174,8 @@ new SynthesizedCode(forwardSourceCodeBuilder::build), true); // Optimize to generate DexCode instead of SynthesizedCode. - converter.optimizeSynthesizedMethod(newVirtualMethod); + ProgramMethod programMethod = new ProgramMethod(clazz, newVirtualMethod); + converter.optimizeSynthesizedMethod(programMethod); return newVirtualMethod; }
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 738f4aa..f691760 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
@@ -10,7 +10,9 @@ import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -21,6 +23,7 @@ import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.utils.ThreadUtils; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -41,20 +44,17 @@ // Maps a nest host to a class met which has that nest host. // The value is used because the nest host might be missing. - private final Map<DexType, DexClass> metNestHosts = new ConcurrentHashMap<>(); + private final Map<DexType, DexProgramClass> metNestHosts = new ConcurrentHashMap<>(); public D8NestBasedAccessDesugaring(AppView<?> appView) { super(appView); } - public void rewriteNestBasedAccesses( - DexEncodedMethod encodedMethod, IRCode code, AppView<?> appView) { - DexClass currentClass = appView.definitionFor(encodedMethod.holder()); - assert currentClass != null; - if (!currentClass.isInANest()) { + public void rewriteNestBasedAccesses(ProgramMethod method, IRCode code, AppView<?> appView) { + if (!method.getHolder().isInANest()) { return; } - metNestHosts.put(currentClass.getNestHost(), currentClass); + metNestHosts.put(method.getHolder().getNestHost(), method.getHolder()); ListIterator<BasicBlock> blocks = code.listIterator(); while (blocks.hasNext()) { @@ -67,8 +67,7 @@ DexMethod methodCalled = invokeMethod.getInvokedMethod(); DexEncodedMethod encodedMethodCalled = methodCalled.holder.isClassType() ? appView.definitionFor(methodCalled) : null; - if (encodedMethodCalled != null - && invokeRequiresRewriting(encodedMethodCalled, currentClass)) { + if (encodedMethodCalled != null && invokeRequiresRewriting(encodedMethodCalled, method)) { DexMethod bridge = ensureInvokeBridge(encodedMethodCalled); if (encodedMethodCalled.isInstanceInitializer()) { instructions.previous(); @@ -87,7 +86,7 @@ } else if (instruction.isFieldInstruction()) { DexEncodedField encodedField = appView.definitionFor(instruction.asFieldInstruction().getField()); - if (encodedField != null && fieldAccessRequiresRewriting(encodedField, currentClass)) { + if (encodedField != null && fieldAccessRequiresRewriting(encodedField, method)) { if (instruction.isInstanceGet() || instruction.isStaticGet()) { DexMethod bridge = ensureFieldAccessBridge(encodedField, true); instructions.replaceCurrentInstruction( @@ -106,7 +105,7 @@ private void processNestsConcurrently(ExecutorService executorService) throws ExecutionException { List<Future<?>> futures = new ArrayList<>(); - for (DexClass clazz : metNestHosts.values()) { + for (DexProgramClass clazz : metNestHosts.values()) { futures.add(asyncProcessNest(clazz, executorService)); } ThreadUtils.awaitFutures(futures); @@ -118,17 +117,15 @@ addDeferredBridges(putFieldBridges.values()); } - private void addDeferredBridges(Collection<DexEncodedMethod> bridges) { - for (DexEncodedMethod bridge : bridges) { - DexClass holder = definitionFor(bridge.holder()); - assert holder != null && holder.isProgramClass(); - holder.asProgramClass().addMethod(bridge); + private void addDeferredBridges(Collection<ProgramMethod> bridges) { + for (ProgramMethod bridge : bridges) { + bridge.getHolder().addMethod(bridge.getDefinition()); } } private void optimizeDeferredBridgesConcurrently( ExecutorService executorService, IRConverter converter) throws ExecutionException { - Collection<DexEncodedMethod> methods = new ArrayList<>(); + ProgramMethodSet methods = ProgramMethodSet.create(); methods.addAll(bridges.values()); methods.addAll(getFieldBridges.values()); methods.addAll(putFieldBridges.values()); @@ -147,7 +144,7 @@ // In D8, programClass are processed on the fly so they do not need to be processed again here. @Override protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) { - return clazz.isNotProgramClass(); + return clazz.isClasspathClass(); } @Override
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 b9913cc..ca7fdec 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
@@ -15,6 +15,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.BasicBlock; @@ -29,14 +30,13 @@ import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.DescriptorUtils; -import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.WorkList; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.ListIterator; @@ -72,7 +72,7 @@ private final Mode mode; private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor; private final Map<DexClass, Set<DexEncodedMethod>> callBackMethods = new IdentityHashMap<>(); - private final Map<DexClass, List<DexEncodedMethod>> pendingCallBackMethods = + private final Map<DexProgramClass, List<DexEncodedMethod>> pendingCallBackMethods = new IdentityHashMap<>(); private final Set<DexMethod> trackedCallBackAPIs; private final Set<DexMethod> trackedAPIs; @@ -111,9 +111,9 @@ } if (!canGenerateWrappersAndCallbacks()) { - assert validateCallbackWasGeneratedInEnqueuer(code.method()); + assert validateCallbackWasGeneratedInEnqueuer(code.context()); } else { - registerCallbackIfRequired(code.method()); + registerCallbackIfRequired(code.context()); } ListIterator<BasicBlock> blockIterator = code.listIterator(); @@ -136,14 +136,12 @@ } } - private boolean validateCallbackWasGeneratedInEnqueuer(DexEncodedMethod encodedMethod) { - if (!shouldRegisterCallback(encodedMethod)) { + private boolean validateCallbackWasGeneratedInEnqueuer(ProgramMethod method) { + if (!shouldRegisterCallback(method)) { return true; } - DexProgramClass holderClass = appView.definitionForProgramType(encodedMethod.holder()); - DexMethod installedCallback = - methodWithVivifiedTypeInSignature(encodedMethod.method, holderClass.type, appView); - assert holderClass.lookupMethod(installedCallback) != null; + DexMethod installedCallback = methodWithVivifiedTypeInSignature(method, appView); + assert method.getHolder().lookupMethod(installedCallback) != null; return true; } @@ -159,15 +157,13 @@ return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.proto, appView); } - public void registerCallbackIfRequired(DexEncodedMethod encodedMethod) { - if (shouldRegisterCallback(encodedMethod)) { - DexClass dexClass = appView.definitionFor(encodedMethod.holder()); - assert dexClass != null; - registerCallback(dexClass, encodedMethod); + public void registerCallbackIfRequired(ProgramMethod method) { + if (shouldRegisterCallback(method)) { + registerCallback(method); } } - private boolean shouldRegisterCallback(DexEncodedMethod encodedMethod) { + private boolean shouldRegisterCallback(ProgramMethod method) { // Any override of a library method can be called by the library. // We duplicate the method to have a vivified type version callable by the library and // a type version callable by the program. We need to add the vivified version to the rootset @@ -175,36 +171,32 @@ // library type), but the enqueuer cannot see that. // To avoid too much computation we first look if the method would need to be rewritten if // it would override a library method, then check if it overrides a library method. - if (encodedMethod.isPrivateMethod() - || encodedMethod.isStatic() - || encodedMethod.isLibraryMethodOverride().isFalse()) { + DexEncodedMethod definition = method.getDefinition(); + if (definition.isPrivateMethod() + || definition.isStatic() + || definition.isLibraryMethodOverride().isFalse()) { return false; } - DexMethod method = encodedMethod.method; - if (method.holder.isArrayType() - || !appView.rewritePrefix.hasRewrittenTypeInSignature(method.proto, appView) + if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.proto(), appView) || appView .options() .desugaredLibraryConfiguration .getEmulateLibraryInterface() - .containsKey(method.holder)) { + .containsKey(method.getHolderType())) { return false; } - DexClass dexClass = appView.definitionFor(method.holder); - if (dexClass == null) { - return false; - } - return overridesLibraryMethod(dexClass, method); + return overridesLibraryMethod(method); } - private boolean overridesLibraryMethod(DexClass theClass, DexMethod method) { + private boolean overridesLibraryMethod(ProgramMethod method) { // We look up everywhere to see if there is a supertype/interface implementing the method... + DexProgramClass holder = method.getHolder(); WorkList<DexType> workList = WorkList.newIdentityWorkList(); - workList.addIfNotSeen(theClass.interfaces.values); + workList.addIfNotSeen(holder.interfaces.values); boolean foundOverrideToRewrite = false; // There is no methods with desugared types on Object. - if (theClass.superType != factory.objectType) { - workList.addIfNotSeen(theClass.superType); + if (holder.superType != factory.objectType) { + workList.addIfNotSeen(holder.superType); } while (workList.hasNext()) { DexType current = workList.next(); @@ -219,7 +211,7 @@ if (!dexClass.isLibraryClass() && !appView.options().isDesugaredLibraryCompilation()) { continue; } - DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method); + DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method.getReference()); if (dexEncodedMethod != null) { // In this case, the object will be wrapped. if (appView.rewritePrefix.hasRewrittenType(dexClass.type, appView)) { @@ -231,34 +223,41 @@ return foundOverrideToRewrite; } - private synchronized void registerCallback(DexClass dexClass, DexEncodedMethod originalMethod) { + private synchronized void registerCallback(ProgramMethod method) { // In R8 we should be in the enqueuer, therefore we can duplicate a default method and both // methods will be desugared. // In D8, this happens after interface method desugaring, we cannot introduce new default // methods, but we do not need to since this is a library override (invokes will resolve) and // all implementors have been enhanced with a forwarding method which will be duplicated. if (!appView.enableWholeProgramOptimizations()) { - if (dexClass.isInterface() - && originalMethod.isDefaultMethod() + if (method.getHolder().isInterface() + && method.getDefinition().isDefaultMethod() && (!appView.options().canUseDefaultAndStaticInterfaceMethods() || appView.options().isDesugaredLibraryCompilation())) { return; } } if (trackedCallBackAPIs != null) { - trackedCallBackAPIs.add(originalMethod.method); + trackedCallBackAPIs.add(method.getReference()); } - addCallBackSignature(dexClass, originalMethod); + addCallBackSignature(method); } - private synchronized void addCallBackSignature(DexClass dexClass, DexEncodedMethod method) { - assert dexClass.type == method.holder(); - if (callBackMethods.computeIfAbsent(dexClass, key -> new HashSet<>()).add(method)) { - pendingCallBackMethods.computeIfAbsent(dexClass, key -> new ArrayList<>()).add(method); + private synchronized void addCallBackSignature(ProgramMethod method) { + DexProgramClass holder = method.getHolder(); + DexEncodedMethod definition = method.getDefinition(); + if (callBackMethods.computeIfAbsent(holder, key -> Sets.newIdentityHashSet()).add(definition)) { + pendingCallBackMethods.computeIfAbsent(holder, key -> new ArrayList<>()).add(definition); } } public static DexMethod methodWithVivifiedTypeInSignature( + ProgramMethod method, AppView<?> appView) { + return methodWithVivifiedTypeInSignature( + method.getReference(), method.getHolderType(), appView); + } + + public static DexMethod methodWithVivifiedTypeInSignature( DexMethod originalMethod, DexType holder, AppView<?> appView) { DexType[] newParameters = originalMethod.proto.parameters.values.clone(); int index = 0; @@ -285,28 +284,32 @@ if (appView.enableWholeProgramOptimizations()) { return; } - List<DexEncodedMethod> callbacks = generateCallbackMethods(); + ProgramMethodSet callbacks = generateCallbackMethods(); irConverter.processMethodsConcurrently(callbacks, executorService); wrapperSynthesizor.finalizeWrappersForD8(builder, irConverter, executorService); } - public List<DexEncodedMethod> generateCallbackMethods() { + public ProgramMethodSet generateCallbackMethods() { if (appView.options().testing.trackDesugaredAPIConversions) { generateTrackDesugaredAPIWarnings(trackedAPIs, ""); generateTrackDesugaredAPIWarnings(trackedCallBackAPIs, "callback "); trackedAPIs.clear(); trackedCallBackAPIs.clear(); } - List<DexEncodedMethod> result = new ArrayList<>(); + ProgramMethodSet allCallbackMethods = ProgramMethodSet.create(); pendingCallBackMethods.forEach( (clazz, callbacks) -> { - List<DexEncodedMethod> generated = - ListUtils.map(callbacks, callback -> generateCallbackMethod(callback, clazz)); - clazz.addVirtualMethods(generated); - result.addAll(generated); + List<DexEncodedMethod> newVirtualMethods = new ArrayList<>(); + callbacks.forEach( + callback -> { + ProgramMethod callbackMethod = generateCallbackMethod(callback, clazz); + newVirtualMethods.add(callbackMethod.getDefinition()); + allCallbackMethods.add(callbackMethod); + }); + clazz.addVirtualMethods(newVirtualMethods); }); pendingCallBackMethods.clear(); - return result; + return allCallbackMethods; } public List<DexProgramClass> synthesizeWrappers( @@ -319,21 +322,21 @@ return wrapperSynthesizor.synthesizeClasspathMock(classToMock, mockType, mockIsInterface); } - private DexEncodedMethod generateCallbackMethod( - DexEncodedMethod originalMethod, DexClass dexClass) { + private ProgramMethod generateCallbackMethod( + DexEncodedMethod originalMethod, DexProgramClass clazz) { DexMethod methodToInstall = - methodWithVivifiedTypeInSignature(originalMethod.method, dexClass.type, appView); + methodWithVivifiedTypeInSignature(originalMethod.method, clazz.type, appView); CfCode cfCode = new APIConverterWrapperCfCodeProvider( - appView, originalMethod.method, null, this, dexClass.isInterface()) + appView, originalMethod.method, null, this, clazz.isInterface()) .generateCfCode(); - DexEncodedMethod newDexEncodedMethod = + DexEncodedMethod newMethod = wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode); - newDexEncodedMethod.setCode(cfCode, appView); + newMethod.setCode(cfCode, appView); if (originalMethod.isLibraryMethodOverride().isTrue()) { - newDexEncodedMethod.setLibraryMethodOverride(OptionalBool.TRUE); + newMethod.setLibraryMethodOverride(OptionalBool.TRUE); } - return newDexEncodedMethod; + return new ProgramMethod(clazz, newMethod); } private void generateTrackDesugaredAPIWarnings(Set<DexMethod> tracked, String inner) {
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 ecf9d2e..a640fb6 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
@@ -30,6 +30,7 @@ import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.utils.StringDiagnostic; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -274,28 +275,27 @@ map.putIfAbsent(emulatedDispatchMethod.holder, new ArrayList<>(1)); map.get(emulatedDispatchMethod.holder).add(emulatedDispatchMethod); } - List<DexEncodedMethod> addedMethods = new ArrayList<>(); + ProgramMethodSet addedMethods = ProgramMethodSet.create(); for (DexProgramClass clazz : appView.appInfo().classes()) { if (clazz.superType == null) { assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString(); continue; } - DexClass dexClass = appView.definitionFor(clazz.superType); + DexClass superclass = appView.definitionFor(clazz.superType); // Only performs computation if superclass is a library class, but not object to filter out // the most common case. - if (dexClass != null - && dexClass.isLibraryClass() - && dexClass.type != appView.dexItemFactory().objectType) { - for (DexType dexType : map.keySet()) { - if (inherit(dexClass.asLibraryClass(), dexType, emulatedDispatchMethods)) { - addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType))); - } - } + if (superclass != null + && superclass.isLibraryClass() + && superclass.type != appView.dexItemFactory().objectType) { + map.forEach( + (type, methods) -> { + if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) { + addInterfacesAndForwardingMethods( + clazz, methods, method -> addedMethods.createAndAdd(clazz, method)); + } + }); } } - if (addedMethods.isEmpty()) { - return; - } converter.processMethodsConcurrently(addedMethods, executorService); } @@ -318,14 +318,15 @@ return false; } - private List<DexEncodedMethod> addInterfacesAndForwardingMethods( - DexProgramClass clazz, List<DexMethod> dexMethods) { + private void addInterfacesAndForwardingMethods( + DexProgramClass clazz, + List<DexMethod> methods, + Consumer<DexEncodedMethod> newForwardingMethodsConsumer) { // DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding // methods. // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor // applies up to 24. - List<DexEncodedMethod> newForwardingMethods = new ArrayList<>(); - for (DexMethod dexMethod : dexMethods) { + for (DexMethod dexMethod : methods) { DexType[] newInterfaces = Arrays.copyOf(clazz.interfaces.values, clazz.interfaces.size() + 1); newInterfaces[newInterfaces.length - 1] = dispatchInterfaceTypeFor(dexMethod); @@ -334,10 +335,9 @@ if (dexEncodedMethod == null) { DexEncodedMethod newMethod = createForwardingMethod(dexMethod, clazz); clazz.addVirtualMethod(newMethod); - newForwardingMethods.add(newMethod); + newForwardingMethodsConsumer.accept(newMethod); } } - return newForwardingMethods; } private DexEncodedMethod createForwardingMethod(DexMethod target, DexClass clazz) {
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 6467824..5d494bb 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
@@ -28,6 +28,7 @@ import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.GraphLense.NestedGraphLense; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -46,6 +47,7 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.StringDiagnostic; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Arrays; @@ -110,7 +112,7 @@ // All forwarding methods generated during desugaring. We don't synchronize access // to this collection since it is only filled in ClassProcessor running synchronously. - private final Set<DexEncodedMethod> synthesizedMethods = Sets.newIdentityHashSet(); + private final ProgramMethodSet synthesizedMethods = ProgramMethodSet.create(); // Caches default interface method info for already processed interfaces. private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>(); @@ -675,28 +677,30 @@ emulateLibraryClassFlags.setFinal(); emulateLibraryClassFlags.setSynthetic(); emulateLibraryClassFlags.setPublic(); - synthesizedMethods.addAll(emulationMethods); - return new DexProgramClass( - emulateLibraryClassType, - null, - new SynthesizedOrigin("interface desugaring (libs)", getClass()), - emulateLibraryClassFlags, - factory.objectType, - DexTypeList.empty(), - theInterface.sourceFile, - null, - Collections.emptyList(), - null, - Collections.emptyList(), - DexAnnotationSet.empty(), - DexEncodedField.EMPTY_ARRAY, - DexEncodedField.EMPTY_ARRAY, - // All synthesized methods are static in this case. - emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY), - DexEncodedMethod.EMPTY_ARRAY, - factory.getSkipNameValidationForTesting(), - DexProgramClass::checksumFromType, - Collections.singletonList(theInterface)); + DexProgramClass clazz = + new DexProgramClass( + emulateLibraryClassType, + null, + new SynthesizedOrigin("interface desugaring (libs)", getClass()), + emulateLibraryClassFlags, + factory.objectType, + DexTypeList.empty(), + theInterface.sourceFile, + null, + Collections.emptyList(), + null, + Collections.emptyList(), + DexAnnotationSet.empty(), + DexEncodedField.EMPTY_ARRAY, + DexEncodedField.EMPTY_ARRAY, + // All synthesized methods are static in this case. + emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY), + DexEncodedMethod.EMPTY_ARRAY, + factory.getSkipNameValidationForTesting(), + DexProgramClass::checksumFromType, + Collections.singletonList(theInterface)); + clazz.forEachProgramMethod(synthesizedMethods::add); + return clazz; } private static String getEmulateLibraryInterfaceClassDescriptor(String descriptor) { @@ -1020,7 +1024,8 @@ } } for (Entry<DexLibraryClass, Set<DexProgramClass>> entry : requiredDispatchClasses.entrySet()) { - synthesizedMethods.addAll(processor.process(entry.getKey(), entry.getValue())); + DexProgramClass dispatchClass = processor.process(entry.getKey(), entry.getValue()); + dispatchClass.forEachProgramMethod(synthesizedMethods::add); } if (appView.enableWholeProgramOptimizations()) { appView.setGraphLense(graphLensBuilder.build(appView.dexItemFactory(), appView.graphLense())); @@ -1029,7 +1034,7 @@ } private void processClasses( - Builder<?> builder, Flavor flavour, Consumer<DexEncodedMethod> newSynthesizedMethodConsumer) { + Builder<?> builder, Flavor flavour, Consumer<ProgramMethod> newSynthesizedMethodConsumer) { ClassProcessor processor = new ClassProcessor(appView, this, newSynthesizedMethodConsumer); // First we compute all desugaring *without* introducing forwarding methods. for (DexProgramClass clazz : builder.getProgramClasses()) {
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 ab5a9cd..f1a499a 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
@@ -232,7 +232,7 @@ return c -> 7 * checksum; } - List<DexEncodedMethod> process(DexLibraryClass iface, Set<DexProgramClass> callers) { + DexProgramClass process(DexLibraryClass iface, Set<DexProgramClass> callers) { assert iface.isInterface(); // The list of methods to be created in dispatch class. @@ -305,7 +305,7 @@ DexProgramClass::checksumFromType, callers); syntheticClasses.put(iface.type, dispatchClass); - return dispatchMethods; + return dispatchClass; } private boolean canMoveToCompanionClass(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java index 88d21d4..05c9c66 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -512,7 +512,7 @@ final Invoke.Type invokeType; private boolean hasEnsuredAccessibility; - private DexEncodedMethod accessibilityBridge; + private ProgramMethod accessibilityBridge; Target(DexMethod callTarget, Invoke.Type invokeType) { assert callTarget != null; @@ -522,10 +522,10 @@ } // Ensure access of the referenced symbol(s). - abstract DexEncodedMethod ensureAccessibility(boolean allowMethodModification); + abstract ProgramMethod ensureAccessibility(boolean allowMethodModification); // Ensure access of the referenced symbol(s). - public DexEncodedMethod ensureAccessibilityIfNeeded(boolean allowMethodModification) { + public ProgramMethod ensureAccessibilityIfNeeded(boolean allowMethodModification) { if (!hasEnsuredAccessibility) { accessibilityBridge = ensureAccessibility(allowMethodModification); hasEnsuredAccessibility = true; @@ -552,7 +552,7 @@ // The only case where we do Lambda desugaring with Cf to Cf is in L8. // If the compilation is not coreLibraryCompilation, then the assertion // implMethodHolder != null may fail, hence the assertion. - assert options.isDesugaredLibraryCompilation() || options.enableCfInterfaceMethodDesugaring; + assert options.cfToCfDesugar; DexMethod implMethod = descriptor.implHandle.asMethod(); DexClass implMethodHolder = definitionFor(implMethod.holder); if (implMethodHolder == null) { @@ -574,7 +574,7 @@ } @Override - DexEncodedMethod ensureAccessibility(boolean allowMethodModification) { + ProgramMethod ensureAccessibility(boolean allowMethodModification) { return null; } } @@ -590,7 +590,7 @@ } @Override - DexEncodedMethod ensureAccessibility(boolean allowMethodModification) { + ProgramMethod ensureAccessibility(boolean allowMethodModification) { // We already found the static method to be called, just relax its accessibility. target.getDefinition().accessFlags.unsetPrivate(); if (target.getHolder().isInterface()) { @@ -609,11 +609,11 @@ } @Override - DexEncodedMethod ensureAccessibility(boolean allowMethodModification) { + ProgramMethod ensureAccessibility(boolean allowMethodModification) { // For all instantiation points for which the compiler creates lambda$ // methods, it creates these methods in the same class/interface. DexMethod implMethod = descriptor.implHandle.asMethod(); - DexClass implMethodHolder = definitionFor(implMethod.holder); + DexProgramClass implMethodHolder = definitionFor(implMethod.holder).asProgramClass(); DexEncodedMethod replacement = implMethodHolder @@ -649,7 +649,7 @@ assert replacement != null : "Unexpected failure to find direct lambda target for: " + implMethod.qualifiedName(); - return replacement; + return new ProgramMethod(implMethodHolder, replacement); } } @@ -661,7 +661,7 @@ } @Override - DexEncodedMethod ensureAccessibility(boolean allowMethodModification) { + ProgramMethod ensureAccessibility(boolean allowMethodModification) { // When compiling with whole program optimization, check that we are not inplace modifying. assert !(rewriter.getAppView().enableWholeProgramOptimizations() && allowMethodModification); // For all instantiation points for which the compiler creates lambda$ @@ -673,34 +673,37 @@ : createSyntheticAccessor(implMethod, implMethodHolder); } - private DexEncodedMethod modifyLambdaImplementationMethod( + private ProgramMethod modifyLambdaImplementationMethod( DexMethod implMethod, DexProgramClass implMethodHolder) { - return implMethodHolder - .getMethodCollection() - .replaceDirectMethodWithVirtualMethod( - implMethod, - encodedMethod -> { - assert encodedMethod.isDirectMethod(); - // We need to create a new method with the same code to be able to safely relax its - // accessibility and make it virtual. - MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy(); - newAccessFlags.unsetPrivate(); - newAccessFlags.setPublic(); - DexEncodedMethod newMethod = - new DexEncodedMethod( - callTarget, - newAccessFlags, - encodedMethod.annotations(), - encodedMethod.parameterAnnotationsList, - encodedMethod.getCode(), - true); - newMethod.copyMetadata(encodedMethod); - rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method); - return newMethod; - }); + DexEncodedMethod replacement = + implMethodHolder + .getMethodCollection() + .replaceDirectMethodWithVirtualMethod( + implMethod, + encodedMethod -> { + assert encodedMethod.isDirectMethod(); + // We need to create a new method with the same code to be able to safely relax + // its + // accessibility and make it virtual. + MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy(); + newAccessFlags.unsetPrivate(); + newAccessFlags.setPublic(); + DexEncodedMethod newMethod = + new DexEncodedMethod( + callTarget, + newAccessFlags, + encodedMethod.annotations(), + encodedMethod.parameterAnnotationsList, + encodedMethod.getCode(), + true); + newMethod.copyMetadata(encodedMethod); + rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method); + return newMethod; + }); + return new ProgramMethod(implMethodHolder, replacement); } - private DexEncodedMethod createSyntheticAccessor( + private ProgramMethod createSyntheticAccessor( DexMethod implMethod, DexProgramClass implMethodHolder) { MethodAccessFlags accessorFlags = MethodAccessFlags.fromSharedAccessFlags( @@ -726,7 +729,7 @@ true); implMethodHolder.addVirtualMethod(accessorEncodedMethod); - return accessorEncodedMethod; + return new ProgramMethod(implMethodHolder, accessorEncodedMethod); } } @@ -739,7 +742,7 @@ } @Override - DexEncodedMethod ensureAccessibility(boolean allowMethodModification) { + ProgramMethod ensureAccessibility(boolean allowMethodModification) { // Create a static accessor with proper accessibility. DexProgramClass accessorClass = programDefinitionFor(callTarget.holder); assert accessorClass != null; @@ -764,7 +767,7 @@ accessorClass.addDirectMethod(accessorEncodedMethod); } - return accessorEncodedMethod; + return new ProgramMethod(accessorClass, accessorEncodedMethod); } } }
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 4236e52..6b41818 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
@@ -15,6 +15,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; 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; @@ -29,6 +30,7 @@ import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableSet; @@ -115,11 +117,12 @@ private void synthesizeAccessibilityBridgesForLambdaClassesD8( Collection<LambdaClass> lambdaClasses, IRConverter converter, ExecutorService executorService) throws ExecutionException { - Set<DexEncodedMethod> nonDexAccessibilityBridges = Sets.newIdentityHashSet(); + ProgramMethodSet nonDexAccessibilityBridges = ProgramMethodSet.create(); for (LambdaClass lambdaClass : lambdaClasses) { // This call may cause originalMethodSignatures to be updated. - DexEncodedMethod accessibilityBridge = lambdaClass.target.ensureAccessibilityIfNeeded(true); - if (accessibilityBridge != null && !accessibilityBridge.getCode().isDexCode()) { + ProgramMethod accessibilityBridge = lambdaClass.target.ensureAccessibilityIfNeeded(true); + if (accessibilityBridge != null + && !accessibilityBridge.getDefinition().getCode().isDexCode()) { nonDexAccessibilityBridges.add(accessibilityBridge); } }
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 f4e1f4a..7da225c 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
@@ -4,13 +4,16 @@ package com.android.tools.r8.ir.desugar; + import com.android.tools.r8.dex.Constants; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ClassAccessFlags; +import com.android.tools.r8.graph.ClasspathMethod; 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.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; @@ -21,6 +24,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.NestMemberClassAttribute; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.origin.SynthesizedOrigin; @@ -55,9 +59,9 @@ protected final AppView<?> appView; // Following maps are there to avoid creating the bridges multiple times // and remember the bridges to add once the nests are processed. - final Map<DexMethod, DexEncodedMethod> bridges = new ConcurrentHashMap<>(); - final Map<DexField, DexEncodedMethod> getFieldBridges = new ConcurrentHashMap<>(); - final Map<DexField, DexEncodedMethod> putFieldBridges = new ConcurrentHashMap<>(); + final Map<DexMethod, ProgramMethod> bridges = new ConcurrentHashMap<>(); + final Map<DexField, ProgramMethod> getFieldBridges = new ConcurrentHashMap<>(); + final Map<DexField, ProgramMethod> putFieldBridges = new ConcurrentHashMap<>(); // Common single empty class for nest based private constructors private final DexProgramClass nestConstructor; private boolean nestConstructorUsed = false; @@ -81,9 +85,9 @@ } private DexEncodedMethod definitionFor( - DexMethod method, DexMethod context, Invoke.Type invokeType) { + DexMethod method, DexClassAndMethod context, Invoke.Type invokeType) { return appView.definitionFor( - appView.graphLense().lookupMethod(method, context, invokeType).getMethod()); + appView.graphLense().lookupMethod(method, context.getReference(), invokeType).getMethod()); } private DexEncodedField definitionFor(DexField field) { @@ -109,7 +113,7 @@ return new Pair<>(hostClass, classesInNest); } - Future<?> asyncProcessNest(DexClass clazz, ExecutorService executorService) { + Future<?> asyncProcessNest(DexProgramClass clazz, ExecutorService executorService) { return executorService.submit( () -> { Pair<DexClass, List<DexType>> nest = extractNest(clazz); @@ -133,11 +137,17 @@ } else { reportDesugarDependencies(host, clazz); if (shouldProcessClassInNest(clazz, nest)) { - NestBasedAccessDesugaringUseRegistry registry = - new NestBasedAccessDesugaringUseRegistry(clazz); - for (DexEncodedMethod method : clazz.methods()) { - registry.setContext(method.method); - method.registerCodeReferences(registry); + for (DexEncodedMethod definition : clazz.methods()) { + if (clazz.isProgramClass()) { + ProgramMethod method = new ProgramMethod(clazz.asProgramClass(), definition); + method.registerCodeReferences(new NestBasedAccessDesugaringUseRegistry(method)); + } else if (clazz.isClasspathClass()) { + ClasspathMethod method = new ClasspathMethod(clazz.asClasspathClass(), definition); + method.registerCodeReferencesForDesugaring( + new NestBasedAccessDesugaringUseRegistry(method)); + } else { + assert false; + } } } } @@ -255,26 +265,26 @@ .createMethod(holderType, proto, computeFieldBridgeName(field, isGet)); } - boolean invokeRequiresRewriting(DexEncodedMethod method, DexClass contextClass) { + boolean invokeRequiresRewriting(DexEncodedMethod method, DexClassAndMethod context) { assert method != null; // Rewrite only when targeting other nest members private fields. - if (!method.accessFlags.isPrivate() || method.holder() == contextClass.type) { + if (!method.accessFlags.isPrivate() || method.holder() == context.getHolderType()) { return false; } DexClass methodHolder = definitionFor(method.holder()); assert methodHolder != null; // from encodedMethod - return methodHolder.getNestHost() == contextClass.getNestHost(); + return methodHolder.getNestHost() == context.getHolder().getNestHost(); } - boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClass contextClass) { + boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClassAndMethod context) { assert field != null; // Rewrite only when targeting other nest members private fields. - if (!field.accessFlags.isPrivate() || field.holder() == contextClass.type) { + if (!field.accessFlags.isPrivate() || field.holder() == context.getHolderType()) { return false; } DexClass fieldHolder = definitionFor(field.holder()); assert fieldHolder != null; // from encodedField - return fieldHolder.getNestHost() == contextClass.getNestHost(); + return fieldHolder.getNestHost() == context.getHolder().getNestHost(); } private boolean holderRequiresBridge(DexClass holder) { @@ -300,14 +310,16 @@ if (holderRequiresBridge(holder)) { return bridgeMethod; } + assert holder.isProgramClass(); // The map is used to avoid creating multiple times the bridge // and remembers the bridges to add. - Map<DexField, DexEncodedMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges; + Map<DexField, ProgramMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges; + assert holder.isProgramClass(); fieldMap.computeIfAbsent( field.field, k -> DexEncodedMethod.createFieldAccessorBridge( - new DexFieldWithAccess(field, isGet), holder, bridgeMethod)); + new DexFieldWithAccess(field, isGet), holder.asProgramClass(), bridgeMethod)); return bridgeMethod; } @@ -327,26 +339,23 @@ } // The map is used to avoid creating multiple times the bridge // and remembers the bridges to add. + assert holder.isProgramClass(); bridges.computeIfAbsent( method.method, k -> method.isInstanceInitializer() - ? method.toInitializerForwardingBridge(holder, bridgeMethod) - : method.toStaticForwardingBridge(holder, computeMethodBridge(method))); + ? method.toInitializerForwardingBridge(holder.asProgramClass(), bridgeMethod) + : method.toStaticForwardingBridge( + holder.asProgramClass(), computeMethodBridge(method))); return bridgeMethod; } protected class NestBasedAccessDesugaringUseRegistry extends UseRegistry { - private final DexClass currentClass; - private DexMethod context; + private final DexClassAndMethod context; - NestBasedAccessDesugaringUseRegistry(DexClass currentClass) { + NestBasedAccessDesugaringUseRegistry(DexClassAndMethod context) { super(appView.options().itemFactory); - this.currentClass = currentClass; - } - - public void setContext(DexMethod context) { this.context = context; } @@ -357,7 +366,7 @@ return false; } DexEncodedMethod encodedMethod = definitionFor(method, context, invokeType); - if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, currentClass)) { + if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, context)) { ensureInvokeBridge(encodedMethod); return true; } @@ -366,7 +375,7 @@ private boolean registerFieldAccess(DexField field, boolean isGet) { DexEncodedField encodedField = definitionFor(field); - if (encodedField != null && fieldAccessRequiresRewriting(encodedField, currentClass)) { + if (encodedField != null && fieldAccessRequiresRewriting(encodedField, context)) { ensureFieldAccessBridge(encodedField, isGet); return true; }
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 ca10fe6..b97888d 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
@@ -7,10 +7,10 @@ import com.android.tools.r8.graph.AppView; 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.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.ThreadUtils; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -54,12 +54,11 @@ } 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().holder()); - assert holder != null && holder.isProgramClass(); - holder.asProgramClass().addMethod(entry.getValue()); - lensInserter.accept(entry.getKey(), entry.getValue().method); + Map<E, ProgramMethod> bridges, BiConsumer<E, DexMethod> lensInserter) { + for (Map.Entry<E, ProgramMethod> entry : bridges.entrySet()) { + ProgramMethod method = entry.getValue(); + method.getHolder().addMethod(method.getDefinition()); + lensInserter.accept(entry.getKey(), method.getReference()); } bridges.clear(); }
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 312ae5d..8d1124b 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
@@ -8,6 +8,7 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodHandle; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; @@ -31,6 +32,7 @@ import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo; import com.android.tools.r8.logging.Log; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.Sets; import java.util.Collection; import java.util.LinkedList; @@ -52,14 +54,14 @@ } private final AppView<AppInfoWithLiveness> appView; - private Set<DexEncodedMethod> revisitedMethods = null; + private ProgramMethodSet revisitedMethods = null; private Mode mode = Mode.COLLECT; public CallSiteOptimizationInfoPropagator(AppView<AppInfoWithLiveness> appView) { assert appView.enableWholeProgramOptimizations(); this.appView = appView; if (Log.isLoggingEnabledFor(CallSiteOptimizationInfoPropagator.class)) { - revisitedMethods = Sets.newIdentityHashSet(); + revisitedMethods = ProgramMethodSet.create(); } } @@ -67,9 +69,12 @@ assert Log.ENABLED; if (revisitedMethods != null) { Log.info(getClass(), "# of methods to revisit: %s", revisitedMethods.size()); - for (DexEncodedMethod m : revisitedMethods) { - Log.info(getClass(), "%s: %s", - m.toSourceString(), m.getCallSiteOptimizationInfo().toString()); + for (ProgramMethod m : revisitedMethods) { + Log.info( + getClass(), + "%s: %s", + m.toSourceString(), + m.getDefinition().getCallSiteOptimizationInfo().toString()); } } } @@ -245,7 +250,7 @@ if (abstractValue.isSingleValue()) { assert appView.options().enablePropagationOfConstantsAtCallSites; SingleValue singleValue = abstractValue.asSingleValue(); - if (singleValue.isMaterializableInContext(appView, code.method().holder())) { + if (singleValue.isMaterializableInContext(appView, code.context())) { Instruction replacement = singleValue.createMaterializingInstruction(appView, code, instr); replacement.setPosition(instr.getPosition()); @@ -318,27 +323,29 @@ } @Override - public Set<DexEncodedMethod> methodsToRevisit() { + public ProgramMethodSet methodsToRevisit() { mode = Mode.REVISIT; - Set<DexEncodedMethod> targetsToRevisit = Sets.newIdentityHashSet(); + ProgramMethodSet targetsToRevisit = ProgramMethodSet.create(); for (DexProgramClass clazz : appView.appInfo().classes()) { - for (DexEncodedMethod method : clazz.methods()) { - assert !method.isObsolete(); - if (method.shouldNotHaveCode() - || !method.hasCode() - || method.getCode().isEmptyVoidMethod()) { - continue; - } - // TODO(b/139246447): Assert no BOTTOM left. - CallSiteOptimizationInfo callSiteOptimizationInfo = method.getCallSiteOptimizationInfo(); - if (!callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, method)) { - continue; - } - targetsToRevisit.add(method); - if (appView.options().testing.callSiteOptimizationInfoInspector != null) { - appView.options().testing.callSiteOptimizationInfoInspector.accept(method); - } - } + clazz.forEachProgramMethodMatching( + definition -> { + assert !definition.isObsolete(); + if (definition.shouldNotHaveCode() + || !definition.hasCode() + || definition.getCode().isEmptyVoidMethod()) { + return false; + } + // TODO(b/139246447): Assert no BOTTOM left. + CallSiteOptimizationInfo callSiteOptimizationInfo = + definition.getCallSiteOptimizationInfo(); + return callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, definition); + }, + method -> { + targetsToRevisit.add(method); + if (appView.options().testing.callSiteOptimizationInfoInspector != null) { + appView.options().testing.callSiteOptimizationInfoInspector.accept(method); + } + }); } if (revisitedMethods != null) { revisitedMethods.addAll(targetsToRevisit);
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 db75756..e0066a7 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
@@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.optimize; -import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.addMonitorEnterValue; import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.collectAllMonitorEnterValues; @@ -16,6 +15,7 @@ import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; @@ -46,16 +46,13 @@ import java.util.List; import java.util.ListIterator; import java.util.Set; -import java.util.function.Predicate; public final class DefaultInliningOracle implements InliningOracle, InliningStrategy { private final AppView<AppInfoWithLiveness> appView; private final Inliner inliner; - private final DexEncodedMethod method; - private final IRCode code; + private final ProgramMethod method; private final MethodProcessor methodProcessor; - private final Predicate<DexEncodedMethod> isProcessedConcurrently; private final InliningReasonStrategy reasonStrategy; private final int inliningInstructionLimit; private int instructionAllowance; @@ -64,8 +61,7 @@ AppView<AppInfoWithLiveness> appView, Inliner inliner, InliningReasonStrategy inliningReasonStrategy, - DexEncodedMethod method, - IRCode code, + ProgramMethod method, MethodProcessor methodProcessor, int inliningInstructionLimit, int inliningInstructionAllowance) { @@ -73,9 +69,7 @@ this.inliner = inliner; this.reasonStrategy = inliningReasonStrategy; this.method = method; - this.code = code; this.methodProcessor = methodProcessor; - this.isProcessedConcurrently = methodProcessor::isProcessedConcurrently; this.inliningInstructionLimit = inliningInstructionLimit; this.instructionAllowance = inliningInstructionAllowance; } @@ -87,40 +81,29 @@ private boolean isSingleTargetInvalid( InvokeMethod invoke, - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { if (singleTarget == null) { throw new Unreachable( "Unexpected attempt to inline invoke that does not have a single target"); } - if (singleTarget.isClassInitializer()) { + if (singleTarget.getDefinition().isClassInitializer()) { throw new Unreachable( "Unexpected attempt to invoke a class initializer (`" - + singleTarget.method.toSourceString() + + singleTarget.toSourceString() + "`)"); } - if (!singleTarget.hasCode()) { + if (!singleTarget.getDefinition().hasCode()) { whyAreYouNotInliningReporter.reportInlineeDoesNotHaveCode(); return true; } - DexClass clazz = appView.definitionFor(singleTarget.holder()); - if (!clazz.isProgramClass()) { - if (clazz.isClasspathClass()) { - whyAreYouNotInliningReporter.reportClasspathMethod(); - } else { - assert clazz.isLibraryClass(); - whyAreYouNotInliningReporter.reportLibraryMethod(); - } - return true; - } - // Ignore the implicit receiver argument. int numberOfArguments = invoke.arguments().size() - BooleanUtils.intValue(invoke.isInvokeMethodWithReceiver()); - int arity = singleTarget.method.getArity(); + int arity = singleTarget.getReference().getArity(); if (numberOfArguments != arity) { whyAreYouNotInliningReporter.reportIncorrectArity(numberOfArguments, arity); return true; @@ -132,27 +115,28 @@ @Override public boolean passesInliningConstraints( InvokeMethod invoke, - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { - if (singleTarget.getOptimizationInfo().neverInline()) { + DexEncodedMethod singleTargetMethod = singleTarget.getDefinition(); + if (singleTargetMethod.getOptimizationInfo().neverInline()) { whyAreYouNotInliningReporter.reportMarkedAsNeverInline(); return false; } // We don't inline into constructors when producing class files since this can mess up // the stackmap, see b/136250031 - if (method.isInstanceInitializer() + if (method.getDefinition().isInstanceInitializer() && appView.options().isGeneratingClassFiles() && reason != Reason.FORCE) { whyAreYouNotInliningReporter.reportNoInliningIntoConstructorsWhenGeneratingClassFiles(); return false; } - if (method == singleTarget) { + if (method.getDefinition() == singleTargetMethod) { // Cannot handle recursive inlining at this point. // Force inlined method should never be recursive. - assert !singleTarget.getOptimizationInfo().forceInline(); + assert !singleTargetMethod.getOptimizationInfo().forceInline(); whyAreYouNotInliningReporter.reportRecursiveMethod(); return false; } @@ -162,7 +146,7 @@ // or optimized code. Right now this happens for the class class staticizer, as it just // processes all relevant methods in parallel with the full optimization pipeline enabled. // TODO(sgjesse): Add this assert "assert !isProcessedConcurrently.test(candidate);" - if (reason != Reason.FORCE && isProcessedConcurrently.test(singleTarget)) { + if (reason != Reason.FORCE && methodProcessor.isProcessedConcurrently(singleTarget)) { whyAreYouNotInliningReporter.reportProcessedConcurrently(); return false; } @@ -170,10 +154,9 @@ InternalOptions options = appView.options(); if (options.featureSplitConfiguration != null && !options.featureSplitConfiguration.inSameFeatureOrBase( - singleTarget.method, method.method)) { + singleTarget.getReference(), method.getReference())) { // Still allow inlining if we inline from the base into a feature. - DexClass clazz = asProgramClassOrNull(appView.definitionFor(singleTarget.method.holder)); - if (!options.featureSplitConfiguration.isInBase(clazz.asProgramClass())) { + if (!options.featureSplitConfiguration.isInBase(singleTarget.getHolder())) { whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit(); return false; } @@ -216,16 +199,16 @@ // 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.holder(), singleTarget)) { + && inlineeRefersToClassesNotInMainDex(method.getHolderType(), singleTarget)) { whyAreYouNotInliningReporter.reportInlineeRefersToClassesNotInMainDex(); return false; } assert reason != Reason.FORCE - || !inlineeRefersToClassesNotInMainDex(method.holder(), singleTarget); + || !inlineeRefersToClassesNotInMainDex(method.getHolderType(), singleTarget); return true; } - private boolean inlineeRefersToClassesNotInMainDex(DexType holder, DexEncodedMethod target) { + private boolean inlineeRefersToClassesNotInMainDex(DexType holder, ProgramMethod target) { if (inliner.mainDexClasses.isEmpty() || !inliner.mainDexClasses.getRoots().contains(holder)) { return false; } @@ -234,9 +217,9 @@ } private boolean satisfiesRequirementsForSimpleInlining( - InvokeMethod invoke, DexEncodedMethod target) { + InvokeMethod invoke, ProgramMethod target) { // If we are looking for a simple method, only inline if actually simple. - Code code = target.getCode(); + Code code = target.getDefinition().getCode(); int instructionLimit = computeInstructionLimit(invoke, target); if (code.estimatedSizeForInliningAtMost(instructionLimit)) { return true; @@ -244,9 +227,9 @@ return false; } - private int computeInstructionLimit(InvokeMethod invoke, DexEncodedMethod candidate) { + private int computeInstructionLimit(InvokeMethod invoke, ProgramMethod candidate) { int instructionLimit = inliningInstructionLimit; - BitSet hints = candidate.getOptimizationInfo().getNonNullParamOrThrow(); + BitSet hints = candidate.getDefinition().getOptimizationInfo().getNonNullParamOrThrow(); if (hints != null) { List<Value> arguments = invoke.inValues(); if (invoke.isInvokeMethodWithReceiver()) { @@ -266,15 +249,15 @@ } @Override - public DexEncodedMethod lookupSingleTarget(InvokeMethod invoke, DexType context) { - return invoke.lookupSingleTarget(appView, context); + public ProgramMethod lookupSingleTarget(InvokeMethod invoke, ProgramMethod context) { + return invoke.lookupSingleProgramTarget(appView, context); } @Override public InlineAction computeInlining( InvokeMethod invoke, - DexEncodedMethod singleTarget, - DexEncodedMethod context, + ProgramMethod singleTarget, + ProgramMethod context, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { if (isSingleTargetInvalid(invoke, singleTarget, whyAreYouNotInliningReporter)) { @@ -290,8 +273,9 @@ return null; } - if (!singleTarget.isInliningCandidate( - method, reason, appView.appInfo(), whyAreYouNotInliningReporter)) { + if (!singleTarget + .getDefinition() + .isInliningCandidate(method, reason, appView.appInfo(), whyAreYouNotInliningReporter)) { return null; } @@ -305,7 +289,7 @@ public InlineAction computeForInvokeWithReceiver( InvokeMethodWithReceiver invoke, - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { Value receiver = invoke.getReceiver(); @@ -322,7 +306,10 @@ // receiver. Therefore, if the receiver may be null and the candidate inlinee does not // throw if the receiver is null before any other side effect, then we must synthesize a // null check. - if (!singleTarget.getOptimizationInfo().checksNullReceiverBeforeAnySideEffect()) { + if (!singleTarget + .getDefinition() + .getOptimizationInfo() + .checksNullReceiverBeforeAnySideEffect()) { InternalOptions options = appView.options(); if (!options.enableInliningOfInvokesWithNullableReceivers) { whyAreYouNotInliningReporter.reportReceiverMaybeNull(); @@ -336,12 +323,16 @@ public InlineAction computeForInvokeStatic( InvokeStatic invoke, - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Reason reason, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { InlineAction action = new InlineAction(singleTarget, invoke, reason); - if (isTargetClassInitialized(invoke, method, singleTarget, classInitializationAnalysis)) { + if (isTargetClassInitialized( + invoke, + method.getDefinition(), + singleTarget.getDefinition(), + classInitializationAnalysis)) { return action; } if (appView.canUseInitClass() @@ -409,8 +400,8 @@ @Override public void ensureMethodProcessed( - DexEncodedMethod target, IRCode inlinee, OptimizationFeedback feedback) { - if (!target.isProcessed()) { + ProgramMethod target, IRCode inlinee, OptimizationFeedback feedback) { + if (!target.getDefinition().isProcessed()) { if (Log.ENABLED) { Log.verbose(getClass(), "Forcing extra inline on " + target.toSourceString()); } @@ -450,10 +441,11 @@ // 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.holder(); + DexType callerMethodHolder = method.getHolderType(); DexType calleeMethodHolder = inlinee.method().holder(); // Calling a constructor on the same class from a constructor can always be inlined. - if (method.isInstanceInitializer() && callerMethodHolder == calleeMethodHolder) { + if (method.getDefinition().isInstanceInitializer() + && callerMethodHolder == calleeMethodHolder) { return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java index 26c28d7..a48dece 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -5,8 +5,8 @@ package com.android.tools.r8.ir.optimize; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; @@ -22,12 +22,12 @@ final class ForcedInliningOracle implements InliningOracle, InliningStrategy { private final AppView<AppInfoWithLiveness> appView; - private final DexEncodedMethod method; + private final ProgramMethod method; private final Map<? extends InvokeMethod, Inliner.InliningInfo> invokesToInline; ForcedInliningOracle( AppView<AppInfoWithLiveness> appView, - DexEncodedMethod method, + ProgramMethod method, Map<? extends InvokeMethod, Inliner.InliningInfo> invokesToInline) { this.appView = appView; this.method = method; @@ -42,26 +42,26 @@ @Override public boolean passesInliningConstraints( InvokeMethod invoke, - DexEncodedMethod candidate, + ProgramMethod candidate, Reason reason, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { return true; } @Override - public DexEncodedMethod lookupSingleTarget(InvokeMethod invoke, DexType context) { + public ProgramMethod lookupSingleTarget(InvokeMethod invoke, ProgramMethod context) { Inliner.InliningInfo info = invokesToInline.get(invoke); if (info != null) { return info.target; } - return invoke.lookupSingleTarget(appView, context); + return invoke.lookupSingleProgramTarget(appView, context); } @Override public InlineAction computeInlining( InvokeMethod invoke, - DexEncodedMethod singleTarget, - DexEncodedMethod context, + ProgramMethod singleTarget, + ProgramMethod context, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { return computeForInvoke(invoke, whyAreYouNotInliningReporter); @@ -74,11 +74,11 @@ return null; } - assert method != info.target; + assert method.getDefinition() != info.target.getDefinition(); // Even though call to Inliner::performForcedInlining is supposed to be controlled by // the caller, it's still suspicious if we want to force inline something that is marked // with neverInline() flag. - assert !info.target.getOptimizationInfo().neverInline(); + assert !info.target.getDefinition().getOptimizationInfo().neverInline(); assert passesInliningConstraints( invoke, info.target, Reason.FORCE, whyAreYouNotInliningReporter); return new InlineAction(info.target, invoke, Reason.FORCE); @@ -86,7 +86,7 @@ @Override public void ensureMethodProcessed( - DexEncodedMethod target, IRCode inlinee, OptimizationFeedback feedback) { + ProgramMethod target, IRCode inlinee, OptimizationFeedback feedback) { // Do nothing. If the method is not yet processed, we still should // be able to build IR for inlining, though. }
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 fdb59ae..6db2341 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
@@ -19,6 +19,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.NestMemberClassAttribute; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy; import com.android.tools.r8.ir.analysis.type.Nullability; @@ -62,6 +63,7 @@ import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.SetUtils; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -69,7 +71,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Deque; -import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -86,9 +88,10 @@ // State for inlining methods which are known to be called twice. private boolean applyDoubleInlining = false; - private final Set<DexEncodedMethod> doubleInlineCallers = Sets.newIdentityHashSet(); - private final Set<DexEncodedMethod> doubleInlineSelectedTargets = Sets.newIdentityHashSet(); - private final Map<DexEncodedMethod, DexEncodedMethod> doubleInlineeCandidates = new HashMap<>(); + private final ProgramMethodSet doubleInlineCallers = ProgramMethodSet.create(); + private final ProgramMethodSet doubleInlineSelectedTargets = ProgramMethodSet.create(); + private final Map<DexEncodedMethod, ProgramMethod> doubleInlineeCandidates = + new IdentityHashMap<>(); private final AvailableApiExceptions availableApiExceptions; @@ -113,25 +116,25 @@ } boolean isBlacklisted( - DexEncodedMethod encodedMethod, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { - DexMethod method = encodedMethod.method; - if (encodedMethod.getOptimizationInfo().forceInline() - && appView.appInfo().neverInline.contains(method)) { + ProgramMethod method, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { + DexMethod reference = method.getReference(); + if (method.getDefinition().getOptimizationInfo().forceInline() + && appView.appInfo().neverInline.contains(reference)) { throw new Unreachable(); } - if (appView.appInfo().isPinned(method)) { + if (appView.appInfo().isPinned(reference)) { whyAreYouNotInliningReporter.reportPinned(); return true; } - if (blacklist.contains(appView.graphLense().getOriginalMethodSignature(method)) - || TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(method, appView)) { + if (blacklist.contains(appView.graphLense().getOriginalMethodSignature(reference)) + || TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(reference, appView)) { whyAreYouNotInliningReporter.reportBlacklisted(); return true; } - if (appView.appInfo().neverInline.contains(method)) { + if (appView.appInfo().neverInline.contains(reference)) { whyAreYouNotInliningReporter.reportMarkedAsNeverInline(); return true; } @@ -153,7 +156,7 @@ return result; } - public ConstraintWithTarget computeInliningConstraint(IRCode code, DexEncodedMethod method) { + public ConstraintWithTarget computeInliningConstraint(IRCode code, ProgramMethod method) { if (containsPotentialCatchHandlerVerificationError(code)) { return ConstraintWithTarget.NEVER; } @@ -168,7 +171,7 @@ new InliningConstraints(appView, GraphLense.getIdentityLense()); for (Instruction instruction : code.instructions()) { ConstraintWithTarget state = - instructionAllowedForInlining(instruction, inliningConstraints, method.holder()); + instructionAllowedForInlining(instruction, inliningConstraints, method.getHolderType()); if (state == ConstraintWithTarget.NEVER) { result = state; break; @@ -179,8 +182,8 @@ return result; } - private boolean returnsIntAsBoolean(IRCode code, DexEncodedMethod method) { - DexType returnType = method.method.proto.returnType; + private boolean returnsIntAsBoolean(IRCode code, ProgramMethod method) { + DexType returnType = method.getDefinition().returnType(); for (BasicBlock basicBlock : code.blocks) { InstructionIterator instructionIterator = basicBlock.iterator(); while (instructionIterator.hasNext()) { @@ -195,16 +198,17 @@ return false; } - boolean hasInliningAccess(DexEncodedMethod method, DexEncodedMethod target) { - if (!isVisibleWithFlags(target.holder(), method.holder(), target.accessFlags)) { + boolean hasInliningAccess(ProgramMethod method, ProgramMethod target) { + if (!isVisibleWithFlags( + target.getHolderType(), method.getHolderType(), target.getDefinition().accessFlags)) { return false; } // The class needs also to be visible for us to have access. - DexClass targetClass = appView.definitionFor(target.holder()); - return isVisibleWithFlags(target.holder(), method.holder(), targetClass.accessFlags); + return isVisibleWithFlags( + target.getHolderType(), method.getHolderType(), target.getHolder().accessFlags); } - private boolean isVisibleWithFlags(DexType target, DexType context, AccessFlags flags) { + private boolean isVisibleWithFlags(DexType target, DexType context, AccessFlags<?> flags) { if (flags.isPublic()) { return true; } @@ -218,12 +222,12 @@ return target.isSamePackage(context); } - public synchronized boolean isDoubleInlineSelectedTarget(DexEncodedMethod method) { + public synchronized boolean isDoubleInlineSelectedTarget(ProgramMethod method) { return doubleInlineSelectedTargets.contains(method); } synchronized boolean satisfiesRequirementsForDoubleInlining( - DexEncodedMethod method, DexEncodedMethod target) { + ProgramMethod method, ProgramMethod target) { if (applyDoubleInlining) { // Don't perform the actual inlining if this was not selected. return doubleInlineSelectedTargets.contains(target); @@ -234,25 +238,25 @@ return false; } - synchronized void recordDoubleInliningCandidate( - DexEncodedMethod method, DexEncodedMethod target) { + synchronized void recordDoubleInliningCandidate(ProgramMethod method, ProgramMethod target) { if (applyDoubleInlining) { return; } - if (doubleInlineeCandidates.containsKey(target)) { + if (doubleInlineeCandidates.containsKey(target.getDefinition())) { // Both calls can be inlined. - doubleInlineCallers.add(doubleInlineeCandidates.get(target)); + ProgramMethod doubleInlineeCandidate = doubleInlineeCandidates.get(target.getDefinition()); + doubleInlineCallers.add(doubleInlineeCandidate); doubleInlineCallers.add(method); doubleInlineSelectedTargets.add(target); } else { // First call can be inlined. - doubleInlineeCandidates.put(target, method); + doubleInlineeCandidates.put(target.getDefinition(), method); } } @Override - public Set<DexEncodedMethod> methodsToRevisit() { + public ProgramMethodSet methodsToRevisit() { applyDoubleInlining = true; return doubleInlineCallers; } @@ -574,14 +578,14 @@ public static class InlineAction { - public final DexEncodedMethod target; + public final ProgramMethod target; public final Invoke invoke; final Reason reason; private boolean shouldSynthesizeInitClass; private boolean shouldSynthesizeNullCheckForReceiver; - InlineAction(DexEncodedMethod target, Invoke invoke, Reason reason) { + InlineAction(ProgramMethod target, Invoke invoke, Reason reason) { this.target = target; this.invoke = invoke; this.reason = reason; @@ -600,7 +604,7 @@ InlineeWithReason buildInliningIR( AppView<? extends AppInfoWithClassHierarchy> appView, InvokeMethod invoke, - DexEncodedMethod context, + ProgramMethod context, InliningIRProvider inliningIRProvider, LambdaMerger lambdaMerger, LensCodeRewriter lensCodeRewriter) { @@ -624,9 +628,9 @@ // building, and therefore, we do not need to do anything here. Upon writing, we will use the // flag "declared synchronized" instead of "synchronized". boolean shouldSynthesizeMonitorEnterExit = - target.accessFlags.isSynchronized() && options.isGeneratingClassFiles(); + target.getDefinition().isSynchronized() && options.isGeneratingClassFiles(); boolean isSynthesizingNullCheckForReceiverUsingMonitorEnter = - shouldSynthesizeMonitorEnterExit && !target.isStatic(); + shouldSynthesizeMonitorEnterExit && !target.getDefinition().isStatic(); if (shouldSynthesizeNullCheckForReceiver && !isSynthesizingNullCheckForReceiverUsingMonitorEnter) { synthesizeNullCheckForReceiver(appView, code); @@ -709,11 +713,11 @@ // If this is a static method, then the class object will act as the lock, so we load it // using a const-class instruction. Value lockValue; - if (target.isStatic()) { + if (target.getDefinition().isStatic()) { lockValue = code.createValue( TypeElement.fromDexType(dexItemFactory.objectType, definitelyNotNull(), appView)); - monitorEnterBlockIterator.add(new ConstClass(lockValue, target.holder())); + monitorEnterBlockIterator.add(new ConstClass(lockValue, target.getHolderType())); } else { lockValue = entryBlock.getInstructions().getFirst().asArgument().outValue(); } @@ -739,7 +743,7 @@ } } - if (inliningIRProvider.shouldApplyCodeRewritings(code.method())) { + if (inliningIRProvider.shouldApplyCodeRewritings(target)) { assert lensCodeRewriter != null; lensCodeRewriter.rewrite(code, target); } @@ -762,7 +766,7 @@ InstructionListIterator iterator = initClassBlock.listIterator(code); iterator.setInsertionPosition(entryBlock.exit().getPosition()); - iterator.add(new InitClass(code.createValue(TypeElement.getInt()), target.holder())); + iterator.add(new InitClass(code.createValue(TypeElement.getInt()), target.getHolderType())); } private void synthesizeNullCheckForReceiver(AppView<?> appView, IRCode code) { @@ -842,17 +846,17 @@ } public static class InliningInfo { - public final DexEncodedMethod target; + public final ProgramMethod target; public final DexType receiverType; // null, if unknown - public InliningInfo(DexEncodedMethod target, DexType receiverType) { + public InliningInfo(ProgramMethod target, DexType receiverType) { this.target = target; this.receiverType = receiverType; } } public void performForcedInlining( - DexEncodedMethod method, + ProgramMethod method, IRCode code, Map<? extends InvokeMethod, InliningInfo> invokesToInline, InliningIRProvider inliningIRProvider) { @@ -860,8 +864,9 @@ performInliningImpl( oracle, oracle, method, code, OptimizationFeedbackIgnore.getInstance(), inliningIRProvider); } + public void performInlining( - DexEncodedMethod method, + ProgramMethod method, IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) { @@ -874,7 +879,7 @@ } public void performInlining( - DexEncodedMethod method, + ProgramMethod method, IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor, @@ -883,7 +888,6 @@ DefaultInliningOracle oracle = createDefaultOracle( method, - code, methodProcessor, options.inliningInstructionLimit, options.inliningInstructionAllowance - numberOfInstructions(code), @@ -904,14 +908,12 @@ } public DefaultInliningOracle createDefaultOracle( - DexEncodedMethod method, - IRCode code, + ProgramMethod method, MethodProcessor methodProcessor, int inliningInstructionLimit, int inliningInstructionAllowance) { return createDefaultOracle( method, - code, methodProcessor, inliningInstructionLimit, inliningInstructionAllowance, @@ -919,8 +921,7 @@ } public DefaultInliningOracle createDefaultOracle( - DexEncodedMethod method, - IRCode code, + ProgramMethod method, MethodProcessor methodProcessor, int inliningInstructionLimit, int inliningInstructionAllowance, @@ -930,7 +931,6 @@ this, inliningReasonStrategy, method, - code, methodProcessor, inliningInstructionLimit, inliningInstructionAllowance); @@ -939,7 +939,7 @@ private void performInliningImpl( InliningStrategy strategy, InliningOracle oracle, - DexEncodedMethod context, + ProgramMethod context, IRCode code, OptimizationFeedback feedback, InliningIRProvider inliningIRProvider) { @@ -964,16 +964,19 @@ if (current.isInvokeMethod()) { InvokeMethod invoke = current.asInvokeMethod(); // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()! - DexEncodedMethod singleTarget = oracle.lookupSingleTarget(invoke, context.holder()); + ProgramMethod singleTarget = oracle.lookupSingleTarget(invoke, context); if (singleTarget == null) { - WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget(invoke, appView, context); + WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget( + invoke, appView, context.getDefinition()); continue; } + DexEncodedMethod singleTargetMethod = singleTarget.getDefinition(); WhyAreYouNotInliningReporter whyAreYouNotInliningReporter = oracle.isForcedInliningOracle() ? NopWhyAreYouNotInliningReporter.getInstance() - : WhyAreYouNotInliningReporter.createFor(singleTarget, appView, context); + : WhyAreYouNotInliningReporter.createFor( + singleTargetMethod, appView, context.getDefinition()); InlineAction action = oracle.computeInlining( invoke, @@ -1012,8 +1015,8 @@ strategy.ensureMethodProcessed(singleTarget, inlinee.code, feedback); // Make sure constructor inlining is legal. - assert !singleTarget.isClassInitializer(); - if (singleTarget.isInstanceInitializer() + assert !singleTargetMethod.isClassInitializer(); + if (singleTargetMethod.isInstanceInitializer() && !strategy.canInlineInstanceInitializer( inlinee.code, whyAreYouNotInliningReporter)) { assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag(); @@ -1041,22 +1044,22 @@ getDowncastTypeIfNeeded(strategy, invoke, singleTarget)); if (inlinee.reason == Reason.SINGLE_CALLER) { - feedback.markInlinedIntoSingleCallSite(singleTarget); + feedback.markInlinedIntoSingleCallSite(singleTargetMethod); } classInitializationAnalysis.notifyCodeHasChanged(); postProcessInlineeBlocks(code, inlinee.code, blockIterator, block); // The synthetic and bridge flags are maintained only if the inlinee has also these flags. - if (context.accessFlags.isBridge() && !inlinee.code.method().accessFlags.isBridge()) { - context.accessFlags.demoteFromBridge(); + if (context.getDefinition().isBridge() && !inlinee.code.method().accessFlags.isBridge()) { + context.getDefinition().accessFlags.demoteFromBridge(); } - if (context.accessFlags.isSynthetic() + if (context.getDefinition().accessFlags.isSynthetic() && !inlinee.code.method().accessFlags.isSynthetic()) { - context.accessFlags.demoteFromSynthetic(); + context.getDefinition().accessFlags.demoteFromSynthetic(); } - context.copyMetadata(singleTarget); + context.getDefinition().copyMetadata(singleTargetMethod); if (inlineeMayHaveInvokeMethod && options.applyInliningToInlinee) { if (inlineeStack.size() + 1 > options.applyInliningToInlineeMaxDepth @@ -1103,7 +1106,7 @@ } private DexType getDowncastTypeIfNeeded( - InliningStrategy strategy, InvokeMethod invoke, DexEncodedMethod target) { + InliningStrategy strategy, InvokeMethod invoke, ProgramMethod target) { if (invoke.isInvokeMethodWithReceiver()) { // If the invoke has a receiver but the actual type of the receiver is different // from the computed target holder, inlining requires a downcast of the receiver. @@ -1113,8 +1116,8 @@ // method holder as a fallback. receiverType = invoke.getInvokedMethod().holder; } - if (!appView.appInfo().isSubtype(receiverType, target.holder())) { - return target.holder(); + if (!appView.appInfo().isSubtype(receiverType, target.getHolderType())) { + return target.getHolderType(); } } return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java index 6f4570f..a9e4736 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -4,8 +4,7 @@ package com.android.tools.r8.ir.optimize; -import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.optimize.Inliner.InlineAction; @@ -20,18 +19,18 @@ boolean isForcedInliningOracle(); // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget(appView, context)! - DexEncodedMethod lookupSingleTarget(InvokeMethod invoke, DexType context); + ProgramMethod lookupSingleTarget(InvokeMethod invoke, ProgramMethod context); boolean passesInliningConstraints( InvokeMethod invoke, - DexEncodedMethod candidate, + ProgramMethod candidate, Reason reason, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter); InlineAction computeInlining( InvokeMethod invoke, - DexEncodedMethod singleTarget, - DexEncodedMethod context, + ProgramMethod singleTarget, + ProgramMethod context, ClassInitializationAnalysis classInitializationAnalysis, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java index 0f22b14..cda0a6b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -4,8 +4,8 @@ package com.android.tools.r8.ir.optimize; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.InvokeMethod; @@ -43,8 +43,7 @@ /** Inform the strategy that the inlinee has been inlined. */ void markInlined(InlineeWithReason inlinee); - void ensureMethodProcessed( - DexEncodedMethod target, IRCode inlinee, OptimizationFeedback feedback); + void ensureMethodProcessed(ProgramMethod target, IRCode inlinee, OptimizationFeedback feedback); DexType getReceiverTypeIfKnown(InvokeMethod invoke); }
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 e6dcbbe..458f4b1 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
@@ -16,6 +16,7 @@ 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.graph.ProgramMethod; 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; @@ -225,7 +226,7 @@ private void rewriteInvokeMethodWithConstantValues( IRCode code, - DexType context, + ProgramMethod context, Set<Value> affectedValues, ListIterator<BasicBlock> blocks, InstructionListIterator iterator, @@ -235,7 +236,7 @@ if (!invokedHolder.isClassType()) { return; } - DexEncodedMethod target = current.lookupSingleTarget(appView, context); + DexEncodedMethod target = current.lookupSingleTarget(appView, context.getHolderType()); if (target != null && target.isInstanceInitializer()) { // Member value propagation does not apply to constructors. Removing a call to a constructor // that is marked as having no side effects could lead to verification errors, due to @@ -302,10 +303,10 @@ current.setOutValue(null); if (current.isInvokeMethodWithReceiver()) { - replaceInstructionByNullCheckIfPossible(current, iterator, context); + replaceInstructionByNullCheckIfPossible(current, iterator, context.getHolderType()); } else if (current.isInvokeStatic()) { replaceInstructionByInitClassIfPossible( - current, target.holder(), code, iterator, context); + current, target.holder(), code, iterator, context.getHolderType()); } // Insert the definition of the replacement. @@ -390,7 +391,7 @@ && singleValue.asSingleFieldValue().getField() == field) { return; } - if (singleValue.isMaterializableInContext(appView, code.method().holder())) { + if (singleValue.isMaterializableInContext(appView, code.context())) { BasicBlock block = current.getBlock(); DexType context = code.method().holder(); Position position = current.getPosition(); @@ -534,7 +535,7 @@ ListIterator<BasicBlock> blockIterator, Set<Value> affectedValues, Predicate<BasicBlock> blockTester) { - DexType context = code.method().holder(); + ProgramMethod context = code.context(); while (blockIterator.hasNext()) { BasicBlock block = blockIterator.next(); if (!blockTester.test(block)) {
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 6a05911..2b9456a 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
@@ -4,7 +4,6 @@ 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.definitelyNotNull; import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; @@ -12,10 +11,10 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ClassAccessFlags; +import com.android.tools.r8.graph.ClasspathMethod; import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DebugLocalInfo; import com.android.tools.r8.graph.DexAnnotationSet; -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.graph.DexItemFactory; @@ -29,6 +28,7 @@ import com.android.tools.r8.graph.GraphLense; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -65,9 +65,11 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOptions.OutlineOptions; +import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.StringUtils.BraceType; -import com.google.common.collect.Sets; +import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -77,7 +79,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import java.util.Set; import java.util.function.Consumer; /** @@ -94,24 +95,25 @@ * <li>Second, {@link Outliner#selectMethodsForOutlining()} is called to retain the lists of * methods found in the first step that are large enough (see {@link InternalOptions#outline} * {@link OutlineOptions#threshold}), and the methods to be further analyzed for outlining is - * returned by {@link Outliner#getMethodsSelectedForOutlining}. Each selected method is then + * returned by {@link Outliner#buildMethodsSelectedForOutlining}. Each selected method is then * converted back to IR and passed to {@link Outliner#identifyOutlineSites(IRCode)}, which * then stores concrete outlining candidates in {@link Outliner#outlineSites}. * <li>Third, {@link Outliner#buildOutlinerClass(DexType)} is called to construct the <em>outline * support class</em> containing a static helper method for each outline candidate that occurs * frequently enough. Each selected method is then converted to IR, passed to {@link - * Outliner#applyOutliningCandidate(IRCode)} to perform the outlining, and - * converted back to the output format (DEX or CF). + * Outliner#applyOutliningCandidate(IRCode)} to perform the outlining, and converted back to + * the output format (DEX or CF). * </ul> */ public class Outliner { /** Result of first step (see {@link Outliner#createOutlineMethodIdentifierGenerator()}. */ - private final List<List<DexEncodedMethod>> candidateMethodLists = new ArrayList<>(); + private final List<List<ProgramMethod>> candidateMethodLists = new ArrayList<>(); /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */ - private final Set<DexEncodedMethod> methodsSelectedForOutlining = Sets.newIdentityHashSet(); + private final LongLivedProgramMethodSetBuilder methodsSelectedForOutlining = + new LongLivedProgramMethodSetBuilder(); /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */ - private final Map<Outline, List<DexEncodedMethod>> outlineSites = new HashMap<>(); + private final Map<Outline, List<ProgramMethod>> outlineSites = new HashMap<>(); /** Result of third step (see {@link Outliner#buildOutlinerClass(DexType)}. */ private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>(); @@ -716,7 +718,7 @@ // replacing. abstract private class OutlineSpotter { - final DexEncodedMethod method; + final ProgramMethod method; final BasicBlock block; // instructionArrayCache is block.getInstructions() copied to an ArrayList. private List<Instruction> instructionArrayCache = null; @@ -733,7 +735,7 @@ int returnValueUsersLeft; int pendingNewInstanceIndex = -1; - OutlineSpotter(DexEncodedMethod method, BasicBlock block) { + OutlineSpotter(ProgramMethod method, BasicBlock block) { this.method = method; this.block = block; reset(0); @@ -862,7 +864,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.holder()); + invoke.inliningConstraint(inliningConstraints, method.getHolderType()); if (constraint != ConstraintWithTarget.ALWAYS) { return false; } @@ -1133,12 +1135,10 @@ // TODO(sgjesse): This does not take several usages in the same method into account. private class OutlineMethodIdentifier extends OutlineSpotter { - private final Map<Outline, List<DexEncodedMethod>> candidateMap; + private final Map<Outline, List<ProgramMethod>> candidateMap; OutlineMethodIdentifier( - DexEncodedMethod method, - BasicBlock block, - Map<Outline, List<DexEncodedMethod>> candidateMap) { + ProgramMethod method, BasicBlock block, Map<Outline, List<ProgramMethod>> candidateMap) { super(method, block); this.candidateMap = candidateMap; } @@ -1150,8 +1150,8 @@ } } - private List<DexEncodedMethod> addOutlineMethodList(Outline outline) { - List<DexEncodedMethod> result = new ArrayList<>(); + private List<ProgramMethod> addOutlineMethodList(Outline outline) { + List<ProgramMethod> result = new ArrayList<>(); candidateMethodLists.add(result); return result; } @@ -1159,7 +1159,7 @@ private class OutlineSiteIdentifier extends OutlineSpotter { - OutlineSiteIdentifier(DexEncodedMethod method, BasicBlock block) { + OutlineSiteIdentifier(ProgramMethod method, BasicBlock block) { super(method, block); } @@ -1184,7 +1184,7 @@ ListIterator<BasicBlock> blocksIterator, BasicBlock block, List<Integer> toRemove) { - super(code.method(), block); + super(code.context(), block); this.code = code; this.blocksIterator = blocksIterator; this.toRemove = toRemove; @@ -1259,7 +1259,9 @@ /** When assertions are enabled, remove method from the outline's list. */ private boolean removeMethodFromOutlineList(Outline outline) { synchronized (outlineSites) { - assert outlineSites.get(outline).remove(method); + assert ListUtils.removeFirstMatch( + outlineSites.get(outline), + element -> element.getDefinition() == method.getDefinition()); } return true; } @@ -1275,14 +1277,14 @@ // out-value of invokes to null), this map must not be used except for identifying methods // potentially relevant to outlining. OutlineMethodIdentifier will add method lists to // candidateMethodLists whenever it adds an entry to candidateMap. - Map<Outline, List<DexEncodedMethod>> candidateMap = new HashMap<>(); + Map<Outline, List<ProgramMethod>> candidateMap = new HashMap<>(); assert candidateMethodLists.isEmpty(); assert outlineMethodIdentifierGenerator == null; outlineMethodIdentifierGenerator = code -> { assert !code.method().getCode().isOutlineCode(); for (BasicBlock block : code.blocks) { - new OutlineMethodIdentifier(code.method(), block, candidateMap).process(); + new OutlineMethodIdentifier(code.context(), block, candidateMap).process(); } }; } @@ -1296,38 +1298,30 @@ public void identifyOutlineSites(IRCode code) { assert !code.method().getCode().isOutlineCode(); - DexClass clazz = asProgramClassOrNull(appView.definitionFor(code.method().holder())); - assert clazz != null; - if (clazz == null) { - return; - } + DexProgramClass clazz = code.context().getHolder(); if (appView.options().featureSplitConfiguration != null - && appView.options().featureSplitConfiguration.isInFeature(clazz.asProgramClass())) { + && appView.options().featureSplitConfiguration.isInFeature(clazz)) { return; } - for (BasicBlock block : code.blocks) { - new OutlineSiteIdentifier(code.method(), block).process(); + new OutlineSiteIdentifier(code.context(), block).process(); } } public boolean selectMethodsForOutlining() { - assert methodsSelectedForOutlining.size() == 0; - assert outlineSites.size() == 0; - for (List<DexEncodedMethod> outlineMethods : candidateMethodLists) { + assert methodsSelectedForOutlining.isEmpty(); + assert outlineSites.isEmpty(); + for (List<ProgramMethod> outlineMethods : candidateMethodLists) { if (outlineMethods.size() >= appView.options().outline.threshold) { - for (DexEncodedMethod outlineMethod : outlineMethods) { - methodsSelectedForOutlining.add( - appView.graphLense().mapDexEncodedMethod(outlineMethod, appView)); - } + methodsSelectedForOutlining.addAll(outlineMethods); } } candidateMethodLists.clear(); - return methodsSelectedForOutlining.size() > 0; + return !methodsSelectedForOutlining.isEmpty(); } - public Set<DexEncodedMethod> getMethodsSelectedForOutlining() { - return methodsSelectedForOutlining; + public ProgramMethodSet buildMethodsSelectedForOutlining() { + return methodsSelectedForOutlining.build(appView); } public DexProgramClass buildOutlinerClass(DexType type) { @@ -1345,7 +1339,7 @@ DexString methodName = appView.dexItemFactory().createString(OutlineOptions.METHOD_PREFIX + count); DexMethod method = outline.buildMethod(type, methodName); - List<DexEncodedMethod> sites = outlineSites.get(outline); + List<ProgramMethod> sites = outlineSites.get(outline); assert !sites.isEmpty(); direct[count] = new DexEncodedMethod( @@ -1356,7 +1350,7 @@ new OutlineCode(outline), true); if (appView.options().isGeneratingClassFiles()) { - direct[count].upgradeClassFileVersion(sites.get(0).getClassFileVersion()); + direct[count].upgradeClassFileVersion(sites.get(0).getDefinition().getClassFileVersion()); } generatedOutlines.put(outline, method); count++; @@ -1393,10 +1387,10 @@ } private List<Outline> selectOutlines() { - assert outlineSites.size() > 0; + assert !outlineSites.isEmpty(); assert candidateMethodLists.isEmpty(); List<Outline> result = new ArrayList<>(); - for (Entry<Outline, List<DexEncodedMethod>> entry : outlineSites.entrySet()) { + for (Entry<Outline, List<ProgramMethod>> entry : outlineSites.entrySet()) { if (entry.getValue().size() >= appView.options().outline.threshold) { result.add(entry.getKey()); } @@ -1603,10 +1597,9 @@ } @Override - public IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin) { - OutlineSourceCode source = new OutlineSourceCode(outline, encodedMethod.method); - return IRBuilder.create(encodedMethod, appView, source, origin) - .build(encodedMethod); + public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { + OutlineSourceCode source = new OutlineSourceCode(outline, method.getReference()); + return IRBuilder.create(method, appView, source, origin).build(method); } @Override @@ -1615,7 +1608,12 @@ } @Override - public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { + throw new Unreachable(); + } + + @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { throw new Unreachable(); }
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 ccb328e..1ba460e 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
@@ -12,6 +12,7 @@ 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.ProgramMethod; 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; @@ -54,7 +55,7 @@ private static final int MAX_CAPACITY_PER_BLOCK = 50; private final AppView<?> appView; - private final DexEncodedMethod method; + private final ProgramMethod method; private final IRCode code; // Values that may require type propagation. @@ -69,7 +70,7 @@ public RedundantFieldLoadElimination(AppView<?> appView, IRCode code) { this.appView = appView; - this.method = code.method(); + this.method = code.context(); this.code = code; } @@ -105,7 +106,7 @@ private final SingleValue value; private MaterializableValue(SingleValue value) { - assert value.isMaterializableInContext(appView.withLiveness(), method.holder()); + assert value.isMaterializableInContext(appView.withLiveness(), method); this.value = value; } @@ -153,14 +154,14 @@ if (appView.enableWholeProgramOptimizations()) { return appView.appInfo().resolveField(field); } - if (field.holder == method.holder()) { + if (field.holder == method.getHolderType()) { return appView.definitionFor(field); } return null; } public void run() { - DexType context = method.holder(); + DexType context = method.getHolderType(); Reference2IntMap<BasicBlock> pendingNormalSuccessors = new Reference2IntOpenHashMap<>(); for (BasicBlock block : code.blocks) { if (!block.hasUniqueNormalSuccessor()) { @@ -213,7 +214,8 @@ FieldAndObject fieldAndObject = new FieldAndObject(field, object); ExistingValue value = new ExistingValue(instancePut.value()); if (isFinal(definition)) { - assert method.isInstanceInitializer() || verifyWasInstanceInitializer(); + assert method.getDefinition().isInstanceInitializer() + || verifyWasInstanceInitializer(); activeState.putFinalInstanceField(fieldAndObject, value); } else { activeState.putNonFinalInstanceField(fieldAndObject, value); @@ -244,7 +246,7 @@ killNonFinalActiveFields(staticPut); ExistingValue value = new ExistingValue(staticPut.value()); if (definition.isFinal()) { - assert method.isClassInitializer(); + assert method.getDefinition().isClassInitializer(); activeState.putFinalStaticField(field, value); } else { activeState.putNonFinalStaticField(field, value); @@ -332,11 +334,11 @@ private boolean verifyWasInstanceInitializer() { VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses(); assert verticallyMergedClasses != null; - assert verticallyMergedClasses.isTarget(method.holder()); + assert verticallyMergedClasses.isTarget(method.getHolderType()); assert appView .dexItemFactory() - .isConstructor(appView.graphLense().getOriginalMethodSignature(method.method)); - assert method.getOptimizationInfo().forceInline(); + .isConstructor(appView.graphLense().getOriginalMethodSignature(method.getReference())); + assert method.getDefinition().getOptimizationInfo().forceInline(); return true; } @@ -346,7 +348,7 @@ return; } - DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType()); if (singleTarget == null || !singleTarget.isInstanceInitializer()) { killAllNonFinalActiveFields(); return; @@ -374,7 +376,7 @@ activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value)); } else if (info.isSingleValue()) { SingleValue value = info.asSingleValue(); - if (value.isMaterializableInContext(appView.withLiveness(), method.holder())) { + if (value.isMaterializableInContext(appView.withLiveness(), method)) { Value object = invoke.getReceiver().getAliasedValue(); FieldAndObject fieldAndObject = new FieldAndObject(field.field, object); activeState.putNonFinalInstanceField(fieldAndObject, new MaterializableValue(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 c60da19..1f410ef 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
@@ -56,7 +56,7 @@ } DexType type = null; if (current.isInvokeVirtual()) { - type = getTypeForGetClass( appView, context, current.asInvokeVirtual()); + type = getTypeForGetClass(appView, context, current.asInvokeVirtual()); } else if (current.isInvokeStatic()) { type = getTypeForClassForName( appView, classInitializationAnalysis, context, current.asInvokeStatic());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java index d5d51e1..7bec65e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -94,7 +94,7 @@ List<DexEncodedField> switchMapFields = clazz.staticFields().stream() .filter(this::maybeIsSwitchMap).collect(Collectors.toList()); if (!switchMapFields.isEmpty()) { - IRCode initializer = clazz.getClassInitializer().buildIR(appView, clazz.origin); + IRCode initializer = clazz.getProgramClassInitializer().buildIR(appView); switchMapFields.forEach(field -> extractSwitchMap(field, initializer)); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java index 798b820..e7104f3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeAnalysis; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -166,7 +167,7 @@ CodeRewriter codeRewriter, StringOptimizer stringOptimizer, EnumValueOptimizer enumValueOptimizer, - DexEncodedMethod method, + ProgramMethod method, IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor, @@ -297,7 +298,8 @@ codeRewriter.simplifyControlFlow(code); // If a method was inlined we may see more trivial computation/conversion of String. boolean isDebugMode = - appView.options().debug || method.getOptimizationInfo().isReachabilitySensitive(); + appView.options().debug + || method.getDefinition().getOptimizationInfo().isReachabilitySensitive(); if (!isDebugMode) { // Reflection/string optimization 3. trivial conversion/computation on const-string stringOptimizer.computeTrivialOperationsOnConstString(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java index da76b7b..3feac87 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
@@ -10,8 +10,8 @@ import static com.android.tools.r8.ir.code.Opcodes.RETURN; 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.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeMethod; @@ -44,19 +44,19 @@ boolean willExceedInstructionBudget( IRCode code, DexProgramClass eligibleClass, - Map<InvokeMethod, DexEncodedMethod> directInlinees, - List<DexEncodedMethod> indirectInlinees) { + Map<InvokeMethod, ProgramMethod> directInlinees, + List<ProgramMethod> indirectInlinees) { if (appView.appInfo().alwaysClassInline.contains(eligibleClass.type)) { return false; } - for (DexEncodedMethod inlinee : indirectInlinees) { + for (ProgramMethod inlinee : indirectInlinees) { // We do not have the corresponding invoke instruction for the inlinees that are not called // directly from `code` (these are called indirectly from one of the methods in // `directInlinees`). Therefore, we currently choose not to build IR for estimating the number // of non-materializing instructions, since we cannot cache the IR (it would have the wrong // position). - int increment = inlinee.getCode().estimatedSizeForInlining(); + int increment = inlinee.getDefinition().getCode().estimatedSizeForInlining(); if (exceedsInstructionBudgetAfterIncrement(increment)) { return true; } @@ -67,14 +67,14 @@ int numberOfSeenDirectInlinees = 0; int numberOfDirectInlinees = directInlinees.size(); for (InvokeMethod invoke : code.<InvokeMethod>instructions(Instruction::isInvokeMethod)) { - DexEncodedMethod inlinee = directInlinees.get(invoke); + ProgramMethod inlinee = directInlinees.get(invoke); if (inlinee == null) { // Not a direct inlinee. continue; } IRCode inliningIR = inliningIRProvider.getAndCacheInliningIR(invoke, inlinee); int increment = - inlinee.getCode().estimatedSizeForInlining() + inlinee.getDefinition().getCode().estimatedSizeForInlining() - estimateNumberOfNonMaterializingInstructions(invoke, inliningIR); assert increment >= 0; if (exceedsInstructionBudgetAfterIncrement(increment)) {
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 9a76a99..59e9a94 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
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize.classinliner; +import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull; import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.google.common.base.Predicates.alwaysFalse; @@ -16,7 +17,7 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -57,6 +58,7 @@ import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -89,7 +91,7 @@ private final Inliner inliner; private final Function<DexClass, EligibilityStatus> isClassEligible; private final MethodProcessor methodProcessor; - private final DexEncodedMethod method; + private final ProgramMethod method; private final Instruction root; private Value eligibleInstance; @@ -98,14 +100,14 @@ private final Map<InvokeMethodWithReceiver, InliningInfo> methodCallsOnInstance = new IdentityHashMap<>(); - private final Set<DexEncodedMethod> indirectMethodCallsOnInstance = Sets.newIdentityHashSet(); + private final ProgramMethodSet indirectMethodCallsOnInstance = ProgramMethodSet.create(); private final Map<InvokeMethod, InliningInfo> extraMethodCalls = new IdentityHashMap<>(); private final List<Pair<InvokeMethod, Integer>> unusedArguments = new ArrayList<>(); - private final Map<InvokeMethod, DexEncodedMethod> directInlinees = new IdentityHashMap<>(); - private final List<DexEncodedMethod> indirectInlinees = new ArrayList<>(); + private final Map<InvokeMethod, ProgramMethod> directInlinees = new IdentityHashMap<>(); + private final List<ProgramMethod> indirectInlinees = new ArrayList<>(); // Sets of values that must/may be an alias of the "root" instance (including the root instance // itself). @@ -116,7 +118,7 @@ Inliner inliner, Function<DexClass, EligibilityStatus> isClassEligible, MethodProcessor methodProcessor, - DexEncodedMethod method, + ProgramMethod method, Instruction root) { this.appView = appView; this.dexItemFactory = appView.dexItemFactory(); @@ -132,11 +134,11 @@ return eligibleClass; } - Map<InvokeMethod, DexEncodedMethod> getDirectInlinees() { + Map<InvokeMethod, ProgramMethod> getDirectInlinees() { return directInlinees; } - List<DexEncodedMethod> getIndirectInlinees() { + List<ProgramMethod> getIndirectInlinees() { return indirectInlinees; } @@ -160,7 +162,7 @@ if (eligibleClass.classInitializationMayHaveSideEffects( appView, // Types that are a super type of the current context are guaranteed to be initialized. - type -> appView.isSubtype(method.holder(), type).isTrue(), + type -> appView.isSubtype(method.getHolderType(), type).isTrue(), Sets.newIdentityHashSet())) { return EligibilityStatus.HAS_CLINIT; } @@ -170,7 +172,7 @@ assert root.isStaticGet(); StaticGet staticGet = root.asStaticGet(); - if (staticGet.instructionMayHaveSideEffects(appView, method.holder())) { + if (staticGet.instructionMayHaveSideEffects(appView, method.getHolderType())) { return EligibilityStatus.RETRIEVAL_MAY_HAVE_SIDE_EFFECTS; } DexEncodedField field = appView.appInfo().resolveField(staticGet.getField()); @@ -265,15 +267,17 @@ if (user.isInvokeMethod()) { InvokeMethod invokeMethod = user.asInvokeMethod(); - DexEncodedMethod singleTarget = invokeMethod.lookupSingleTarget(appView, method.holder()); - if (singleTarget == null) { + DexEncodedMethod singleTargetMethod = + invokeMethod.lookupSingleTarget(appView, method.getHolderType()); + if (singleTargetMethod == null) { return user; // Not eligible. } - if (isEligibleLibraryMethodCall(invokeMethod, singleTarget)) { + if (isEligibleLibraryMethodCall(invokeMethod, singleTargetMethod)) { continue; } + ProgramMethod singleTarget = singleTargetMethod.asProgramMethod(appView); if (!isEligibleSingleTarget(singleTarget)) { return user; // Not eligible. } @@ -463,8 +467,11 @@ throw new IllegalClassInlinerStateException(); } + ProgramMethod singleTargetMethod = + new ProgramMethod( + appView.definitionForHolder(singleTarget).asProgramClass(), singleTarget); methodCallsOnInstance.put( - invoke, new InliningInfo(singleTarget, root.asNewInstance().clazz)); + invoke, new InliningInfo(singleTargetMethod, root.asNewInstance().clazz)); break; } } @@ -506,8 +513,7 @@ continue; } - DexEncodedMethod singleTarget = - invoke.lookupSingleTarget(appView, code.method().holder()); + ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, method); if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) { throw new IllegalClassInlinerStateException(); } @@ -568,7 +574,7 @@ continue; } - DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType()); if (singleTarget != null) { Predicate<InvokeMethod> noSideEffectsPredicate = dexItemFactory.libraryMethodsWithoutSideEffects.getOrDefault( @@ -618,7 +624,7 @@ throw new Unreachable( "Unexpected usage left in method `" - + method.method.toSourceString() + + method.toSourceString() + "` after inlining: " + user); } @@ -650,7 +656,7 @@ throw new Unreachable( "Unexpected usage left in method `" - + method.method.toSourceString() + + method.toSourceString() + "` after inlining: " + user); } @@ -689,7 +695,7 @@ if (!user.isInstancePut()) { throw new Unreachable( "Unexpected usage left in method `" - + method.method.toSourceString() + + method.toSourceString() + "` after field reads removed: " + user); } @@ -699,7 +705,7 @@ if (field == null) { throw new Unreachable( "Unexpected field write left in method `" - + method.method.toSourceString() + + method.toSourceString() + "` after field reads removed: " + user); } @@ -707,8 +713,7 @@ } } - private InliningInfo isEligibleConstructorCall( - InvokeDirect invoke, DexEncodedMethod singleTarget) { + private InliningInfo isEligibleConstructorCall(InvokeDirect invoke, ProgramMethod singleTarget) { assert dexItemFactory.isConstructor(invoke.getInvokedMethod()); assert isEligibleSingleTarget(singleTarget); @@ -735,7 +740,7 @@ // Check that the `eligibleInstance` does not escape via the constructor. InstanceInitializerInfo instanceInitializerInfo = - singleTarget.getOptimizationInfo().getInstanceInitializerInfo(); + singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(); if (instanceInitializerInfo.receiverMayEscapeOutsideConstructorChain()) { return null; } @@ -746,21 +751,22 @@ if (parent == null) { return null; } - DexEncodedMethod encodedParent = appView.definitionFor(parent); + ProgramMethod encodedParent = asProgramMethodOrNull(appView.definitionFor(parent), appView); if (encodedParent == null) { return null; } if (methodProcessor.isProcessedConcurrently(encodedParent)) { return null; } - if (!encodedParent.isInliningCandidate( + DexEncodedMethod encodedParentMethod = encodedParent.getDefinition(); + if (!encodedParentMethod.isInliningCandidate( method, Reason.SIMPLE, appView.appInfo(), NopWhyAreYouNotInliningReporter.getInstance())) { return null; } - parent = encodedParent.getOptimizationInfo().getInstanceInitializerInfo().getParent(); + parent = encodedParentMethod.getOptimizationInfo().getInstanceInitializerInfo().getParent(); } return new InliningInfo(singleTarget, eligibleClass.type); @@ -848,7 +854,7 @@ private InliningInfo isEligibleDirectVirtualMethodCall( InvokeMethodWithReceiver invoke, - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Set<Instruction> indirectUsers, Supplier<InliningOracle> defaultOracle) { assert isEligibleSingleTarget(singleTarget); @@ -862,7 +868,7 @@ } // TODO(b/141719453): Should not constrain library overrides if all instantiations are inlined. - if (singleTarget.isLibraryMethodOverride().isTrue()) { + if (singleTarget.getDefinition().isLibraryMethodOverride().isTrue()) { InliningOracle inliningOracle = defaultOracle.get(); if (!inliningOracle.passesInliningConstraints( invoke, singleTarget, Reason.SIMPLE, NopWhyAreYouNotInliningReporter.getInstance())) { @@ -880,18 +886,20 @@ } ClassInlinerEligibilityInfo eligibility = - singleTarget.getOptimizationInfo().getClassInlinerEligibility(); + singleTarget.getDefinition().getOptimizationInfo().getClassInlinerEligibility(); if (eligibility.callsReceiver.size() > 1) { return null; } if (!eligibility.callsReceiver.isEmpty()) { assert eligibility.callsReceiver.get(0).getFirst() == Invoke.Type.VIRTUAL; DexMethod indirectlyInvokedMethod = eligibility.callsReceiver.get(0).getSecond(); - DexEncodedMethod indirectSingleTarget = - appView.appInfo().resolveMethod(eligibleClass, indirectlyInvokedMethod).getSingleTarget(); - if (indirectSingleTarget == null) { + ResolutionResult resolutionResult = + appView.appInfo().resolveMethod(eligibleClass, indirectlyInvokedMethod); + if (!resolutionResult.isSingleResolution()) { return null; } + ProgramMethod indirectSingleTarget = + resolutionResult.asSingleResolution().getResolutionPair().asProgramMethod(); if (!isEligibleIndirectVirtualMethodCall(indirectlyInvokedMethod, indirectSingleTarget)) { return null; } @@ -902,17 +910,19 @@ } private boolean isEligibleIndirectVirtualMethodCall(DexMethod invokedMethod) { - DexEncodedMethod singleTarget = - appView.appInfo().resolveMethod(eligibleClass, invokedMethod).getSingleTarget(); + ProgramMethod singleTarget = + asProgramMethodOrNull( + appView.appInfo().resolveMethod(eligibleClass, invokedMethod).getSingleTarget(), + appView); return isEligibleIndirectVirtualMethodCall(invokedMethod, singleTarget); } private boolean isEligibleIndirectVirtualMethodCall( - DexMethod invokedMethod, DexEncodedMethod singleTarget) { + DexMethod invokedMethod, ProgramMethod singleTarget) { if (!isEligibleSingleTarget(singleTarget)) { return false; } - if (singleTarget.isLibraryMethodOverride().isTrue()) { + if (singleTarget.getDefinition().isLibraryMethodOverride().isTrue()) { return false; } return isEligibleVirtualMethodCall( @@ -926,7 +936,7 @@ private boolean isEligibleVirtualMethodCall( InvokeMethodWithReceiver invoke, DexMethod callee, - DexEncodedMethod singleTarget, + ProgramMethod singleTarget, Predicate<ClassInlinerEligibilityInfo> eligibilityAcceptanceCheck) { assert isEligibleSingleTarget(singleTarget); @@ -939,14 +949,14 @@ return false; } - if (!singleTarget.isNonPrivateVirtualMethod()) { + if (!singleTarget.getDefinition().isNonPrivateVirtualMethod()) { return false; } - if (method == singleTarget) { + if (method.getDefinition() == singleTarget.getDefinition()) { return false; // Don't inline itself. } - MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo(); + MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo(); ClassInlinerEligibilityInfo eligibility = optimizationInfo.getClassInlinerEligibility(); if (eligibility == null) { return false; @@ -1013,7 +1023,7 @@ // -- method itself can be inlined // private boolean isExtraMethodCallEligible( - InvokeMethod invoke, DexEncodedMethod singleTarget, Supplier<InliningOracle> defaultOracle) { + InvokeMethod invoke, ProgramMethod singleTarget, Supplier<InliningOracle> defaultOracle) { // Don't consider constructor invocations and super calls, since we don't want to forcibly // inline them. assert isExtraMethodCall(invoke); @@ -1035,10 +1045,11 @@ } } - MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo(); + MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo(); // Go through all arguments, see if all usages of eligibleInstance are good. - if (!isEligibleParameterUsages(invoke, arguments, singleTarget, defaultOracle)) { + if (!isEligibleParameterUsages( + invoke, arguments, singleTarget.getDefinition(), defaultOracle)) { return false; } @@ -1155,16 +1166,23 @@ } // Check if the method is inline-able by standard inliner. - DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder()); + DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.getHolderType()); if (singleTarget == null) { return false; } + DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(singleTarget)); + if (holder == null) { + return false; + } + + ProgramMethod singleTargetMethod = new ProgramMethod(holder, singleTarget); + InliningOracle oracle = defaultOracle.get(); InlineAction inlineAction = oracle.computeInlining( invoke, - singleTarget, + singleTargetMethod, method, ClassInitializationAnalysis.trivial(), NopWhyAreYouNotInliningReporter.getInstance()); @@ -1188,15 +1206,12 @@ return initializerInfo.receiverNeverEscapesOutsideConstructorChain(); } - private boolean exemptFromInstructionLimit(DexEncodedMethod inlinee) { - DexType inlineeHolder = inlinee.holder(); - DexClass inlineeClass = appView.definitionFor(inlineeHolder); - assert inlineeClass != null; - KotlinClassLevelInfo kotlinInfo = inlineeClass.getKotlinInfo(); + private boolean exemptFromInstructionLimit(ProgramMethod inlinee) { + KotlinClassLevelInfo kotlinInfo = inlinee.getHolder().getKotlinInfo(); return kotlinInfo.isSyntheticClass() && kotlinInfo.asSyntheticClass().isLambda(); } - private void markSizeForInlining(InvokeMethod invoke, DexEncodedMethod inlinee) { + private void markSizeForInlining(InvokeMethod invoke, ProgramMethod inlinee) { assert !methodProcessor.isProcessedConcurrently(inlinee); if (!exemptFromInstructionLimit(inlinee)) { if (invoke != null) { @@ -1207,18 +1222,20 @@ } } - private boolean isEligibleSingleTarget(DexEncodedMethod singleTarget) { + private boolean isEligibleSingleTarget(ProgramMethod singleTarget) { if (singleTarget == null) { return false; } - if (!singleTarget.isProgramMethod(appView)) { - return false; - } if (methodProcessor.isProcessedConcurrently(singleTarget)) { return false; } - if (!singleTarget.isInliningCandidate( - method, Reason.SIMPLE, appView.appInfo(), NopWhyAreYouNotInliningReporter.getInstance())) { + if (!singleTarget + .getDefinition() + .isInliningCandidate( + method, + Reason.SIMPLE, + appView.appInfo(), + NopWhyAreYouNotInliningReporter.getInstance())) { // If `singleTarget` is not an inlining candidate, we won't be able to inline it here. // // Note that there may be some false negatives here since the method may
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 cf9c904..06c2894 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
@@ -53,6 +53,7 @@ import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.StringDiagnostic; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -73,7 +74,7 @@ private final DexItemFactory factory; // Map the enum candidates with their dependencies, i.e., the methods to reprocess for the given // enum if the optimization eventually decides to unbox it. - private final Map<DexType, Set<DexEncodedMethod>> enumsUnboxingCandidates; + private final Map<DexType, ProgramMethodSet> enumsUnboxingCandidates; private EnumUnboxingRewriter enumUnboxerRewriter; @@ -184,11 +185,11 @@ } if (!eligibleEnums.isEmpty()) { for (DexType eligibleEnum : eligibleEnums) { - Set<DexEncodedMethod> dependencies = enumsUnboxingCandidates.get(eligibleEnum); + ProgramMethodSet dependencies = enumsUnboxingCandidates.get(eligibleEnum); // If dependencies is null, it means the enum is not eligible (It has been marked as // unboxable by this thread or another one), so we do not need to record dependencies. if (dependencies != null) { - dependencies.add(code.method()); + dependencies.add(code.context()); } } } @@ -313,7 +314,6 @@ appView .appInfo() .rewrittenWithLens(appView.appInfo().app().asDirect(), enumUnboxingLens)); - classStaticizer.filterCandidates(); // Update optimization info. feedback.fixupOptimizationInfos( appView, @@ -609,9 +609,9 @@ } @Override - public Set<DexEncodedMethod> methodsToRevisit() { - Set<DexEncodedMethod> toReprocess = Sets.newIdentityHashSet(); - for (Set<DexEncodedMethod> methods : enumsUnboxingCandidates.values()) { + public ProgramMethodSet methodsToRevisit() { + ProgramMethodSet toReprocess = ProgramMethodSet.create(); + for (ProgramMethodSet methods : enumsUnboxingCandidates.values()) { toReprocess.addAll(methods); } return toReprocess;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java index 5412037..d138d95 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -18,9 +18,8 @@ import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.ir.optimize.enums.EnumUnboxer.Reason; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.google.common.collect.Sets; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; class EnumUnboxingCandidateAnalysis { @@ -28,7 +27,7 @@ private final AppView<AppInfoWithLiveness> appView; private final EnumUnboxer enumUnboxer; private final DexItemFactory factory; - private Map<DexType, Set<DexEncodedMethod>> enumToUnboxCandidates = new ConcurrentHashMap<>(); + private Map<DexType, ProgramMethodSet> enumToUnboxCandidates = new ConcurrentHashMap<>(); EnumUnboxingCandidateAnalysis(AppView<AppInfoWithLiveness> appView, EnumUnboxer enumUnboxer) { this.appView = appView; @@ -36,10 +35,10 @@ factory = appView.dexItemFactory(); } - Map<DexType, Set<DexEncodedMethod>> findCandidates() { + Map<DexType, ProgramMethodSet> findCandidates() { for (DexProgramClass clazz : appView.appInfo().classes()) { if (isEnumUnboxingCandidate(clazz)) { - enumToUnboxCandidates.put(clazz.type, Sets.newConcurrentHashSet()); + enumToUnboxCandidates.put(clazz.type, ProgramMethodSet.createConcurrent()); } } removeEnumsInAnnotations();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java index 953514d..c94be2e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java
@@ -4,13 +4,13 @@ package com.android.tools.r8.ir.optimize.enums; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.EnumValueInfoMapCollection; import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo; import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeDirect; @@ -54,8 +54,8 @@ if (!clazz.accessFlags.isEnum() || clazz.isNotProgramClass() || !clazz.hasClassInitializer()) { return; } - DexEncodedMethod initializer = clazz.getClassInitializer(); - IRCode code = initializer.getCode().buildIR(initializer, appView, clazz.origin); + ProgramMethod initializer = clazz.getProgramClassInitializer(); + IRCode code = initializer.buildIR(appView); LinkedHashMap<DexField, EnumValueInfo> enumValueInfoMap = new LinkedHashMap<>(); for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) { if (staticPut.getField().type != clazz.type) {
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 14a3800..23b74b1 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
@@ -289,8 +289,7 @@ return; } - boolean synchronizedVirtualMethod = - method.accessFlags.isSynchronized() && method.isVirtualMethod(); + boolean synchronizedVirtualMethod = method.isSynchronized() && method.isVirtualMethod(); feedback.setClassInlinerEligibility( method, @@ -1042,7 +1041,7 @@ return; } boolean mayHaveSideEffects; - if (method.accessFlags.isSynchronized()) { + if (method.isSynchronized()) { // If the method is synchronized then it acquires a lock. mayHaveSideEffects = true; } else if (method.isInstanceInitializer() && hasNonTrivialFinalizeMethod(context)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java index 5c45a37..ae543a2 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -6,6 +6,8 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.conversion.CallSiteInformation; import com.android.tools.r8.ir.optimize.Inliner; @@ -29,25 +31,27 @@ @Override public Reason computeInliningReason( - InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { - if (target.getOptimizationInfo().forceInline() + InvokeMethod invoke, ProgramMethod target, ProgramMethod context) { + DexEncodedMethod targetMethod = target.getDefinition(); + DexMethod targetReference = target.getReference(); + if (targetMethod.getOptimizationInfo().forceInline() || (appView.appInfo().hasLiveness() - && appView.withLiveness().appInfo().forceInline.contains(target.method))) { - assert !appView.appInfo().neverInline.contains(target.method); + && appView.withLiveness().appInfo().forceInline.contains(targetReference))) { + assert !appView.appInfo().neverInline.contains(targetReference); return Reason.FORCE; } if (appView.appInfo().hasLiveness() - && appView.withLiveness().appInfo().alwaysInline.contains(target.method)) { + && appView.withLiveness().appInfo().alwaysInline.contains(targetReference)) { return Reason.ALWAYS; } if (appView.options().disableInliningOfLibraryMethodOverrides - && target.isLibraryMethodOverride().isTrue()) { + && targetMethod.isLibraryMethodOverride().isTrue()) { // This method will always have an implicit call site from the library, so we won't be able to // remove it after inlining even if we have single or dual call site information from the // program. return Reason.SIMPLE; } - if (callSiteInformation.hasSingleCallSite(target.method)) { + if (callSiteInformation.hasSingleCallSite(target)) { return Reason.SINGLE_CALLER; } if (isDoubleInliningTarget(target)) { @@ -56,11 +60,11 @@ return Reason.SIMPLE; } - private boolean isDoubleInliningTarget(DexEncodedMethod candidate) { + private boolean isDoubleInliningTarget(ProgramMethod candidate) { // 10 is found from measuring. - if (callSiteInformation.hasDoubleCallSite(candidate.method) + if (callSiteInformation.hasDoubleCallSite(candidate) || inliner.isDoubleInlineSelectedTarget(candidate)) { - return candidate.getCode().estimatedSizeForInliningAtMost(10); + return candidate.getDefinition().getCode().estimatedSizeForInliningAtMost(10); } return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java index 2b4c072..ae23a74 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.ir.optimize.inliner; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.optimize.Inliner.Reason; @@ -18,7 +18,7 @@ @Override public Reason computeInliningReason( - InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) { + InvokeMethod invoke, ProgramMethod target, ProgramMethod context) { return reason; } }
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 608c41f..85cb117 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
@@ -5,7 +5,7 @@ package com.android.tools.r8.ir.optimize.inliner; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.Position; @@ -18,32 +18,32 @@ public class InliningIRProvider { private final AppView<?> appView; - private final DexEncodedMethod context; + private final ProgramMethod context; private final ValueNumberGenerator valueNumberGenerator; private final MethodProcessor methodProcessor; private final Map<InvokeMethod, IRCode> cache = new IdentityHashMap<>(); public InliningIRProvider( - AppView<?> appView, DexEncodedMethod context, IRCode code, MethodProcessor methodProcessor) { + AppView<?> appView, ProgramMethod context, IRCode code, MethodProcessor methodProcessor) { this.appView = appView; this.context = context; this.valueNumberGenerator = code.valueNumberGenerator; this.methodProcessor = methodProcessor; } - public IRCode getInliningIR(InvokeMethod invoke, DexEncodedMethod method) { + public IRCode getInliningIR(InvokeMethod invoke, ProgramMethod method) { IRCode cached = cache.remove(invoke); if (cached != null) { return cached; } Position position = Position.getPositionForInlining(appView, invoke, context); - Origin origin = appView.appInfo().originFor(method.holder()); + Origin origin = method.getOrigin(); return method.buildInliningIR( context, appView, valueNumberGenerator, position, origin, methodProcessor); } - public IRCode getAndCacheInliningIR(InvokeMethod invoke, DexEncodedMethod method) { + public IRCode getAndCacheInliningIR(InvokeMethod invoke, ProgramMethod method) { IRCode inliningIR = getInliningIR(invoke, method); cacheInliningIR(invoke, inliningIR); return inliningIR; @@ -59,7 +59,7 @@ return true; } - public boolean shouldApplyCodeRewritings(DexEncodedMethod method) { + public boolean shouldApplyCodeRewritings(ProgramMethod method) { return methodProcessor.shouldApplyCodeRewritings(method); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java index 6e8e1aa..f2ae52a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java
@@ -4,12 +4,11 @@ package com.android.tools.r8.ir.optimize.inliner; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.optimize.Inliner.Reason; public interface InliningReasonStrategy { - Reason computeInliningReason( - InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context); + Reason computeInliningReason(InvokeMethod invoke, ProgramMethod target, ProgramMethod context); }
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 eb8b742..92635f2 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
@@ -6,11 +6,11 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.CheckCast; import com.android.tools.r8.ir.code.ConstClass; @@ -158,19 +158,19 @@ private final LambdaTypeVisitor lambdaChecker; // Specify the context of the current instruction: method/code/blocks/instructions. - public final DexEncodedMethod method; + public final ProgramMethod method; public final IRCode code; public final ListIterator<BasicBlock> blocks; private InstructionListIterator instructions; // The inlining context (caller), if any. - private final DexEncodedMethod context; + private final ProgramMethod context; CodeProcessor( AppView<AppInfoWithLiveness> appView, Function<DexType, Strategy> strategyProvider, LambdaTypeVisitor lambdaChecker, - DexEncodedMethod method, + ProgramMethod method, IRCode code) { this(appView, strategyProvider, lambdaChecker, method, code, null); } @@ -179,9 +179,9 @@ AppView<AppInfoWithLiveness> appView, Function<DexType, Strategy> strategyProvider, LambdaTypeVisitor lambdaChecker, - DexEncodedMethod method, + ProgramMethod method, IRCode code, - DexEncodedMethod context) { + ProgramMethod context) { this.appView = appView; this.strategyProvider = strategyProvider; this.factory = appView.dexItemFactory(); @@ -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).holder(); + return type != (context != null ? context : method).getHolderType(); } @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 63d2c1d..2a5e9cd 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
@@ -10,12 +10,14 @@ import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexApplication.Builder; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses; import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater; @@ -50,6 +52,8 @@ import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.ThrowingConsumer; +import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Sets; @@ -67,7 +71,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.function.Function; -import java.util.stream.Collectors; // Merging lambda classes into single lambda group classes. There are three flavors // of lambdas we are dealing with: @@ -97,19 +100,19 @@ private abstract static class Mode { void rewriteCode( - DexEncodedMethod method, + ProgramMethod method, IRCode code, Inliner inliner, - DexEncodedMethod context, + ProgramMethod context, InliningIRProvider provider) {} - void analyzeCode(DexEncodedMethod method, IRCode code) {} + void analyzeCode(ProgramMethod method, IRCode code) {} } private class AnalyzeMode extends Mode { @Override - void analyzeCode(DexEncodedMethod method, IRCode code) { + void analyzeCode(ProgramMethod method, IRCode code) { new AnalysisStrategy(method, code).processCode(); } } @@ -128,27 +131,24 @@ @Override void rewriteCode( - DexEncodedMethod method, + ProgramMethod method, IRCode code, Inliner inliner, - DexEncodedMethod context, + ProgramMethod context, InliningIRProvider provider) { - DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass(); - assert clazz != null; - - LambdaGroup lambdaGroup = lambdaGroups.get(clazz); + LambdaGroup lambdaGroup = lambdaGroups.get(method.getHolder()); if (lambdaGroup == null) { // Only rewrite the methods that have not been synthesized for the lambda group classes. new ApplyStrategy(method, code, context, optimizationInfoFixer).processCode(); return; } - if (method.isInitializer()) { + if (method.getDefinition().isInitializer()) { // Should not require rewriting. return; } - assert method.isNonPrivateVirtualMethod(); + assert method.getDefinition().isNonPrivateVirtualMethod(); assert context == null; Map<InvokeVirtual, InliningInfo> invokesToInline = new IdentityHashMap<>(); @@ -160,9 +160,10 @@ ResolutionResult resolution = appView.appInfo().resolveMethod(holder, invokedMethod, false); assert resolution.isSingleResolution(); - DexEncodedMethod singleTarget = resolution.getSingleTarget(); + ProgramMethod singleTarget = + resolution.asSingleResolution().getResolutionPair().asProgramMethod(); assert singleTarget != null; - invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.holder())); + invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.getHolderType())); } } @@ -194,7 +195,8 @@ // we mark a method for further processing, and then invalidate the only lambda referenced // from it. In this case we will reprocess method that does not need patching, but it // should not be happening very frequently and we ignore possible overhead. - private final Set<DexEncodedMethod> methodsToReprocess = Sets.newIdentityHashSet(); + private final LongLivedProgramMethodSetBuilder methodsToReprocess = + new LongLivedProgramMethodSetBuilder(); private final AppView<AppInfoWithLiveness> appView; private final Kotlin kotlin; @@ -236,7 +238,7 @@ return lambdas.get(lambda); } - private synchronized void queueForProcessing(DexEncodedMethod method) { + private synchronized void queueForProcessing(ProgramMethod method) { methodsToReprocess.add(method); } @@ -291,7 +293,7 @@ * <li>in APPLY mode does nothing. * </ol> */ - public final void analyzeCode(DexEncodedMethod method, IRCode code) { + public final void analyzeCode(ProgramMethod method, IRCode code) { if (mode != null) { mode.analyzeCode(method, code); } @@ -308,10 +310,10 @@ * </ol> */ public final void rewriteCode( - DexEncodedMethod method, IRCode code, Inliner inliner, MethodProcessor methodProcessor) { + ProgramMethod method, IRCode code, Inliner inliner, MethodProcessor methodProcessor) { if (mode != null) { mode.rewriteCode( - method, + code.context(), code, inliner, null, @@ -320,12 +322,12 @@ } /** - * Similar to {@link #rewriteCode(DexEncodedMethod, IRCode, Inliner, MethodProcessor)}, but for + * Similar to {@link #rewriteCode(ProgramMethod, IRCode, Inliner, MethodProcessor)}, but for * rewriting code for inlining. The {@param context} is the caller that {@param method} is being * inlined into. */ public final void rewriteCodeForInlining( - DexEncodedMethod method, IRCode code, DexEncodedMethod context, InliningIRProvider provider) { + ProgramMethod method, IRCode code, ProgramMethod context, InliningIRProvider provider) { if (mode != null) { mode.rewriteCode(method, code, null, context, provider); } @@ -453,12 +455,11 @@ if (methodsToReprocess.isEmpty()) { return; } - Set<DexEncodedMethod> methods = - methodsToReprocess.stream() - .map(method -> appView.graphLense().mapDexEncodedMethod(method, appView)) - .collect(Collectors.toSet()); + ProgramMethodSet methods = methodsToReprocess.build(appView); converter.processMethodsConcurrently(methods, executorService); - assert methods.stream().allMatch(DexEncodedMethod::isProcessed); + assert methods.stream() + .map(DexClassAndMethod::getDefinition) + .allMatch(DexEncodedMethod::isProcessed); } private void analyzeClass(DexProgramClass clazz) { @@ -491,7 +492,7 @@ } private final class AnalysisStrategy extends CodeProcessor { - private AnalysisStrategy(DexEncodedMethod method, IRCode code) { + private AnalysisStrategy(ProgramMethod method, IRCode code) { super( LambdaMerger.this.appView, LambdaMerger.this::strategyProvider, @@ -543,9 +544,9 @@ private final Set<Value> typeAffectedValues = Sets.newIdentityHashSet(); private ApplyStrategy( - DexEncodedMethod method, + ProgramMethod method, IRCode code, - DexEncodedMethod context, + ProgramMethod context, LambdaMergerOptimizationInfoFixer optimizationInfoFixer) { super( LambdaMerger.this.appView,
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 4d79186..112b37c 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
@@ -53,8 +53,8 @@ // static class initializer. return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName && lambda == field.type - && context.factory.isClassConstructor(context.method.method) - && context.method.holder() == lambda; + && context.method.getDefinition().isClassInitializer() + && context.method.getHolderType() == lambda; } @Override @@ -69,10 +69,10 @@ @Override public boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field) { DexType lambda = field.holder; - DexMethod method = context.method.method; + DexMethod method = context.method.getReference(); assert group.containsLambda(lambda); // Support writes to capture instance fields inside lambda constructor only. - return method.holder == lambda && context.factory.isConstructor(method); + return method.holder == lambda && context.method.getDefinition().isInstanceInitializer(); } @Override @@ -100,7 +100,7 @@ // 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.holder() == lambda)) + return (isSingletonLambda == (context.method.getHolderType() == lambda)) && invoke.isInvokeDirect() && context.factory.isConstructor(method) && CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
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 3311e1d..041cf09 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
@@ -4,6 +4,8 @@ package com.android.tools.r8.ir.optimize.staticizer; +import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; @@ -15,6 +17,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.IRCode; @@ -31,11 +34,13 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.SetUtils; +import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -64,7 +69,6 @@ final AtomicInteger fieldWrites = new AtomicInteger(); // Number of instances created. final AtomicInteger instancesCreated = new AtomicInteger(); - final Set<DexEncodedMethod> referencedFrom = Sets.newConcurrentHashSet(); final AtomicReference<DexEncodedMethod> constructor = new AtomicReference<>(); final AtomicReference<DexEncodedMethod> getter = new AtomicReference<>(); @@ -86,8 +90,8 @@ return singletonField.holder(); } - DexClass hostClass() { - DexClass hostClass = appView.definitionFor(hostType()); + DexProgramClass hostClass() { + DexProgramClass hostClass = asProgramClassOrNull(appView.definitionFor(hostType())); assert hostClass != null; return hostClass; } @@ -96,17 +100,11 @@ candidates.remove(candidate.type); return null; } - - void filterMethods() { - Set<DexEncodedMethod> newReferencedFrom = Sets.newIdentityHashSet(); - for (DexEncodedMethod dexEncodedMethod : referencedFrom) { - newReferencedFrom.add(appView.graphLense().mapDexEncodedMethod(dexEncodedMethod, appView)); - } - referencedFrom.clear(); - referencedFrom.addAll(newReferencedFrom); - } } + final Map<CandidateInfo, LongLivedProgramMethodSetBuilder> referencedFrom = + new IdentityHashMap<>(); + // The map storing all the potential candidates for staticizing. final ConcurrentHashMap<DexType, CandidateInfo> candidates = new ConcurrentHashMap<>(); @@ -218,10 +216,11 @@ // or field defined in the class. // // NOTE: can be called concurrently. - public final void examineMethodCode(DexEncodedMethod method, IRCode code) { + public final void examineMethodCode(IRCode code) { + ProgramMethod method = code.context(); Set<Instruction> alreadyProcessed = Sets.newIdentityHashSet(); - CandidateInfo receiverClassCandidateInfo = candidates.get(method.holder()); + CandidateInfo receiverClassCandidateInfo = candidates.get(method.getHolderType()); Value receiverValue = code.getThis(); // NOTE: is null for static methods. if (receiverClassCandidateInfo != null) { if (receiverValue != null) { @@ -229,26 +228,28 @@ // which we will check later), check if all the references to 'this' are valid // (the call will invalidate the candidate if some of them are not valid). analyzeAllValueUsers( - receiverClassCandidateInfo, receiverValue, factory.isConstructor(method.method)); + receiverClassCandidateInfo, + receiverValue, + factory.isConstructor(method.getReference())); // If the candidate is still valid, ignore all instructions // we treat as valid usages on receiver. - if (candidates.get(method.holder()) != null) { + if (candidates.get(method.getHolderType()) != 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.holder()) { + if (method.getDefinition().returnType() == method.getHolderType()) { List<Instruction> examined = isValidGetter(receiverClassCandidateInfo, code); if (examined != null) { DexEncodedMethod getter = receiverClassCandidateInfo.getter.get(); if (getter == null) { - receiverClassCandidateInfo.getter.set(method); + receiverClassCandidateInfo.getter.set(method.getDefinition()); // Except for static-get and return, iterate other remaining instructions if any. alreadyProcessed.addAll(examined); } else { - assert getter != method; + assert getter != method.getDefinition(); // Not sure how to deal with many getters. receiverClassCandidateInfo.invalidate(); } @@ -274,7 +275,8 @@ if (instruction.isNewInstance()) { // Check the class being initialized against valid staticizing candidates. NewInstance newInstance = instruction.asNewInstance(); - CandidateInfo candidateInfo = processInstantiation(method, iterator, newInstance); + CandidateInfo candidateInfo = + processInstantiation(method.getDefinition(), iterator, newInstance); if (candidateInfo != null) { alreadyProcessed.addAll(newInstance.outValue().aliasedUsers()); // For host class initializers having eligible instantiation we also want to @@ -282,14 +284,16 @@ // 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.holder(), iterator.next(), code)) { + if (!isAllowedInHostClassInitializer(method.getHolderType(), iterator.next(), code)) { candidateInfo.preserveRead.set(true); iterator.previous(); break; } // Ignore just read instruction. } - candidateInfo.referencedFrom.add(method); + referencedFrom + .computeIfAbsent(candidateInfo, ignore -> new LongLivedProgramMethodSetBuilder()) + .add(method); } continue; } @@ -309,7 +313,9 @@ // Check the field being read: make sure all usages are valid. CandidateInfo info = processStaticFieldRead(instruction.asStaticGet()); if (info != null) { - info.referencedFrom.add(method); + referencedFrom + .computeIfAbsent(info, ignore -> new LongLivedProgramMethodSetBuilder()) + .add(method); // If the candidate is still valid, ignore all usages in further analysis. Value value = instruction.outValue(); if (value != null) { @@ -323,7 +329,9 @@ // Check if it is a static singleton getter. CandidateInfo info = processInvokeStatic(instruction.asInvokeStatic()); if (info != null) { - info.referencedFrom.add(method); + referencedFrom + .computeIfAbsent(info, ignore -> new LongLivedProgramMethodSetBuilder()) + .add(method); // If the candidate is still valid, ignore all usages in further analysis. Value value = instruction.outValue(); if (value != null) { @@ -659,17 +667,6 @@ return false; } - // Methods may have their signature changed in-between the IR processing rounds, leading to - // duplicates where one version is the outdated version. Remove these. - // This also ensures no unboxed enum are staticized, if that would be the case, then - // the candidate would need to be removed from the candidate list. - public void filterCandidates() { - for (Map.Entry<DexType, CandidateInfo> entry : candidates.entrySet()) { - assert !appView.unboxedEnums().containsEnum(entry.getKey()); - entry.getValue().filterMethods(); - } - } - // Perform staticizing candidates: // // 1. After filtering candidates based on usage, finalize the list of candidates by
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 845a01c..1e7fb3c 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,7 @@ package com.android.tools.r8.ir.optimize.staticizer; +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; @@ -17,6 +18,7 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -36,10 +38,11 @@ import com.android.tools.r8.ir.optimize.CodeRewriter; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer.CandidateInfo; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.TraversalContinuation; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; @@ -66,13 +69,15 @@ private final ClassStaticizer classStaticizer; private final IRConverter converter; + private final ProgramMethodSet methodsToReprocess = ProgramMethodSet.create(); + // Optimization order matters, hence a collection that preserves orderings. private final Map<DexEncodedMethod, ImmutableList.Builder<BiConsumer<IRCode, MethodProcessor>>> processingQueue = new IdentityHashMap<>(); - private final Set<DexEncodedMethod> referencingExtraMethods = Sets.newIdentityHashSet(); + private final ProgramMethodSet referencingExtraMethods = ProgramMethodSet.create(); private final Map<DexEncodedMethod, CandidateInfo> hostClassInits = new IdentityHashMap<>(); - private final Set<DexEncodedMethod> methodsToBeStaticized = Sets.newIdentityHashSet(); + private final ProgramMethodSet methodsToBeStaticized = ProgramMethodSet.create(); private final Map<DexField, CandidateInfo> singletonFields = new IdentityHashMap<>(); private final Map<DexMethod, CandidateInfo> singletonGetters = new IdentityHashMap<>(); private final Map<DexType, DexType> candidateToHostMapping = new IdentityHashMap<>(); @@ -89,14 +94,21 @@ final void run(OptimizationFeedback feedback, ExecutorService executorService) throws ExecutionException { // Filter out candidates based on the information we collected while examining methods. - finalEligibilityCheck(); + Map<CandidateInfo, ProgramMethodSet> materializedReferencedFromCollections = + finalEligibilityCheck(); // Prepare interim data. - prepareCandidates(); + prepareCandidates(materializedReferencedFromCollections); // Enqueue all host class initializers (only remove instantiations). + ProgramMethodSet hostClassInitMethods = ProgramMethodSet.create(); + hostClassInits + .values() + .forEach( + candidateInfo -> + hostClassInitMethods.add(candidateInfo.hostClass().getProgramClassInitializer())); enqueueMethodsWithCodeOptimizations( - hostClassInits.keySet(), + hostClassInitMethods, optimizations -> optimizations .add(this::removeCandidateInstantiation) @@ -114,13 +126,13 @@ // TODO(b/140767158): Merge the remaining part below. // Convert instance methods into static methods with an extra parameter. - Set<DexEncodedMethod> methods = staticizeMethodSymbols(); + ProgramMethodSet methods = staticizeMethodSymbols(); // Process all other methods that may reference singleton fields and call methods on them. // (Note that we exclude the former instance methods, but include new static methods created as // a result of staticizing.) methods.addAll(referencingExtraMethods); - methods.addAll(hostClassInits.keySet()); + methods.addAll(hostClassInitMethods); enqueueMethodsWithCodeOptimizations( methods, optimizations -> @@ -131,13 +143,17 @@ // Process queued methods with associated optimizations processMethodsConcurrently(feedback, executorService); + + // Clear all candidate information now that all candidates have been staticized. + classStaticizer.candidates.clear(); } - private void finalEligibilityCheck() { + private Map<CandidateInfo, ProgramMethodSet> finalEligibilityCheck() { + Map<CandidateInfo, ProgramMethodSet> materializedReferencedFromCollections = + new IdentityHashMap<>(); Set<Phi> visited = Sets.newIdentityHashSet(); Set<Phi> trivialPhis = Sets.newIdentityHashSet(); - Iterator<Entry<DexType, CandidateInfo>> it = - classStaticizer.candidates.entrySet().iterator(); + Iterator<Entry<DexType, CandidateInfo>> it = classStaticizer.candidates.entrySet().iterator(); while (it.hasNext()) { Entry<DexType, CandidateInfo> entry = it.next(); DexType candidateType = entry.getKey(); @@ -185,33 +201,41 @@ } // CHECK: references to 'this' in instance methods are fixable. - boolean fixableThisPointer = true; - for (DexEncodedMethod method : candidateClass.methods()) { - if (method.isStatic() || factory().isConstructor(method.method)) { - continue; - } - IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder())); - assert code != null; - Value thisValue = code.getThis(); - assert thisValue != null; - visited.clear(); - trivialPhis.clear(); - boolean onlyHasTrivialPhis = testAndCollectPhisComposedOfThis( - visited, thisValue.uniquePhiUsers(), thisValue, trivialPhis); - if (thisValue.hasPhiUsers() && !onlyHasTrivialPhis) { - fixableThisPointer = false; - break; - } - } - if (!fixableThisPointer) { + TraversalContinuation fixableThisPointer = + candidateClass.traverseProgramMethods( + method -> { + IRCode code = method.buildIR(appView); + assert code != null; + Value thisValue = code.getThis(); + assert thisValue != null; + visited.clear(); + trivialPhis.clear(); + boolean onlyHasTrivialPhis = + testAndCollectPhisComposedOfThis( + visited, thisValue.uniquePhiUsers(), thisValue, trivialPhis); + if (thisValue.hasPhiUsers() && !onlyHasTrivialPhis) { + return TraversalContinuation.BREAK; + } + return TraversalContinuation.CONTINUE; + }, + definition -> !definition.isStatic() && !definition.isInstanceInitializer()); + if (fixableThisPointer.shouldBreak()) { it.remove(); continue; } + ProgramMethodSet referencedFrom; + if (classStaticizer.referencedFrom.containsKey(info)) { + referencedFrom = classStaticizer.referencedFrom.remove(info).build(appView); + materializedReferencedFromCollections.put(info, referencedFrom); + } else { + referencedFrom = ProgramMethodSet.empty(); + } + // 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.holder())); + for (ProgramMethod method : referencedFrom) { + IRCode code = method.buildIR(appView); assert code != null; List<Instruction> singletonUsers = Streams.stream(code.instructionIterator()) @@ -258,9 +282,11 @@ continue; } } + return materializedReferencedFromCollections; } - private void prepareCandidates() { + private void prepareCandidates( + Map<CandidateInfo, ProgramMethodSet> materializedReferencedFromCollections) { Set<DexEncodedMethod> removedInstanceMethods = Sets.newIdentityHashSet(); for (CandidateInfo candidate : classStaticizer.candidates.values()) { @@ -273,40 +299,46 @@ assert previous == null; // Collect instance methods to be staticized. - for (DexEncodedMethod method : candidateClass.methods()) { - if (!method.isStatic()) { - removedInstanceMethods.add(method); - if (!factory().isConstructor(method.method)) { - methodsToBeStaticized.add(method); - } - } - } + candidateClass.forEachProgramMethodMatching( + definition -> { + if (!definition.isStatic()) { + removedInstanceMethods.add(definition); + return !definition.isInstanceInitializer(); + } + return false; + }, + methodsToBeStaticized::add); singletonFields.put(candidate.singletonField.field, candidate); DexEncodedMethod getter = candidate.getter.get(); if (getter != null) { singletonGetters.put(getter.method, candidate); } - assert validMethods(candidate.referencedFrom); - referencingExtraMethods.addAll(candidate.referencedFrom); + ProgramMethodSet referencedFrom = + materializedReferencedFromCollections.getOrDefault(candidate, ProgramMethodSet.empty()); + assert validMethods(referencedFrom); + referencingExtraMethods.addAll(referencedFrom); } - referencingExtraMethods.removeAll(removedInstanceMethods); + removedInstanceMethods.forEach(referencingExtraMethods::remove); } - private boolean validMethods(Set<DexEncodedMethod> referencedFrom) { - for (DexEncodedMethod dexEncodedMethod : referencedFrom) { - DexClass clazz = appView.definitionForHolder(dexEncodedMethod.method); + private boolean validMethods(ProgramMethodSet referencedFrom) { + for (ProgramMethod method : referencedFrom) { + DexClass clazz = appView.definitionForHolder(method.getReference()); assert clazz != null; - assert clazz.lookupMethod(dexEncodedMethod.method) == dexEncodedMethod; + assert clazz.lookupMethod(method.getReference()) == method.getDefinition(); } return true; } private void enqueueMethodsWithCodeOptimizations( - Iterable<DexEncodedMethod> methods, + Iterable<ProgramMethod> methods, Consumer<ImmutableList.Builder<BiConsumer<IRCode, MethodProcessor>>> extension) { - for (DexEncodedMethod method : methods) { - extension.accept(processingQueue.computeIfAbsent(method, ignore -> ImmutableList.builder())); + for (ProgramMethod method : methods) { + methodsToReprocess.add(method); + extension.accept( + processingQueue.computeIfAbsent( + method.getDefinition(), ignore -> ImmutableList.builder())); } } @@ -322,24 +354,27 @@ */ private void processMethodsConcurrently( OptimizationFeedback feedback, ExecutorService executorService) throws ExecutionException { - Set<DexEncodedMethod> wave = processingQueue.keySet(); - OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.getInstance(wave); + OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.getInstance(methodsToReprocess); methodProcessor.forEachWave( method -> - forEachMethod(method, processingQueue.get(method).build(), feedback, methodProcessor), + forEachMethod( + method, + processingQueue.get(method.getDefinition()).build(), + feedback, + methodProcessor), executorService); // TODO(b/140767158): No need to clear if we can do every thing in one go. + methodsToReprocess.clear(); processingQueue.clear(); } // TODO(b/140766440): Should be part or variant of PostProcessor. private void forEachMethod( - DexEncodedMethod method, + ProgramMethod method, Collection<BiConsumer<IRCode, MethodProcessor>> codeOptimizations, OptimizationFeedback feedback, OneTimeMethodProcessor methodProcessor) { - Origin origin = appView.appInfo().originFor(method.holder()); - IRCode code = method.buildIR(appView, origin); + IRCode code = method.buildIR(appView); codeOptimizations.forEach(codeOptimization -> codeOptimization.accept(code, methodProcessor)); CodeRewriter.removeAssumeInstructions(appView, code); converter.removeDeadCodeAndFinalizeIR(method, code, feedback, Timing.empty()); @@ -686,11 +721,11 @@ return field; } - private Set<DexEncodedMethod> staticizeMethodSymbols() { + private ProgramMethodSet staticizeMethodSymbols() { BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create(); BiMap<DexField, DexField> fieldMapping = HashBiMap.create(); - Set<DexEncodedMethod> staticizedMethods = Sets.newIdentityHashSet(); + ProgramMethodSet staticizedMethods = ProgramMethodSet.create(); for (CandidateInfo candidate : classStaticizer.candidates.values()) { DexProgramClass candidateClass = candidate.candidate; @@ -702,7 +737,7 @@ } else if (!factory().isConstructor(method.method)) { DexEncodedMethod staticizedMethod = method.toStaticMethodWithoutThis(); newDirectMethods.add(staticizedMethod); - staticizedMethods.add(staticizedMethod); + staticizedMethods.createAndAdd(candidateClass, staticizedMethod); methodMapping.put(method.method, staticizedMethod.method); } } @@ -712,7 +747,7 @@ // Consider moving static members from candidate into host. DexType hostType = candidate.hostType(); if (candidateClass.type != hostType) { - DexClass hostClass = appView.definitionFor(hostType); + DexProgramClass hostClass = asProgramClassOrNull(appView.definitionFor(hostType)); assert hostClass != null; if (!classMembersConflict(candidateClass, hostClass)) { // Move all members of the candidate class into host class. @@ -736,9 +771,10 @@ } private void moveMembersIntoHost( - Set<DexEncodedMethod> staticizedMethods, + ProgramMethodSet staticizedMethods, DexProgramClass candidateClass, - DexType hostType, DexClass hostClass, + DexType hostType, + DexProgramClass hostClass, BiMap<DexMethod, DexMethod> methodMapping, BiMap<DexField, DexField> fieldMapping) { candidateToHostMapping.put(candidateClass.type, hostType); @@ -787,7 +823,7 @@ if (staticizedMethods.remove(method)) { // Properly update staticized methods to reprocess, i.e., add the corresponding one that // has just been migrated to the host class. - staticizedMethods.add(newMethod); + staticizedMethods.createAndAdd(hostClass, newMethod); } DexMethod originalMethod = methodMapping.inverse().get(method.method); if (originalMethod == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java index 331f14d..9402fbb 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -6,8 +6,11 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.ClasspathMethod; import com.android.tools.r8.graph.Code; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Position; @@ -35,30 +38,28 @@ } @Override - public final IRCode buildIR(DexEncodedMethod encodedMethod, AppView<?> appView, Origin origin) { - return IRBuilder.create( - encodedMethod, - appView, - getSourceCodeProvider().get(null), - origin).build(encodedMethod); + public final IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) { + return IRBuilder.create(method, appView, getSourceCodeProvider().get(null), origin) + .build(method); } @Override public IRCode buildInliningIR( - DexEncodedMethod context, - DexEncodedMethod encodedMethod, + ProgramMethod context, + ProgramMethod method, AppView<?> appView, ValueNumberGenerator valueNumberGenerator, Position callerPosition, Origin origin, MethodProcessor methodProcessor) { return IRBuilder.createForInlining( - encodedMethod, - appView, - getSourceCodeProvider().get(callerPosition), - origin, - methodProcessor, - valueNumberGenerator).build(context); + method, + appView, + getSourceCodeProvider().get(callerPosition), + origin, + methodProcessor, + valueNumberGenerator) + .build(context); } @Override @@ -67,7 +68,16 @@ } @Override - public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) { + public void registerCodeReferences(ProgramMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + @Override + public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) { + internalRegisterCodeReferences(method, registry); + } + + private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry registry) { getRegistryCallback().accept(registry); }
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 9f51daa..4f1e59a 100644 --- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java +++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.jar; import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; +import static org.objectweb.asm.Opcodes.V1_6; +import static org.objectweb.asm.Opcodes.V1_8; import com.android.tools.r8.ByteDataView; import com.android.tools.r8.ClassFileConsumer; @@ -106,7 +108,7 @@ for (DexProgramClass clazz : application.classes()) { if (clazz.getSynthesizedFrom().isEmpty() || options.isDesugaredLibraryCompilation() - || options.enableCfInterfaceMethodDesugaring) { + || options.cfToCfDesugar) { writeClass(clazz, consumer, markerString); } else { throw new Unimplemented("No support for synthetics in the Java bytecode backend."); @@ -194,16 +196,16 @@ // which do not have class file version. assert options.testing.enableForceNestBasedAccessDesugaringForTest || options.isDesugaredLibraryCompilation() - || options.enableCfInterfaceMethodDesugaring; + || options.cfToCfDesugar; // TODO(b/146424042): We may call static methods on interface classes so we have to go for - // version 52. - return options.enableCfInterfaceMethodDesugaring ? 52 : 0; + // Java 8. + return options.cfToCfDesugar ? V1_8 : 0; } return method.getClassFileVersion(); } private int getClassFileVersion(DexProgramClass clazz) { - int version = clazz.hasClassFileVersion() ? clazz.getInitialClassFileVersion() : 50; + int version = clazz.hasClassFileVersion() ? clazz.getInitialClassFileVersion() : V1_6; for (DexEncodedMethod method : clazz.directMethods()) { version = Math.max(version, getClassFileVersion(method)); }
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 332477a..2dc287a 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -10,9 +10,7 @@ import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod; import com.android.tools.r8.graph.AppView; -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.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexReference; @@ -20,6 +18,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexValue.DexItemBasedValueString; import com.android.tools.r8.graph.DexValue.DexValueString; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo; import com.android.tools.r8.ir.code.ConstString; @@ -92,12 +91,11 @@ } } - public void decoupleIdentifierNameStringsInMethod(DexEncodedMethod method, IRCode code) { - decoupleIdentifierNameStringsInBlocks(method, code, null); + public void decoupleIdentifierNameStringsInMethod(IRCode code) { + decoupleIdentifierNameStringsInBlocks(code, null); } - public void decoupleIdentifierNameStringsInBlocks( - DexEncodedMethod method, IRCode code, Set<BasicBlock> blocks) { + public void decoupleIdentifierNameStringsInBlocks(IRCode code, Set<BasicBlock> blocks) { if (!code.metadata().mayHaveConstString()) { return; } @@ -124,11 +122,11 @@ if (instruction.isStaticPut() || instruction.isInstancePut()) { iterator = decoupleIdentifierNameStringForFieldPutInstruction( - code, method, blockIterator, iterator, instruction.asFieldInstruction()); + code, blockIterator, iterator, instruction.asFieldInstruction()); } else if (instruction.isInvokeMethod()) { iterator = decoupleIdentifierNameStringForInvokeInstruction( - code, method, blockIterator, iterator, instruction.asInvokeMethod()); + code, blockIterator, iterator, instruction.asInvokeMethod()); } } } @@ -136,7 +134,6 @@ private InstructionListIterator decoupleIdentifierNameStringForFieldPutInstruction( IRCode code, - DexEncodedMethod method, ListIterator<BasicBlock> blocks, InstructionListIterator iterator, FieldInstruction instruction) { @@ -148,13 +145,13 @@ } Value in = instruction.value(); if (!in.isConstString()) { - warnUndeterminedIdentifierIfNecessary(field, method.holder(), instruction, null); + warnUndeterminedIdentifierIfNecessary(field, code.context(), instruction, null); return iterator; } DexString original = in.getConstInstruction().asConstString().getValue(); DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary(field, method.holder(), instruction, original); + warnUndeterminedIdentifierIfNecessary(field, code.context(), instruction, original); return iterator; } // Move the cursor back to $fieldPut @@ -199,7 +196,6 @@ private InstructionListIterator decoupleIdentifierNameStringForInvokeInstruction( IRCode code, - DexEncodedMethod method, ListIterator<BasicBlock> blocks, InstructionListIterator iterator, InvokeMethod invoke) { @@ -213,8 +209,7 @@ if (isReflectionMethod(appView.dexItemFactory(), invokedMethod) || isClassNameComparison) { DexReference itemBasedString = identifyIdentifier(invoke, appView); if (itemBasedString == null) { - DexType context = method.holder(); - warnUndeterminedIdentifierIfNecessary(invokedMethod, context, invoke, null); + warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, null); return iterator; } @@ -280,13 +275,13 @@ for (int i = 0; i < ins.size(); i++) { Value in = ins.get(i); if (!in.isConstString()) { - warnUndeterminedIdentifierIfNecessary(invokedMethod, method.holder(), invoke, null); + warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, null); continue; } DexString original = in.getConstInstruction().asConstString().getValue(); DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary(invokedMethod, method.holder(), invoke, original); + warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, original); continue; } // Move the cursor back to $invoke @@ -361,23 +356,18 @@ } private void warnUndeterminedIdentifierIfNecessary( - DexReference member, DexType originHolder, Instruction instruction, DexString original) { + DexReference member, ProgramMethod method, Instruction instruction, DexString original) { assert member.isDexField() || member.isDexMethod(); // Only issue warnings for -identifiernamestring rules explicitly added by the user. boolean matchedByExplicitRule = identifierNameStrings.getBoolean(member); if (!matchedByExplicitRule) { return; } - DexClass originClass = appView.definitionFor(originHolder); - // If the origin is a library class, it is out of developers' control. - if (originClass != null && originClass.isNotProgramClass()) { - return; - } // Undetermined identifiers matter only if minification is enabled. if (!appView.options().isMinifying()) { return; } - Origin origin = appView.appInfo().originFor(originHolder); + Origin origin = method.getOrigin(); String kind = member.isDexField() ? "field" : "method"; String originalMessage = original == null ? "what identifier string flows to " : "what '" + original.toString() + "' refers to, which flows to ";
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java index d37ef84..45892a2 100644 --- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java +++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -7,12 +7,13 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.logging.Log; import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.ForEachable; +import com.android.tools.r8.utils.IntBox; import com.google.common.collect.Sets; -import java.util.List; import java.util.Set; public class VisibilityBridgeRemover { @@ -24,52 +25,61 @@ } private void removeUnneededVisibilityBridgesFromClass(DexProgramClass clazz) { - DexEncodedMethod[] newDirectMethods = removeUnneededVisibilityBridges(clazz.directMethods()); + DexEncodedMethod[] newDirectMethods = + removeUnneededVisibilityBridges( + clazz::forEachProgramDirectMethod, clazz.getMethodCollection().numberOfDirectMethods()); if (newDirectMethods != null) { clazz.setDirectMethods(newDirectMethods); } - DexEncodedMethod[] newVirtualMethods = removeUnneededVisibilityBridges(clazz.virtualMethods()); + DexEncodedMethod[] newVirtualMethods = + removeUnneededVisibilityBridges( + clazz::forEachProgramVirtualMethod, + clazz.getMethodCollection().numberOfVirtualMethods()); if (newVirtualMethods != null) { clazz.setVirtualMethods(newVirtualMethods); } } - private DexEncodedMethod[] removeUnneededVisibilityBridges(List<DexEncodedMethod> methods) { - Set<DexEncodedMethod> methodsToBeRemoved = null; - for (DexEncodedMethod method : methods) { - if (isUnneededVisibilityBridge(method)) { - if (methodsToBeRemoved == null) { - methodsToBeRemoved = Sets.newIdentityHashSet(); - } - methodsToBeRemoved.add(method); - } - } - if (methodsToBeRemoved != null) { - Set<DexEncodedMethod> finalMethodsToBeRemoved = methodsToBeRemoved; - return methods.stream() - .filter(method -> !finalMethodsToBeRemoved.contains(method)) - .toArray(DexEncodedMethod[]::new); + private DexEncodedMethod[] removeUnneededVisibilityBridges( + ForEachable<ProgramMethod> methods, int size) { + Set<DexEncodedMethod> methodsToBeRemoved = Sets.newIdentityHashSet(); + methods.forEach( + method -> { + if (isUnneededVisibilityBridge(method)) { + methodsToBeRemoved.add(method.getDefinition()); + } + }); + if (!methodsToBeRemoved.isEmpty()) { + DexEncodedMethod[] newMethods = new DexEncodedMethod[size - methodsToBeRemoved.size()]; + IntBox i = new IntBox(0); + methods.forEach( + method -> { + if (!methodsToBeRemoved.contains(method.getDefinition())) { + newMethods[i.getAndIncrement()] = method.getDefinition(); + } + }); + return newMethods; } return null; } - private boolean isUnneededVisibilityBridge(DexEncodedMethod method) { - if (appView.appInfo().isPinned(method.method)) { + private boolean isUnneededVisibilityBridge(ProgramMethod method) { + if (appView.appInfo().isPinned(method.getReference())) { return false; } - MethodAccessFlags accessFlags = method.accessFlags; - if (!accessFlags.isBridge() || accessFlags.isAbstract()) { + DexEncodedMethod definition = method.getDefinition(); + if (!definition.isBridge() || definition.isAbstract()) { return false; } InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor(appView.dexItemFactory()); - method.getCode().registerCodeReferences(method, targetExtractor); + method.registerCodeReferences(targetExtractor); DexMethod target = targetExtractor.getTarget(); InvokeKind kind = targetExtractor.getKind(); // javac-generated visibility forward bridge method has same descriptor (name, signature and // return type). - if (target != null && target.hasSameProtoAndName(method.method)) { - assert !accessFlags.isPrivate() && !accessFlags.isConstructor(); + if (target != null && target.hasSameProtoAndName(method.getReference())) { + assert !definition.isPrivate() && !definition.isInstanceInitializer(); if (kind == InvokeKind.SUPER) { // This is a visibility forward, so check for the direct target. DexEncodedMethod targetMethod = @@ -77,10 +87,7 @@ if (targetMethod != null && targetMethod.accessFlags.isPublic()) { if (Log.ENABLED) { Log.info( - getClass(), - "Removing visibility forwarding %s -> %s", - method.method, - targetMethod.method); + getClass(), "Removing visibility forwarding %s -> %s", method, targetMethod.method); } return true; }
diff --git a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java index c2f5837..3ec10a2 100644 --- a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java +++ b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexCallSite; +import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItem; import com.android.tools.r8.graph.DexItemFactory; @@ -35,6 +36,15 @@ } public NamingLens compute(Map<PackageReference, PackageReference> mapping) { + // Prefetch all code objects to ensure we have seen all types. + // TODO(b/129925954): When updated, there is no need for this prefetch. + for (DexProgramClass clazz : appView.appInfo().classes()) { + for (DexEncodedMethod method : clazz.methods()) { + if (method.getCode() != null) { + method.getCode().asCfCode(); + } + } + } ImmutableMap.Builder<String, String> packingMappings = ImmutableMap.builder(); for (PackageReference key : mapping.keySet()) { String source = key.getPackageName(); @@ -52,32 +62,36 @@ packingMappings.put(sourceBinary, targetBinary); DexString sourceDescriptor = appView.dexItemFactory().createString("L" + sourceBinary); DexString targetDescriptor = appView.dexItemFactory().createString("L" + targetBinary); - for (DexProgramClass clazz : appView.appInfo().classes()) { - DexString descriptor = clazz.type.descriptor; - // Check if descriptor can be a prefix. - if (descriptor.size <= sourceDescriptor.size) { - continue; - } - // Check if it is either the empty prefix or a fully qualified package. - if (sourceDescriptor.size != 1 - && descriptor.content[sourceDescriptor.size] - != DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR) { - continue; - } - // Do a char-by-char comparison of the prefix. - if (!descriptor.startsWith(sourceDescriptor)) { - continue; - } - // This type should be mapped. - if (typeMappings.containsKey(clazz.type)) { - appView.options().reporter.error(RelocatorDiagnostic.typeRelocateAmbiguous(clazz.type)); - appView.options().reporter.failIfPendingErrors(); - } - DexString relocatedDescriptor = - clazz.type.descriptor.withNewPrefix( - sourceDescriptor, targetDescriptor, appView.dexItemFactory()); - typeMappings.put(clazz.type, relocatedDescriptor); - } + // TODO(b/129925954): Change to a lazy implementation in the naming lens. + appView + .dexItemFactory() + .forAllTypes( + type -> { + DexString descriptor = type.descriptor; + // Check if descriptor can be a prefix. + if (descriptor.size <= sourceDescriptor.size) { + return; + } + // Check if it is either the empty prefix or a fully qualified package. + if (sourceDescriptor.size != 1 + && descriptor.content[sourceDescriptor.size] + != DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR) { + return; + } + // Do a char-by-char comparison of the prefix. + if (!descriptor.startsWith(sourceDescriptor)) { + return; + } + // This type should be mapped. + if (typeMappings.containsKey(type)) { + appView.options().reporter.error(RelocatorDiagnostic.typeRelocateAmbiguous(type)); + appView.options().reporter.failIfPendingErrors(); + } + DexString relocatedDescriptor = + type.descriptor.withNewPrefix( + sourceDescriptor, targetDescriptor, appView.dexItemFactory()); + typeMappings.put(type, relocatedDescriptor); + }); } return new RelocatorNamingLens(typeMappings, packingMappings.build(), appView.dexItemFactory());
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 3f657b9..77edcc6 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull; import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys; import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding; @@ -34,6 +35,7 @@ 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.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -1074,6 +1076,15 @@ } } + public ProgramMethod lookupSingleProgramTarget( + Type type, + DexMethod target, + DexType invocationContext, + LibraryModeledPredicate modeledPredicate) { + return asProgramMethodOrNull( + lookupSingleTarget(type, target, invocationContext, modeledPredicate), this); + } + /** For mapping invoke virtual instruction to single target method. */ public DexEncodedMethod lookupSingleVirtualTarget( DexMethod method, DexType invocationContext, boolean isInterface) {
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 09aafdb..7c0afe9 100644 --- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java +++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -20,10 +20,9 @@ private final ProgramMethod context; protected final Enqueuer enqueuer; - public DefaultEnqueuerUseRegistry( - AppView<?> appView, DexProgramClass holder, DexEncodedMethod method, Enqueuer enqueuer) { + public DefaultEnqueuerUseRegistry(AppView<?> appView, ProgramMethod context, Enqueuer enqueuer) { super(appView.dexItemFactory()); - this.context = new ProgramMethod(holder, method); + this.context = context; this.enqueuer = enqueuer; } @@ -71,22 +70,22 @@ @Override public boolean registerInstanceFieldRead(DexField field) { - return enqueuer.traceInstanceFieldRead(field, context.getDefinition()); + return enqueuer.traceInstanceFieldRead(field, context); } @Override public boolean registerInstanceFieldReadFromMethodHandle(DexField field) { - return enqueuer.traceInstanceFieldReadFromMethodHandle(field, context.getDefinition()); + return enqueuer.traceInstanceFieldReadFromMethodHandle(field, context); } @Override public boolean registerInstanceFieldWrite(DexField field) { - return enqueuer.traceInstanceFieldWrite(field, context.getDefinition()); + return enqueuer.traceInstanceFieldWrite(field, context); } @Override public boolean registerInstanceFieldWriteFromMethodHandle(DexField field) { - return enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context.getDefinition()); + return enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context); } @Override @@ -96,43 +95,43 @@ @Override public boolean registerStaticFieldRead(DexField field) { - return enqueuer.traceStaticFieldRead(field, context.getDefinition()); + return enqueuer.traceStaticFieldRead(field, context); } @Override public boolean registerStaticFieldReadFromMethodHandle(DexField field) { - return enqueuer.traceStaticFieldReadFromMethodHandle(field, context.getDefinition()); + return enqueuer.traceStaticFieldReadFromMethodHandle(field, context); } @Override public boolean registerStaticFieldWrite(DexField field) { - return enqueuer.traceStaticFieldWrite(field, context.getDefinition()); + return enqueuer.traceStaticFieldWrite(field, context); } @Override public boolean registerStaticFieldWriteFromMethodHandle(DexField field) { - return enqueuer.traceStaticFieldWriteFromMethodHandle(field, context.getDefinition()); + return enqueuer.traceStaticFieldWriteFromMethodHandle(field, context); } @Override public boolean registerConstClass(DexType type) { - return enqueuer.traceConstClass(type, context.getDefinition()); + return enqueuer.traceConstClass(type, context); } @Override public boolean registerCheckCast(DexType type) { - return enqueuer.traceCheckCast(type, context.getDefinition()); + return enqueuer.traceCheckCast(type, context); } @Override public boolean registerTypeReference(DexType type) { - return enqueuer.traceTypeReference(type, context.getDefinition()); + return enqueuer.traceTypeReference(type, context); } @Override public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) { super.registerMethodHandle(methodHandle, use); - enqueuer.traceMethodHandle(methodHandle, use, context.getDefinition()); + enqueuer.traceMethodHandle(methodHandle, use, context); } @Override
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 a1d6312..9604389 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -85,7 +85,6 @@ import com.android.tools.r8.ir.desugar.LambdaDescriptor; import com.android.tools.r8.ir.desugar.LambdaRewriter; import com.android.tools.r8.logging.Log; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction; import com.android.tools.r8.shaking.EnqueuerWorklist.EnqueuerAction; import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness; @@ -104,6 +103,7 @@ import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.Visibility; import com.android.tools.r8.utils.WorkList; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; @@ -201,8 +201,8 @@ * Tracks the dependency between a method and the super-method it calls, if any. Used to make * super methods become live when they become reachable from a live sub-method. */ - private final Map<DexEncodedMethod, Set<DexEncodedMethod>> superInvokeDependencies = Maps - .newIdentityHashMap(); + private final Map<DexEncodedMethod, ProgramMethodSet> superInvokeDependencies = + Maps.newIdentityHashMap(); /** Set of instance fields that can be reached by read/write operations. */ private final Map<DexProgramClass, SetWithReason<DexEncodedField>> reachableInstanceFields = Maps.newIdentityHashMap(); @@ -283,10 +283,8 @@ /** A queue of items that need processing. Different items trigger different actions. */ private final EnqueuerWorklist workList; - /** - * A set of methods that need code inspection for Java reflection in use. - */ - private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet(); + /** A set of methods that need code inspection for Java reflection in use. */ + private final ProgramMethodSet pendingReflectiveUses = ProgramMethodSet.createLinked(); /** Mapping of types to the methods reachable at that type. */ private final Map<DexProgramClass, Set<DexMethod>> reachableVirtualTargets = @@ -336,7 +334,7 @@ private final LambdaRewriter lambdaRewriter; private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis; - private final Map<DexType, Pair<LambdaClass, DexEncodedMethod>> lambdaClasses = + private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses = new IdentityHashMap<>(); private final Map<DexEncodedMethod, Map<DexCallSite, LambdaClass>> lambdaCallSites = new IdentityHashMap<>(); @@ -600,15 +598,14 @@ } else { workList.enqueueMarkInstantiatedAction(clazz, null, InstantiationReason.KEEP_RULE, witness); if (clazz.hasDefaultInitializer()) { - DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer(); + ProgramMethod defaultInitializer = clazz.getProgramDefaultInitializer(); if (forceProguardCompatibility) { workList.enqueueMarkMethodKeptAction( - clazz, defaultInitializer, - graphReporter.reportCompatKeepDefaultInitializer(clazz, defaultInitializer)); + graphReporter.reportCompatKeepDefaultInitializer(defaultInitializer)); } if (clazz.isExternalizable(appView)) { - enqueueMarkMethodLiveAction(clazz, defaultInitializer, witness); + enqueueMarkMethodLiveAction(defaultInitializer, witness); } } } @@ -626,8 +623,7 @@ DexProgramClass holder = getProgramClassOrNull(encodedMethod.holder()); if (holder != null) { workList.enqueueMarkMethodKeptAction( - holder, - encodedMethod, + new ProgramMethod(holder, encodedMethod), graphReporter.reportKeepMethod(precondition, rules, encodedMethod)); } } else { @@ -649,16 +645,14 @@ clazz = superClass; } if (clazz.hasDefaultInitializer()) { - enqueueMarkMethodLiveAction(clazz, clazz.getDefaultInitializer(), reason); + enqueueMarkMethodLiveAction(clazz.getProgramDefaultInitializer(), reason); } } // Utility to avoid adding to the worklist if already live. - private boolean enqueueMarkMethodLiveAction( - DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.holder() == clazz.type; - if (liveMethods.add(clazz, method, reason)) { - workList.enqueueMarkMethodLiveAction(clazz, method, reason); + private boolean enqueueMarkMethodLiveAction(ProgramMethod method, KeepReason reason) { + if (liveMethods.add(method, reason)) { + workList.enqueueMarkMethodLiveAction(method, reason); return true; } return false; @@ -678,34 +672,39 @@ // private boolean registerMethodWithTargetAndContext( - Map<DexMethod, Set<DexEncodedMethod>> seen, DexMethod method, DexEncodedMethod context) { + Map<DexMethod, Set<DexEncodedMethod>> seen, DexMethod method, ProgramMethod context) { DexType baseHolder = method.holder.toBaseType(appView.dexItemFactory()); if (baseHolder.isClassType()) { markTypeAsLive(baseHolder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context)); - return seen.computeIfAbsent(method, ignore -> Sets.newIdentityHashSet()).add(context); + return seen.computeIfAbsent(method, ignore -> Sets.newIdentityHashSet()) + .add(context.getDefinition()); } return false; } - public boolean registerFieldRead(DexField field, DexEncodedMethod context) { - return registerFieldAccess(field, context, true, false); + public boolean registerFieldRead(DexField field, ProgramMethod context) { + return registerFieldAccess(field, context.getDefinition(), true, false); } - public boolean registerReflectiveFieldRead(DexField field, DexEncodedMethod context) { - return registerFieldAccess(field, context, true, true); + public boolean registerFieldReadFromAnnotation(DexField field) { + return registerFieldAccess(field, DexEncodedMethod.ANNOTATION_REFERENCE, true, false); } - public boolean registerFieldWrite(DexField field, DexEncodedMethod context) { - return registerFieldAccess(field, context, false, false); + public boolean registerReflectiveFieldRead(DexField field, ProgramMethod context) { + return registerFieldAccess(field, context.getDefinition(), true, true); } - public boolean registerReflectiveFieldWrite(DexField field, DexEncodedMethod context) { - return registerFieldAccess(field, context, false, true); + public boolean registerFieldWrite(DexField field, ProgramMethod context) { + return registerFieldAccess(field, context.getDefinition(), false, false); } - public boolean registerReflectiveFieldAccess(DexField field, DexEncodedMethod context) { - boolean changed = registerFieldAccess(field, context, true, true); - changed |= registerFieldAccess(field, context, false, true); + public boolean registerReflectiveFieldWrite(DexField field, ProgramMethod context) { + return registerFieldAccess(field, context.getDefinition(), false, true); + } + + public boolean registerReflectiveFieldAccess(DexField field, ProgramMethod context) { + boolean changed = registerFieldAccess(field, context.getDefinition(), true, true); + changed |= registerFieldAccess(field, context.getDefinition(), false, true); return changed; } @@ -745,12 +744,6 @@ return isRead ? info.recordRead(field, context) : info.recordWrite(field, context); } - private boolean isStringConcat(DexMethodHandle bootstrapMethod) { - return bootstrapMethod.type.isInvokeStatic() - && (bootstrapMethod.asMethod() == appView.dexItemFactory().stringConcatWithConstantsMethod - || bootstrapMethod.asMethod() == appView.dexItemFactory().stringConcatMethod); - } - void traceCallSite(DexCallSite callSite, ProgramMethod context) { DexProgramClass bootstrapClass = getProgramClassOrNull(callSite.bootstrapMethod.asMethod().holder); @@ -771,7 +764,7 @@ if (code != null) { LambdaClass lambdaClass = lambdaRewriter.getOrCreateLambdaClass(descriptor, contextMethod.holder()); - lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, contextMethod)); + lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, context)); lambdaCallSites .computeIfAbsent(contextMethod, k -> new IdentityHashMap<>()) .put(callSite, lambdaClass); @@ -783,7 +776,7 @@ desugaredLambdaImplementationMethods.add(descriptor.implHandle.asMethod()); } } else { - markLambdaAsInstantiated(descriptor, contextMethod); + markLambdaAsInstantiated(descriptor, context); transitionMethodsForInstantiatedLambda(descriptor); callSites.add(callSite); } @@ -820,11 +813,11 @@ } } - boolean traceCheckCast(DexType type, DexEncodedMethod currentMethod) { + boolean traceCheckCast(DexType type, ProgramMethod currentMethod) { return traceConstClassOrCheckCast(type, currentMethod); } - boolean traceConstClass(DexType type, DexEncodedMethod currentMethod) { + boolean traceConstClass(DexType type, ProgramMethod currentMethod) { // We conservatively group T.class and T[].class to ensure that we do not merge T with S if // potential locks on T[].class and S[].class exists. DexType baseType = type.toBaseType(appView.dexItemFactory()); @@ -837,7 +830,7 @@ return traceConstClassOrCheckCast(type, currentMethod); } - private boolean traceConstClassOrCheckCast(DexType type, DexEncodedMethod currentMethod) { + private boolean traceConstClassOrCheckCast(DexType type, ProgramMethod currentMethod) { if (!forceProguardCompatibility) { return traceTypeReference(type, currentMethod); } @@ -868,7 +861,7 @@ initClassReferences.put( type, computeMinimumRequiredVisibilityForInitClassField(type, currentMethod.getHolder())); - markTypeAsLive(type, classReferencedFromReporter(currentMethod.getDefinition())); + markTypeAsLive(type, classReferencedFromReporter(currentMethod)); markDirectAndIndirectClassInitializersAsLive(clazz); return true; } @@ -914,7 +907,7 @@ } void traceMethodHandle( - DexMethodHandle methodHandle, MethodHandleUse use, DexEncodedMethod currentMethod) { + DexMethodHandle methodHandle, MethodHandleUse use, ProgramMethod currentMethod) { // If a method handle is not an argument to a lambda metafactory it could flow to a // MethodHandle.invokeExact invocation. For that to work, the receiver type cannot have // changed and therefore we cannot perform member rebinding. For these handles, we maintain @@ -938,33 +931,28 @@ } } - boolean traceTypeReference(DexType type, DexEncodedMethod currentMethod) { + boolean traceTypeReference(DexType type, ProgramMethod currentMethod) { markTypeAsLive(type, classReferencedFromReporter(currentMethod)); return true; } boolean traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) { - DexProgramClass currentHolder = context.getHolder(); - DexEncodedMethod currentMethod = context.getDefinition(); boolean skipTracing = registerDeferredActionForDeadProtoBuilder( invokedMethod.holder, - currentMethod, - () -> - workList.enqueueTraceInvokeDirectAction( - invokedMethod, currentHolder, currentMethod)); + context, + () -> workList.enqueueTraceInvokeDirectAction(invokedMethod, context)); if (skipTracing) { addDeadProtoTypeCandidate(invokedMethod.holder); return false; } - return traceInvokeDirect( - invokedMethod, context, KeepReason.invokedFrom(currentHolder, currentMethod)); + return traceInvokeDirect(invokedMethod, context, KeepReason.invokedFrom(context)); } /** Returns true if a deferred action was registered. */ private boolean registerDeferredActionForDeadProtoBuilder( - DexType type, DexEncodedMethod currentMethod, Action action) { + DexType type, ProgramMethod currentMethod, Action action) { DexProgramClass clazz = getProgramClassOrNull(type); if (clazz != null) { return appView.withGeneratedMessageLiteBuilderShrinker( @@ -978,13 +966,12 @@ boolean traceInvokeDirectFromLambda(DexMethod invokedMethod, ProgramMethod context) { return traceInvokeDirect( - invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition())); + invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context)); } private boolean traceInvokeDirect( DexMethod invokedMethod, ProgramMethod context, KeepReason reason) { - DexEncodedMethod currentMethod = context.getDefinition(); - if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, currentMethod)) { + if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, context)) { return false; } if (Log.ENABLED) { @@ -1001,13 +988,12 @@ boolean traceInvokeInterfaceFromLambda(DexMethod invokedMethod, ProgramMethod context) { return traceInvokeInterface( - invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition())); + invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context)); } private boolean traceInvokeInterface( DexMethod method, ProgramMethod context, KeepReason keepReason) { - DexEncodedMethod currentMethod = context.getDefinition(); - if (!registerMethodWithTargetAndContext(interfaceInvokes, method, currentMethod)) { + if (!registerMethodWithTargetAndContext(interfaceInvokes, method, context)) { return false; } if (Log.ENABLED) { @@ -1024,32 +1010,31 @@ boolean traceInvokeStaticFromLambda(DexMethod invokedMethod, ProgramMethod context) { return traceInvokeStatic( - invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition())); + invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context)); } private boolean traceInvokeStatic( DexMethod invokedMethod, ProgramMethod context, KeepReason reason) { - DexEncodedMethod currentMethod = context.getDefinition(); DexItemFactory dexItemFactory = appView.dexItemFactory(); if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod) || dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) { // Implicitly add -identifiernamestring rule for the Java reflection in use. identifierNameStrings.add(invokedMethod); // Revisit the current method to implicitly add -keep rule for items with reflective access. - pendingReflectiveUses.add(currentMethod); + pendingReflectiveUses.add(context); } // See comment in handleJavaLangEnumValueOf. if (invokedMethod == dexItemFactory.enumMethods.valueOf) { - pendingReflectiveUses.add(currentMethod); + pendingReflectiveUses.add(context); } // Handling of application services. if (dexItemFactory.serviceLoaderMethods.isLoadMethod(invokedMethod)) { - pendingReflectiveUses.add(currentMethod); + pendingReflectiveUses.add(context); } if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) { - pendingReflectiveUses.add(currentMethod); + pendingReflectiveUses.add(context); } - if (!registerMethodWithTargetAndContext(staticInvokes, invokedMethod, currentMethod)) { + if (!registerMethodWithTargetAndContext(staticInvokes, invokedMethod, context)) { return false; } if (Log.ENABLED) { @@ -1061,17 +1046,16 @@ } boolean traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) { - DexEncodedMethod currentMethod = context.getDefinition(); // We have to revisit super invokes based on the context they are found in. The same // method descriptor will hit different targets, depending on the context it is used in. - DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, currentMethod); - if (!registerMethodWithTargetAndContext(superInvokes, invokedMethod, currentMethod)) { + DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, context); + if (!registerMethodWithTargetAndContext(superInvokes, invokedMethod, context)) { return false; } if (Log.ENABLED) { Log.verbose(getClass(), "Register invokeSuper `%s`.", actualTarget); } - workList.enqueueMarkReachableSuperAction(invokedMethod, currentMethod); + workList.enqueueMarkReachableSuperAction(invokedMethod, context); invokeAnalyses.forEach(analysis -> analysis.traceInvokeSuper(invokedMethod, context)); return true; } @@ -1082,22 +1066,21 @@ boolean traceInvokeVirtualFromLambda(DexMethod invokedMethod, ProgramMethod context) { return traceInvokeVirtual( - invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition())); + invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context)); } private boolean traceInvokeVirtual( DexMethod invokedMethod, ProgramMethod context, KeepReason reason) { if (invokedMethod == appView.dexItemFactory().classMethods.newInstance || invokedMethod == appView.dexItemFactory().constructorMethods.newInstance) { - pendingReflectiveUses.add(context.getDefinition()); + pendingReflectiveUses.add(context); } else if (appView.dexItemFactory().classMethods.isReflectiveMemberLookup(invokedMethod)) { // Implicitly add -identifiernamestring rule for the Java reflection in use. identifierNameStrings.add(invokedMethod); // Revisit the current method to implicitly add -keep rule for items with reflective access. - pendingReflectiveUses.add(context.getDefinition()); + pendingReflectiveUses.add(context); } - if (!registerMethodWithTargetAndContext( - virtualInvokes, invokedMethod, context.getDefinition())) { + if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, context)) { return false; } if (Log.ENABLED) { @@ -1109,10 +1092,9 @@ } boolean traceNewInstance(DexType type, ProgramMethod context) { - DexEncodedMethod currentMethod = context.getDefinition(); boolean skipTracing = registerDeferredActionForDeadProtoBuilder( - type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, context)); + type, context, () -> workList.enqueueTraceNewInstanceAction(type, context)); if (skipTracing) { addDeadProtoTypeCandidate(type); return false; @@ -1122,15 +1104,12 @@ type, context, InstantiationReason.NEW_INSTANCE_INSTRUCTION, - KeepReason.instantiatedIn(currentMethod)); + KeepReason.instantiatedIn(context)); } boolean traceNewInstanceFromLambda(DexType type, ProgramMethod context) { return traceNewInstance( - type, - context, - InstantiationReason.LAMBDA, - KeepReason.invokedFromLambdaCreatedIn(context.getDefinition())); + type, context, InstantiationReason.LAMBDA, KeepReason.invokedFromLambdaCreatedIn(context)); } private boolean traceNewInstance( @@ -1138,29 +1117,27 @@ ProgramMethod context, InstantiationReason instantiationReason, KeepReason keepReason) { - DexEncodedMethod currentMethod = context.getDefinition(); DexProgramClass clazz = getProgramClassOrNull(type); if (clazz != null) { if (clazz.isAnnotation() || clazz.isInterface()) { markTypeAsLive(clazz, graphReporter.registerClass(clazz, keepReason)); } else { - workList.enqueueMarkInstantiatedAction( - clazz, currentMethod, instantiationReason, keepReason); + workList.enqueueMarkInstantiatedAction(clazz, context, instantiationReason, keepReason); } } return true; } - boolean traceInstanceFieldRead(DexField field, DexEncodedMethod currentMethod) { + boolean traceInstanceFieldRead(DexField field, ProgramMethod currentMethod) { return traceInstanceFieldRead(field, currentMethod, false); } - boolean traceInstanceFieldReadFromMethodHandle(DexField field, DexEncodedMethod currentMethod) { + boolean traceInstanceFieldReadFromMethodHandle(DexField field, ProgramMethod currentMethod) { return traceInstanceFieldRead(field, currentMethod, true); } private boolean traceInstanceFieldRead( - DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) { + DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) { if (!registerFieldRead(field, currentMethod)) { return false; } @@ -1200,16 +1177,16 @@ return true; } - boolean traceInstanceFieldWrite(DexField field, DexEncodedMethod currentMethod) { + boolean traceInstanceFieldWrite(DexField field, ProgramMethod currentMethod) { return traceInstanceFieldWrite(field, currentMethod, false); } - boolean traceInstanceFieldWriteFromMethodHandle(DexField field, DexEncodedMethod currentMethod) { + boolean traceInstanceFieldWriteFromMethodHandle(DexField field, ProgramMethod currentMethod) { return traceInstanceFieldWrite(field, currentMethod, true); } private boolean traceInstanceFieldWrite( - DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) { + DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) { if (!registerFieldWrite(field, currentMethod)) { return false; } @@ -1249,16 +1226,16 @@ return true; } - boolean traceStaticFieldRead(DexField field, DexEncodedMethod currentMethod) { + boolean traceStaticFieldRead(DexField field, ProgramMethod currentMethod) { return traceStaticFieldRead(field, currentMethod, false); } - boolean traceStaticFieldReadFromMethodHandle(DexField field, DexEncodedMethod currentMethod) { + boolean traceStaticFieldReadFromMethodHandle(DexField field, ProgramMethod currentMethod) { return traceStaticFieldRead(field, currentMethod, true); } private boolean traceStaticFieldRead( - DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) { + DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) { if (!registerFieldRead(field, currentMethod)) { return false; } @@ -1308,16 +1285,16 @@ return true; } - boolean traceStaticFieldWrite(DexField field, DexEncodedMethod currentMethod) { + boolean traceStaticFieldWrite(DexField field, ProgramMethod currentMethod) { return traceStaticFieldWrite(field, currentMethod, false); } - boolean traceStaticFieldWriteFromMethodHandle(DexField field, DexEncodedMethod currentMethod) { + boolean traceStaticFieldWriteFromMethodHandle(DexField field, ProgramMethod currentMethod) { return traceStaticFieldWrite(field, currentMethod, true); } private boolean traceStaticFieldWrite( - DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) { + DexField field, ProgramMethod currentMethod, boolean fromMethodHandle) { if (!registerFieldWrite(field, currentMethod)) { return false; } @@ -1368,17 +1345,17 @@ } private Function<DexProgramClass, KeepReasonWitness> classReferencedFromReporter( - DexEncodedMethod currentMethod) { + ProgramMethod currentMethod) { return clazz -> graphReporter.reportClassReferencedFrom(clazz, currentMethod); } - private DexMethod getInvokeSuperTarget(DexMethod method, DexEncodedMethod currentMethod) { + private DexMethod getInvokeSuperTarget(DexMethod method, ProgramMethod currentMethod) { DexClass methodHolderClass = appView.definitionFor(method.holder); if (methodHolderClass != null && methodHolderClass.isInterface()) { return method; } - DexClass holderClass = appView.definitionFor(currentMethod.holder()); - if (holderClass == null || holderClass.superType == null || holderClass.isInterface()) { + DexProgramClass holderClass = currentMethod.getHolder(); + if (holderClass.superType == null || holderClass.isInterface()) { // We do not know better or this call is made from an interface. return method; } @@ -1390,9 +1367,10 @@ // Actual actions performed. // - private boolean verifyMethodIsTargeted(DexEncodedMethod method) { - assert !method.isClassInitializer() : "Class initializers are never targeted"; - assert targetedMethods.contains(method); + private boolean verifyMethodIsTargeted(ProgramMethod method) { + DexEncodedMethod definition = method.getDefinition(); + assert !definition.isClassInitializer() : "Class initializers are never targeted"; + assert targetedMethods.contains(definition); return true; } @@ -1539,13 +1517,12 @@ private void ensureMethodsContinueToWidenAccess( DexProgramClass clazz, ScopedDexMethodSet seen, KeepReason reason) { - for (DexEncodedMethod method : clazz.virtualMethods()) { - if (seen.addMethodIfMoreVisible(method) == AddMethodIfMoreVisibleResult.ADDED_MORE_VISIBLE - && clazz.isProgramClass() - && appView.appInfo().methodDefinedInInterfaces(method, clazz.type)) { - markMethodAsTargeted(clazz, method, reason); - } - } + clazz.forEachProgramVirtualMethodMatching( + definition -> + seen.addMethodIfMoreVisible(definition) + == AddMethodIfMoreVisibleResult.ADDED_MORE_VISIBLE + && appView.appInfo().methodDefinedInInterfaces(definition, clazz.type), + method -> markMethodAsTargeted(method, reason)); } private void markInterfaceTypeAsLiveViaInheritanceClause( @@ -1595,9 +1572,7 @@ } private void enqueueHolderWithDependentInstanceConstructor( - DexProgramClass clazz, - DexEncodedMethod instanceInitializer, - Set<ProguardKeepRuleBase> reasons) { + DexProgramClass clazz, ProgramMethod instanceInitializer, Set<ProguardKeepRuleBase> reasons) { enqueueRootItem(clazz, reasons); } @@ -1677,8 +1652,8 @@ return resolutionResult.asSingleResolution(); } - private void handleInvokeOfStaticTarget(DexMethod method, KeepReason reason) { - SingleResolutionResult resolution = resolveMethod(method, reason); + private void handleInvokeOfStaticTarget(DexMethod reference, KeepReason reason) { + SingleResolutionResult resolution = resolveMethod(reference, reason); if (resolution == null || resolution.getResolvedHolder().isNotProgramClass()) { return; } @@ -1687,12 +1662,13 @@ // We have to mark the resolved method as targeted even if it cannot actually be invoked // to make sure the invocation will keep failing in the appropriate way. - markMethodAsTargeted(clazz, encodedMethod, reason); + ProgramMethod method = new ProgramMethod(clazz, encodedMethod); + markMethodAsTargeted(method, reason); // Only mark methods for which invocation will succeed at runtime live. if (encodedMethod.isStatic()) { markDirectAndIndirectClassInitializersAsLive(clazz); - markDirectStaticOrConstructorMethodAsLive(clazz, encodedMethod, reason); + markDirectStaticOrConstructorMethodAsLive(method, reason); } } @@ -1719,13 +1695,13 @@ /** Returns true if the class initializer became live for the first time. */ private boolean markDirectClassInitializerAsLive(DexProgramClass clazz) { - DexEncodedMethod clinit = clazz.getClassInitializer(); + ProgramMethod clinit = clazz.getProgramClassInitializer(); KeepReasonWitness witness = graphReporter.reportReachableClassInitializer(clazz, clinit); if (!initializedTypes.add(clazz, witness)) { return false; } - if (clinit != null && clinit.getOptimizationInfo().mayHaveSideEffects()) { - markDirectStaticOrConstructorMethodAsLive(clazz, clinit, witness); + if (clinit != null && clinit.getDefinition().getOptimizationInfo().mayHaveSideEffects()) { + markDirectStaticOrConstructorMethodAsLive(clinit, witness); } return true; } @@ -1735,37 +1711,39 @@ handleInvokeOfDirectTarget(method, reason); } - private void handleInvokeOfDirectTarget(DexMethod method, KeepReason reason) { - DexType holder = method.holder; + private void handleInvokeOfDirectTarget(DexMethod reference, KeepReason reason) { + DexType holder = reference.holder; DexProgramClass clazz = getProgramClassOrNull(holder); if (clazz == null) { - recordMethodReference(method); + recordMethodReference(reference); return; } // TODO(zerny): Is it ok that we lookup in both the direct and virtual pool here? - DexEncodedMethod encodedMethod = clazz.lookupMethod(method); + DexEncodedMethod encodedMethod = clazz.lookupMethod(reference); if (encodedMethod == null) { - reportMissingMethod(method); + reportMissingMethod(reference); return; } + ProgramMethod method = new ProgramMethod(clazz, encodedMethod); + // We have to mark the resolved method as targeted even if it cannot actually be invoked // to make sure the invocation will keep failing in the appropriate way. - markMethodAsTargeted(clazz, encodedMethod, reason); + markMethodAsTargeted(method, reason); // Only mark methods for which invocation will succeed at runtime live. if (encodedMethod.isStatic()) { return; } - markDirectStaticOrConstructorMethodAsLive(clazz, encodedMethod, reason); + markDirectStaticOrConstructorMethodAsLive(method, reason); // It is valid to have an invoke-direct instruction in a default interface method that // targets another default method in the same interface (see testInvokeSpecialToDefault- // Method). In a class, that would lead to a verification error. if (encodedMethod.isNonPrivateVirtualMethod() && virtualMethodsTargetedByInvokeDirect.add(encodedMethod.method)) { - enqueueMarkMethodLiveAction(clazz, encodedMethod, reason); + enqueueMarkMethodLiveAction(method, reason); } } @@ -1839,26 +1817,26 @@ } } - private void markMethodAsTargeted( - DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.holder() == clazz.type; - if (!targetedMethods.add(method, reason)) { + private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) { + DexEncodedMethod definition = method.getDefinition(); + DexProgramClass holder = method.getHolder(); + if (!targetedMethods.add(definition, reason)) { // Already targeted. return; } markReferencedTypesAsLive(method); - processAnnotations(clazz, method); - method.parameterAnnotationsList.forEachAnnotation( - annotation -> processAnnotation(clazz, method, annotation)); + processAnnotations(holder, definition); + definition.parameterAnnotationsList.forEachAnnotation( + annotation -> processAnnotation(holder, definition, annotation)); if (Log.ENABLED) { - Log.verbose(getClass(), "Method `%s` is targeted.", method.method); + Log.verbose(getClass(), "Method `%s` is targeted.", method); } if (forceProguardCompatibility) { // Keep targeted default methods in compatibility mode. The tree pruner will otherwise make // these methods abstract, whereas Proguard does not (seem to) touch their code. - if (!method.accessFlags.isAbstract() && clazz.isInterface()) { - markMethodAsLiveWithCompatRule(clazz, method); + if (!definition.isAbstract() && holder.isInterface()) { + markMethodAsLiveWithCompatRule(method); } } } @@ -1870,7 +1848,7 @@ // Package protected due to entry point from worklist. void processNewlyInstantiatedClass( DexProgramClass clazz, - DexEncodedMethod context, + ProgramMethod context, InstantiationReason instantiationReason, KeepReason keepReason) { assert !clazz.isAnnotation(); @@ -1905,7 +1883,7 @@ // TODO(b/146016987): Make this the single instantiation entry rather than the worklist action. private boolean markInstantiatedClass( DexProgramClass clazz, - DexEncodedMethod context, + ProgramMethod context, InstantiationReason instantiationReason, KeepReason keepReason) { assert !clazz.isInterface(); @@ -1923,7 +1901,7 @@ transitionDependentItemsForInstantiatedInterface(clazz); } - private void markLambdaAsInstantiated(LambdaDescriptor descriptor, DexEncodedMethod context) { + private void markLambdaAsInstantiated(LambdaDescriptor descriptor, ProgramMethod context) { // Each descriptor is unique, so there is no check for already marking the lambda. for (DexType iface : descriptor.interfaces) { checkLambdaInterface(iface, context); @@ -1931,13 +1909,13 @@ } } - private void checkLambdaInterface(DexType itf, DexEncodedMethod context) { + private void checkLambdaInterface(DexType itf, ProgramMethod context) { DexClass clazz = definitionFor(itf); if (clazz == null) { StringDiagnostic message = new StringDiagnostic( "Lambda expression implements missing interface `" + itf.toSourceString() + "`", - appInfo.originFor(context.holder())); + context.getOrigin()); options.reporter.warning(message); } else if (!clazz.isInterface()) { StringDiagnostic message = @@ -1946,7 +1924,7 @@ + "`" + itf.toSourceString() + "`", - appInfo.originFor(context.holder())); + context.getOrigin()); options.reporter.warning(message); } } @@ -2040,8 +2018,7 @@ // simply marked live on its holder. if (resolutionMethod.getDefinition().isPrivateMethod()) { markVirtualMethodAsLive( - resolutionMethod.getHolder(), - resolutionMethod.getDefinition(), + resolutionMethod, graphReporter.reportReachableMethodAsLive( resolutionMethod.getDefinition().method, resolutionMethod)); return; @@ -2194,7 +2171,7 @@ } } - private void markFieldAsTargeted(DexField field, DexEncodedMethod context) { + private void markFieldAsTargeted(DexField field, ProgramMethod context) { markTypeAsLive(field.type, clazz -> graphReporter.reportClassReferencedFrom(clazz, context)); markTypeAsLive(field.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context)); } @@ -2256,34 +2233,28 @@ analyses.forEach(analysis -> analysis.processNewlyLiveField(field)); } - private void markDirectStaticOrConstructorMethodAsLive( - DexProgramClass clazz, DexEncodedMethod encodedMethod, KeepReason reason) { - assert encodedMethod.holder() == clazz.type; - - if (!enqueueMarkMethodLiveAction(clazz, encodedMethod, reason)) { + private void markDirectStaticOrConstructorMethodAsLive(ProgramMethod method, KeepReason reason) { + if (!enqueueMarkMethodLiveAction(method, reason)) { // Already marked live. return; } // Should already have marked the type live previously. - DexMethod method = encodedMethod.method; - assert encodedMethod.isClassInitializer() || verifyMethodIsTargeted(encodedMethod); - assert verifyTypeIsLive(clazz); + assert method.getDefinition().isClassInitializer() || verifyMethodIsTargeted(method); + assert verifyTypeIsLive(method.getHolder()); if (Log.ENABLED) { - Log.verbose( - getClass(), "Method `%s` has become live due to direct invoke", encodedMethod.method); + Log.verbose(getClass(), "Method `%s` has become live due to direct invoke", method); } } - private void markVirtualMethodAsLive( - DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { + private void markVirtualMethodAsLive(ProgramMethod method, KeepReason reason) { assert method != null; // Only explicit keep rules or reflective use should make abstract methods live. - assert !method.accessFlags.isAbstract() + assert !method.getDefinition().isAbstract() || reason.isDueToKeepRule() || reason.isDueToReflectiveUse(); - if (enqueueMarkMethodLiveAction(clazz, method, reason)) { + if (enqueueMarkMethodLiveAction(method, reason)) { if (Log.ENABLED) { - Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method.method); + Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method); } } } @@ -2434,7 +2405,7 @@ // need at least an abstract version of it so that it can be targeted. DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass(); DexEncodedMethod resolvedMethod = resolution.getResolvedMethod(); - markMethodAsTargeted(resolvedHolder, resolvedMethod, reason); + markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason); DexProgramClass context = contextOrNull == null ? null : contextOrNull.getHolder(); if (contextOrNull != null @@ -2484,8 +2455,7 @@ DexClassAndMethod target, Function<ProgramMethod, KeepReasonWitness> reason) { ProgramMethod programMethod = target.asProgramMethod(); if (programMethod != null && !programMethod.getDefinition().isAbstract()) { - markVirtualMethodAsLive( - programMethod.getHolder(), programMethod.getDefinition(), reason.apply(programMethod)); + markVirtualMethodAsLive(programMethod, reason.apply(programMethod)); } } @@ -2493,10 +2463,7 @@ LookupLambdaTarget target, Function<ProgramMethod, KeepReasonWitness> reason) { ProgramMethod implementationMethod = target.getImplementationMethod().asProgramMethod(); if (implementationMethod != null) { - enqueueMarkMethodLiveAction( - implementationMethod.getHolder(), - implementationMethod.getDefinition(), - reason.apply(implementationMethod)); + enqueueMarkMethodLiveAction(implementationMethod, reason.apply(implementationMethod)); } } @@ -2508,7 +2475,7 @@ DexProgramClass clazz = getProgramClassOrNull(method.holder()); if (clazz != null) { failedResolutionTargets.add(method.method); - markMethodAsTargeted(clazz, method, reason); + markMethodAsTargeted(new ProgramMethod(clazz, method), reason); } }); } @@ -2530,28 +2497,28 @@ if (valuesMethod != null) { // TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the // marking for not renaming it is in the root set. - workList.enqueueMarkMethodKeptAction(clazz, valuesMethod, reason); + workList.enqueueMarkMethodKeptAction(new ProgramMethod(clazz, valuesMethod), reason); pinnedItems.add(valuesMethod.toReference()); rootSet.shouldNotBeMinified(valuesMethod.toReference()); } } // Package protected due to entry point from worklist. - void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) { + void markSuperMethodAsReachable(DexMethod reference, ProgramMethod from) { KeepReason reason = KeepReason.targetedBySuperFrom(from); - SingleResolutionResult resolution = resolveMethod(method, reason); + SingleResolutionResult resolution = resolveMethod(reference, reason); if (resolution == null) { return; } // If the resolution is in the program, mark it targeted. if (resolution.getResolvedHolder().isProgramClass()) { markMethodAsTargeted( - resolution.getResolvedHolder().asProgramClass(), resolution.getResolvedMethod(), reason); + new ProgramMethod( + resolution.getResolvedHolder().asProgramClass(), resolution.getResolvedMethod()), + reason); } // 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.holder()).asProgramClass(); - DexEncodedMethod target = resolution.lookupInvokeSuperTarget(fromHolder, appInfo); + DexEncodedMethod target = resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo); if (target == null) { failedResolutionTargets.add(resolution.getResolvedMethod().method); return; @@ -2561,16 +2528,19 @@ if (clazz == null) { return; } + + ProgramMethod method = new ProgramMethod(clazz, target); + if (Log.ENABLED) { - Log.verbose(getClass(), "Adding super constraint from `%s` to `%s`", from.method, - target.method); + Log.verbose(getClass(), "Adding super constraint from `%s` to `%s`", from, target.method); } - if (superInvokeDependencies.computeIfAbsent( - from, ignore -> Sets.newIdentityHashSet()).add(target)) { + if (superInvokeDependencies + .computeIfAbsent(from.getDefinition(), ignore -> ProgramMethodSet.create()) + .add(method)) { if (liveMethods.contains(from)) { - markMethodAsTargeted(clazz, target, KeepReason.invokedViaSuperFrom(from)); + markMethodAsTargeted(method, KeepReason.invokedViaSuperFrom(from)); if (!target.accessFlags.isAbstract()) { - markVirtualMethodAsLive(clazz, target, KeepReason.invokedViaSuperFrom(from)); + markVirtualMethodAsLive(method, KeepReason.invokedViaSuperFrom(from)); } } } @@ -2629,7 +2599,7 @@ private static class SyntheticAdditions { - Map<DexType, Pair<DexProgramClass, DexEncodedMethod>> syntheticInstantiations = + Map<DexType, Pair<DexProgramClass, ProgramMethod>> syntheticInstantiations = new IdentityHashMap<>(); Map<DexMethod, ProgramMethod> liveMethods = new IdentityHashMap<>(); @@ -2649,7 +2619,7 @@ } void addInstantiatedClass( - DexProgramClass clazz, DexEncodedMethod context, boolean isMainDexClass) { + DexProgramClass clazz, ProgramMethod context, boolean isMainDexClass) { assert !syntheticInstantiations.containsKey(clazz.type); syntheticInstantiations.put(clazz.type, new Pair<>(clazz, context)); if (isMainDexClass) { @@ -2675,7 +2645,7 @@ void amendApplication(Builder appBuilder) { assert !isEmpty(); - for (Pair<DexProgramClass, DexEncodedMethod> clazzAndContext : + for (Pair<DexProgramClass, ProgramMethod> clazzAndContext : syntheticInstantiations.values()) { appBuilder.addProgramClass(clazzAndContext.getFirst()); } @@ -2690,7 +2660,7 @@ KeepReasonWitness fakeReason = enqueuer.graphReporter.fakeReportShouldNotBeUsed(); enqueuer.pinnedItems.addAll(pinnedMethods); - for (Pair<DexProgramClass, DexEncodedMethod> clazzAndContext : + for (Pair<DexProgramClass, ProgramMethod> clazzAndContext : syntheticInstantiations.values()) { enqueuer.workList.enqueueMarkInstantiatedAction( clazzAndContext.getFirst(), @@ -2700,10 +2670,8 @@ } for (ProgramMethod liveMethod : liveMethods.values()) { assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition()); - DexProgramClass holder = liveMethod.getHolder(); - DexEncodedMethod method = liveMethod.getDefinition(); - enqueuer.markMethodAsTargeted(holder, method, fakeReason); - enqueuer.enqueueMarkMethodLiveAction(holder, method, fakeReason); + enqueuer.markMethodAsTargeted(liveMethod, fakeReason); + enqueuer.enqueueMarkMethodLiveAction(liveMethod, fakeReason); } } } @@ -2752,17 +2720,18 @@ assert classesWithSerializableLambdas.isEmpty(); return; } - for (Pair<LambdaClass, DexEncodedMethod> lambdaClassAndContext : lambdaClasses.values()) { + for (Pair<LambdaClass, ProgramMethod> lambdaClassAndContext : lambdaClasses.values()) { // Add all desugared classes to the application, main-dex list, and mark them instantiated. LambdaClass lambdaClass = lambdaClassAndContext.getFirst(); - DexEncodedMethod context = lambdaClassAndContext.getSecond(); + ProgramMethod context = lambdaClassAndContext.getSecond(); DexProgramClass programClass = lambdaClass.getOrCreateLambdaClass(); additions.addInstantiatedClass(programClass, context, lambdaClass.addToMainDexList.get()); // Mark the instance constructor targeted and live. DexEncodedMethod constructor = programClass.lookupDirectMethod(lambdaClass.constructor); KeepReason reason = KeepReason.instantiatedIn(context); - markMethodAsTargeted(programClass, constructor, reason); - markDirectStaticOrConstructorMethodAsLive(programClass, constructor, reason); + ProgramMethod method = new ProgramMethod(programClass, constructor); + markMethodAsTargeted(method, reason); + markDirectStaticOrConstructorMethodAsLive(method, reason); } // Rewrite all of the invoke-dynamic instructions to lambda class instantiations. @@ -2915,14 +2884,9 @@ if (!liveMethods.contains(synthesizedClass.lookupMethod(method))) { return; } - DexEncodedMethod accessor = lambda.target.ensureAccessibilityIfNeeded(false); - if (accessor == null) { - return; - } - DexProgramClass accessorClass = getProgramClassOrNull(accessor.holder()); - assert accessorClass != null; - if (accessorClass != null) { - liveMethods.add(accessorClass, accessor, graphReporter.fakeReportShouldNotBeUsed()); + ProgramMethod accessor = lambda.target.ensureAccessibilityIfNeeded(false); + if (accessor != null) { + liveMethods.add(accessor, graphReporter.fakeReportShouldNotBeUsed()); } }); } @@ -2995,11 +2959,8 @@ } // Generate first the callbacks since they may require extra wrappers. - List<DexEncodedMethod> callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods(); - for (DexEncodedMethod callback : callbacks) { - DexProgramClass clazz = getProgramClassOrNull(callback.holder()); - additions.addLiveMethod(new ProgramMethod(clazz, callback)); - } + ProgramMethodSet callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods(); + callbacks.forEach(additions::addLiveMethod); // Generate the wrappers. List<DexProgramClass> wrappers = desugaredLibraryWrapperAnalysis.generateWrappers(); @@ -3261,8 +3222,7 @@ singleTargetHolder.isInterface(), null, graphReporter.fakeReportShouldNotBeUsed()); - enqueueMarkMethodLiveAction( - singleTargetHolder, singleTargetMethod, graphReporter.fakeReportShouldNotBeUsed()); + enqueueMarkMethodLiveAction(singleTarget, graphReporter.fakeReportShouldNotBeUsed()); } } action.getAction().accept(builder); @@ -3286,36 +3246,37 @@ } // Package protected due to entry point from worklist. - void markMethodAsKept(DexProgramClass holder, DexEncodedMethod target, KeepReason reason) { - DexMethod method = target.method; - if (target.isVirtualMethod()) { + void markMethodAsKept(ProgramMethod target, KeepReason reason) { + DexEncodedMethod definition = target.getDefinition(); + DexProgramClass holder = target.getHolder(); + DexMethod reference = target.getReference(); + if (definition.isVirtualMethod()) { // A virtual method. Mark it as reachable so that subclasses, if instantiated, keep // their overrides. However, we don't mark it live, as a keep rule might not imply that // the corresponding class is live. - markVirtualMethodAsReachable(method, holder.isInterface(), null, reason); + markVirtualMethodAsReachable(reference, holder.isInterface(), null, reason); if (holder.isInterface()) { // Reachability for default methods is based on live subtypes in general. For keep rules, // we need special handling as we essentially might have live subtypes that are outside of // the current compilation unit. Keep either the default-method or its implementation // method. // TODO(b/120959039): Codify the kept-graph expectations for these cases in tests. - if (target.isNonAbstractVirtualMethod()) { - markVirtualMethodAsLive(holder, target, reason); + if (definition.isNonAbstractVirtualMethod()) { + markVirtualMethodAsLive(target, reason); } else { - DexEncodedMethod implementation = target.getDefaultInterfaceMethodImplementation(); + DexEncodedMethod implementation = definition.getDefaultInterfaceMethodImplementation(); if (implementation != null) { DexProgramClass companion = getProgramClassOrNull(implementation.holder()); markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion)); markVirtualMethodAsLive( - companion, - implementation, - graphReporter.reportCompanionMethod(target, implementation)); + new ProgramMethod(companion, implementation), + graphReporter.reportCompanionMethod(definition, implementation)); } } } } else { - markMethodAsTargeted(holder, target, reason); - markDirectStaticOrConstructorMethodAsLive(holder, target, reason); + markMethodAsTargeted(target, reason); + markDirectStaticOrConstructorMethodAsLive(target, reason); } } @@ -3396,61 +3357,52 @@ } // Package protected due to entry point from worklist. - void markMethodAsLive(DexEncodedMethod method, KeepReason reason) { - assert liveMethods.contains(method); + void markMethodAsLive(ProgramMethod method, KeepReason reason) { + DexProgramClass holder = method.getHolder(); + DexEncodedMethod definition = method.getDefinition(); - DexProgramClass clazz = getProgramClassOrNull(method.holder()); - if (clazz == null) { - return; + assert liveMethods.contains(definition); + + if (definition.isStatic()) { + markDirectAndIndirectClassInitializersAsLive(method.getHolder()); } - if (method.isStatic()) { - markDirectAndIndirectClassInitializersAsLive(clazz); - } - - Set<DexEncodedMethod> superCallTargets = superInvokeDependencies.get(method); + ProgramMethodSet superCallTargets = superInvokeDependencies.get(method.getDefinition()); if (superCallTargets != null) { - for (DexEncodedMethod superCallTarget : superCallTargets) { + for (ProgramMethod superCallTarget : superCallTargets) { if (Log.ENABLED) { - Log.verbose(getClass(), "Found super invoke constraint on `%s`.", superCallTarget.method); + Log.verbose(getClass(), "Found super invoke constraint on `%s`.", superCallTarget); } - DexProgramClass targetClass = getProgramClassOrNull(superCallTarget.holder()); - assert targetClass != null; - if (targetClass != null) { - markMethodAsTargeted( - targetClass, superCallTarget, KeepReason.invokedViaSuperFrom(method)); - markVirtualMethodAsLive( - targetClass, superCallTarget, KeepReason.invokedViaSuperFrom(method)); - } + markMethodAsTargeted(superCallTarget, KeepReason.invokedViaSuperFrom(method)); + markVirtualMethodAsLive(superCallTarget, KeepReason.invokedViaSuperFrom(method)); } } markParameterAndReturnTypesAsLive(method); - processAnnotations(clazz, method); - method.parameterAnnotationsList.forEachAnnotation( - annotation -> processAnnotation(clazz, method, annotation)); - method.registerCodeReferences(useRegistryFactory.create(appView, clazz, method, this)); + processAnnotations(holder, definition); + definition.parameterAnnotationsList.forEachAnnotation( + annotation -> processAnnotation(holder, definition, annotation)); + method.registerCodeReferences(useRegistryFactory.create(appView, method, this)); // Add all dependent members to the workqueue. - enqueueRootItems(rootSet.getDependentItems(method)); + enqueueRootItems(rootSet.getDependentItems(definition)); // Notify analyses. analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method)); } - private void markReferencedTypesAsLive(DexEncodedMethod method) { + private void markReferencedTypesAsLive(ProgramMethod method) { markTypeAsLive( - method.holder(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method)); + method.getHolderType(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method)); markParameterAndReturnTypesAsLive(method); } - - private void markParameterAndReturnTypesAsLive(DexEncodedMethod method) { - for (DexType parameterType : method.method.proto.parameters.values) { + private void markParameterAndReturnTypesAsLive(ProgramMethod method) { + for (DexType parameterType : method.getDefinition().parameters().values) { markTypeAsLive( parameterType, clazz -> graphReporter.reportClassReferencedFrom(clazz, method)); } markTypeAsLive( - method.method.proto.returnType, + method.getDefinition().returnType(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method)); } @@ -3470,22 +3422,20 @@ } else { workList.enqueueMarkInstantiatedAction(clazz, null, InstantiationReason.KEEP_RULE, witness); if (clazz.hasDefaultInitializer()) { - DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer(); + ProgramMethod defaultInitializer = clazz.getProgramDefaultInitializer(); workList.enqueueMarkReachableDirectAction( - defaultInitializer.method, - graphReporter.reportCompatKeepDefaultInitializer(clazz, defaultInitializer)); + defaultInitializer.getReference(), + graphReporter.reportCompatKeepDefaultInitializer(defaultInitializer)); } } } - private void markMethodAsLiveWithCompatRule(DexProgramClass clazz, DexEncodedMethod method) { - enqueueMarkMethodLiveAction(clazz, method, graphReporter.reportCompatKeepMethod(clazz, method)); + private void markMethodAsLiveWithCompatRule(ProgramMethod method) { + enqueueMarkMethodLiveAction(method, graphReporter.reportCompatKeepMethod(method)); } - private void handleReflectiveBehavior(DexEncodedMethod method) { - DexType originHolder = method.holder(); - Origin origin = appInfo.originFor(originHolder); - IRCode code = method.buildIR(appView, origin); + private void handleReflectiveBehavior(ProgramMethod method) { + IRCode code = method.buildIR(appView); InstructionIterator iterator = code.instructionIterator(); while (iterator.hasNext()) { Instruction instruction = iterator.next(); @@ -3493,7 +3443,7 @@ } } - private void handleReflectiveBehavior(DexEncodedMethod method, Instruction instruction) { + private void handleReflectiveBehavior(ProgramMethod method, Instruction instruction) { if (!instruction.isInvokeMethod()) { return; } @@ -3542,10 +3492,10 @@ workList.enqueueMarkInstantiatedAction( clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method)); if (clazz.hasDefaultInitializer()) { - DexEncodedMethod initializer = clazz.getDefaultInitializer(); + ProgramMethod initializer = clazz.getProgramDefaultInitializer(); KeepReason reason = KeepReason.reflectiveUseIn(method); - markMethodAsTargeted(clazz, initializer, reason); - markDirectStaticOrConstructorMethodAsLive(clazz, initializer, reason); + markMethodAsTargeted(initializer, reason); + markDirectStaticOrConstructorMethodAsLive(initializer, reason); } } } else if (identifierItem.isDexField()) { @@ -3575,27 +3525,28 @@ } } else { assert identifierItem.isDexMethod(); - DexMethod targetedMethod = identifierItem.asDexMethod(); - DexProgramClass clazz = getProgramClassOrNull(targetedMethod.holder); + DexMethod targetedMethodReference = identifierItem.asDexMethod(); + DexProgramClass clazz = getProgramClassOrNull(targetedMethodReference.holder); if (clazz == null) { return; } - DexEncodedMethod encodedMethod = appView.definitionFor(targetedMethod); - if (encodedMethod == null) { + DexEncodedMethod targetedMethodDefinition = clazz.lookupMethod(targetedMethodReference); + if (targetedMethodDefinition == null) { return; } + ProgramMethod targetedMethod = new ProgramMethod(clazz, targetedMethodDefinition); KeepReason reason = KeepReason.reflectiveUseIn(method); - if (encodedMethod.accessFlags.isStatic() || encodedMethod.accessFlags.isConstructor()) { - markMethodAsTargeted(clazz, encodedMethod, reason); - markDirectStaticOrConstructorMethodAsLive(clazz, encodedMethod, reason); + if (targetedMethodDefinition.isStatic() || targetedMethodDefinition.isInstanceInitializer()) { + markMethodAsTargeted(targetedMethod, reason); + markDirectStaticOrConstructorMethodAsLive(targetedMethod, reason); } else { - markVirtualMethodAsLive(clazz, encodedMethod, reason); + markVirtualMethodAsLive(targetedMethod, reason); } } } /** Handles reflective uses of {@link Class#newInstance()}. */ - private void handleJavaLangClassNewInstance(DexEncodedMethod method, InvokeMethod invoke) { + private void handleJavaLangClassNewInstance(ProgramMethod method, InvokeMethod invoke) { if (!invoke.isInvokeVirtual()) { assert false; return; @@ -3614,18 +3565,18 @@ if (clazz == null) { return; } - DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer(); + ProgramMethod defaultInitializer = clazz.getProgramDefaultInitializer(); if (defaultInitializer != null) { KeepReason reason = KeepReason.reflectiveUseIn(method); markClassAsInstantiatedWithReason(clazz, reason); - markMethodAsTargeted(clazz, defaultInitializer, reason); - markDirectStaticOrConstructorMethodAsLive(clazz, defaultInitializer, reason); + markMethodAsTargeted(defaultInitializer, reason); + markDirectStaticOrConstructorMethodAsLive(defaultInitializer, reason); } } /** Handles reflective uses of {@link java.lang.reflect.Constructor#newInstance(Object...)}. */ private void handleJavaLangReflectConstructorNewInstance( - DexEncodedMethod method, InvokeMethod invoke) { + ProgramMethod method, InvokeMethod invoke) { if (!invoke.isInvokeVirtual()) { assert false; return; @@ -3669,11 +3620,11 @@ return; } - DexEncodedMethod initializer = null; + ProgramMethod initializer = null; int parametersSize = parametersSizeValue.definition.asConstNumber().getIntValue(); if (parametersSize == 0) { - initializer = clazz.getDefaultInitializer(); + initializer = clazz.getProgramDefaultInitializer(); } else { DexType[] parameterTypes = new DexType[parametersSize]; int missingIndices = parametersSize; @@ -3711,15 +3662,15 @@ } if (missingIndices == 0) { - initializer = clazz.getInitializer(parameterTypes); + initializer = clazz.getProgramInitializer(parameterTypes); } } if (initializer != null) { KeepReason reason = KeepReason.reflectiveUseIn(method); markClassAsInstantiatedWithReason(clazz, reason); - markMethodAsTargeted(clazz, initializer, reason); - markDirectStaticOrConstructorMethodAsLive(clazz, initializer, reason); + markMethodAsTargeted(initializer, reason); + markDirectStaticOrConstructorMethodAsLive(initializer, reason); } } @@ -3728,7 +3679,7 @@ * Class[], InvocationHandler)}. */ private void handleJavaLangReflectProxyNewProxyInstance( - DexEncodedMethod method, InvokeMethod invoke) { + ProgramMethod method, InvokeMethod invoke) { if (!invoke.isInvokeStatic()) { assert false; return; @@ -3770,7 +3721,7 @@ } } - private void handleJavaLangEnumValueOf(DexEncodedMethod method, InvokeMethod invoke) { + private void handleJavaLangEnumValueOf(ProgramMethod method, InvokeMethod invoke) { // The use of java.lang.Enum.valueOf(java.lang.Class, java.lang.String) will indirectly // access the values() method of the enum class passed as the first argument. The method // SomeEnumClass.valueOf(java.lang.String) which is generated by javac for all enums will @@ -3779,13 +3730,12 @@ DexType type = invoke.inValues().get(0).definition.asConstClass().getValue(); DexProgramClass clazz = getProgramClassOrNull(type); if (clazz != null && clazz.accessFlags.isEnum()) { - DexProgramClass holder = getProgramClassOrNull(method.holder()); - markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(holder, method)); + markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(method)); } } } - private void handleServiceLoaderInvocation(DexEncodedMethod method, InvokeMethod invoke) { + private void handleServiceLoaderInvocation(ProgramMethod method, InvokeMethod invoke) { if (invoke.inValues().size() == 0) { // Should never happen. return; @@ -3804,7 +3754,7 @@ + "` is being passed to the method `" + invoke.getInvokedMethod().toSourceString() + "`, but was not found in `META-INF/services/`.", - appInfo.originFor(method.holder()))); + method.getOrigin())); } return; } @@ -3877,16 +3827,21 @@ this.register = register; } - boolean add(DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - register.accept(method, reason); - transitionUnusedInterfaceToLive(clazz); - return items.add(method); + boolean add(ProgramMethod method, KeepReason reason) { + DexEncodedMethod definition = method.getDefinition(); + register.accept(definition, reason); + transitionUnusedInterfaceToLive(method.getHolder()); + return items.add(definition); } boolean contains(DexEncodedMethod method) { return items.contains(method); } + boolean contains(ProgramMethod method) { + return contains(method.getDefinition()); + } + Set<DexEncodedMethod> getItems() { return Collections.unmodifiableSet(items); } @@ -3946,7 +3901,7 @@ if (target != null) { // There is no dispatch on annotations, so only keep what is directly referenced. if (target.field == field) { - if (!registerFieldRead(field, DexEncodedMethod.ANNOTATION_REFERENCE)) { + if (!registerFieldReadFromAnnotation(field)) { return false; } markStaticFieldAsLive(target, KeepReason.referencedInAnnotation(annotationHolder)); @@ -3983,13 +3938,16 @@ // There is no dispatch on annotations, so only keep what is directly referenced. if (target.method == method) { markDirectStaticOrConstructorMethodAsLive( - holder, target, KeepReason.referencedInAnnotation(annotationHolder)); + new ProgramMethod(holder, target), + KeepReason.referencedInAnnotation(annotationHolder)); } } else { target = holder.lookupVirtualMethod(method); // There is no dispatch on annotations, so only keep what is directly referenced. if (target != null && target.method == method) { - markMethodAsTargeted(holder, target, KeepReason.referencedInAnnotation(annotationHolder)); + markMethodAsTargeted( + new ProgramMethod(holder, target), + KeepReason.referencedInAnnotation(annotationHolder)); } } return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java index d32a130..86acd84 100644 --- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java +++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
@@ -5,15 +5,10 @@ package com.android.tools.r8.shaking; 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.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; public interface EnqueuerUseRegistryFactory { - UseRegistry create( - AppView<?> appView, - DexProgramClass currentHolder, - DexEncodedMethod currentMethod, - Enqueuer enqueuer); + UseRegistry create(AppView<?> appView, ProgramMethod currentMethod, Enqueuer enqueuer); }
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 2a46ba3..f5ffc75 100644 --- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java +++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -6,7 +6,6 @@ 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.DexMethod; import com.android.tools.r8.graph.DexProgramClass; @@ -39,9 +38,9 @@ static class MarkReachableSuperAction extends EnqueuerAction { final DexMethod target; - final DexEncodedMethod context; + final ProgramMethod context; - public MarkReachableSuperAction(DexMethod target, DexEncodedMethod context) { + public MarkReachableSuperAction(DexMethod target, ProgramMethod context) { this.target = target; this.context = context; } @@ -70,13 +69,13 @@ static class MarkInstantiatedAction extends EnqueuerAction { final DexProgramClass target; - final DexEncodedMethod context; + final ProgramMethod context; final InstantiationReason instantiationReason; final KeepReason keepReason; public MarkInstantiatedAction( DexProgramClass target, - DexEncodedMethod context, + ProgramMethod context, InstantiationReason instantiationReason, KeepReason keepReason) { this.target = target; @@ -122,35 +121,32 @@ } static class MarkMethodLiveAction extends EnqueuerAction { - final DexEncodedMethod target; + final ProgramMethod method; final KeepReason reason; - public MarkMethodLiveAction(DexEncodedMethod target, KeepReason reason) { - this.target = target; + public MarkMethodLiveAction(ProgramMethod method, KeepReason reason) { + this.method = method; this.reason = reason; } @Override public void run(Enqueuer enqueuer) { - enqueuer.markMethodAsLive(target, reason); + enqueuer.markMethodAsLive(method, reason); } } static class MarkMethodKeptAction extends EnqueuerAction { - final DexProgramClass holder; - final DexEncodedMethod target; + final ProgramMethod target; final KeepReason reason; - public MarkMethodKeptAction( - DexProgramClass holder, DexEncodedMethod target, KeepReason reason) { - this.holder = holder; + public MarkMethodKeptAction(ProgramMethod target, KeepReason reason) { this.target = target; this.reason = reason; } @Override public void run(Enqueuer enqueuer) { - enqueuer.markMethodAsKept(holder, target, reason); + enqueuer.markMethodAsKept(target, reason); } } @@ -174,34 +170,31 @@ static class TraceConstClassAction extends EnqueuerAction { final DexType type; - final DexEncodedMethod currentMethod; + final ProgramMethod context; - TraceConstClassAction(DexType type, DexEncodedMethod currentMethod) { + TraceConstClassAction(DexType type, ProgramMethod context) { this.type = type; - this.currentMethod = currentMethod; + this.context = context; } @Override public void run(Enqueuer enqueuer) { - enqueuer.traceConstClass(type, currentMethod); + enqueuer.traceConstClass(type, context); } } static class TraceInvokeDirectAction extends EnqueuerAction { final DexMethod invokedMethod; - final DexProgramClass currentHolder; - final DexEncodedMethod currentMethod; + final ProgramMethod context; - TraceInvokeDirectAction( - DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) { + TraceInvokeDirectAction(DexMethod invokedMethod, ProgramMethod context) { this.invokedMethod = invokedMethod; - this.currentHolder = currentHolder; - this.currentMethod = currentMethod; + this.context = context; } @Override public void run(Enqueuer enqueuer) { - enqueuer.traceInvokeDirect(invokedMethod, new ProgramMethod(currentHolder, currentMethod)); + enqueuer.traceInvokeDirect(invokedMethod, context); } } @@ -222,16 +215,16 @@ static class TraceStaticFieldReadAction extends EnqueuerAction { final DexField field; - final DexEncodedMethod currentMethod; + final ProgramMethod context; - TraceStaticFieldReadAction(DexField field, DexEncodedMethod currentMethod) { + TraceStaticFieldReadAction(DexField field, ProgramMethod context) { this.field = field; - this.currentMethod = currentMethod; + this.context = context; } @Override public void run(Enqueuer enqueuer) { - enqueuer.traceStaticFieldRead(field, currentMethod); + enqueuer.traceStaticFieldRead(field, context); } } @@ -258,7 +251,7 @@ queue.add(new MarkReachableDirectAction(method, reason)); } - void enqueueMarkReachableSuperAction(DexMethod method, DexEncodedMethod from) { + void enqueueMarkReachableSuperAction(DexMethod method, ProgramMethod from) { queue.add(new MarkReachableSuperAction(method, from)); } @@ -272,7 +265,7 @@ // Consider updating call sites with the context information to increase precision where possible. public void enqueueMarkInstantiatedAction( DexProgramClass clazz, - DexEncodedMethod context, + ProgramMethod context, InstantiationReason instantiationReason, KeepReason keepReason) { assert !clazz.isAnnotation(); @@ -292,16 +285,12 @@ queue.add(new MarkInterfaceInstantiatedAction(clazz, reason)); } - void enqueueMarkMethodLiveAction( - DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.holder() == clazz.type; + void enqueueMarkMethodLiveAction(ProgramMethod method, KeepReason reason) { queue.add(new MarkMethodLiveAction(method, reason)); } - void enqueueMarkMethodKeptAction( - DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) { - assert method.holder() == clazz.type; - queue.add(new MarkMethodKeptAction(clazz, method, reason)); + void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason) { + queue.add(new MarkMethodKeptAction(method, reason)); } void enqueueMarkFieldKeptAction( @@ -310,23 +299,19 @@ queue.add(new MarkFieldKeptAction(holder, field, witness)); } - public void enqueueTraceConstClassAction(DexType type, DexEncodedMethod currentMethod) { - assert currentMethod.isProgramMethod(appView); - queue.add(new TraceConstClassAction(type, currentMethod)); + public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) { + queue.add(new TraceConstClassAction(type, context)); } - public void enqueueTraceInvokeDirectAction( - DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) { - assert currentMethod.holder() == currentHolder.type; - queue.add(new TraceInvokeDirectAction(invokedMethod, currentHolder, currentMethod)); + public void enqueueTraceInvokeDirectAction(DexMethod invokedMethod, ProgramMethod context) { + queue.add(new TraceInvokeDirectAction(invokedMethod, context)); } public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) { queue.add(new TraceNewInstanceAction(type, context)); } - public void enqueueTraceStaticFieldRead(DexField field, DexEncodedMethod currentMethod) { - assert currentMethod.isProgramMethod(appView); - queue.add(new TraceStaticFieldReadAction(field, currentMethod)); + public void enqueueTraceStaticFieldRead(DexField field, ProgramMethod context) { + queue.add(new TraceStaticFieldReadAction(field, context)); } }
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 4ff3594..0e39b4c 100644 --- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java +++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -169,21 +169,19 @@ return KeepReasonWitness.INSTANCE; } - public KeepReasonWitness reportCompatKeepDefaultInitializer( - DexProgramClass holder, DexEncodedMethod defaultInitializer) { - assert holder.type == defaultInitializer.holder(); - assert holder.getDefaultInitializer() == defaultInitializer; + public KeepReasonWitness reportCompatKeepDefaultInitializer(ProgramMethod defaultInitializer) { + assert defaultInitializer.getHolder().getDefaultInitializer() + == defaultInitializer.getDefinition(); if (keptGraphConsumer != null) { reportEdge( - getClassGraphNode(holder.type), - getMethodGraphNode(defaultInitializer.method), + getClassGraphNode(defaultInitializer.getHolderType()), + getMethodGraphNode(defaultInitializer.getReference()), EdgeKind.CompatibilityRule); } return KeepReasonWitness.INSTANCE; } - public KeepReasonWitness reportCompatKeepMethod(DexProgramClass holder, DexEncodedMethod method) { - assert holder.type == method.holder(); + public KeepReasonWitness reportCompatKeepMethod(ProgramMethod method) { // 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 @@ -192,10 +190,10 @@ } public KeepReasonWitness reportCompatInstantiated( - DexProgramClass instantiated, DexEncodedMethod method) { + DexProgramClass instantiated, ProgramMethod method) { if (keptGraphConsumer != null) { reportEdge( - getMethodGraphNode(method.method), + getMethodGraphNode(method.getReference()), getClassGraphNode(instantiated.type), EdgeKind.CompatibilityRule); } @@ -212,10 +210,9 @@ return KeepReasonWitness.INSTANCE; } - public KeepReasonWitness reportClassReferencedFrom( - DexProgramClass clazz, DexEncodedMethod method) { + public KeepReasonWitness reportClassReferencedFrom(DexProgramClass clazz, ProgramMethod method) { if (keptGraphConsumer != null) { - MethodGraphNode source = getMethodGraphNode(method.method); + MethodGraphNode source = getMethodGraphNode(method.getReference()); ClassGraphNode target = getClassGraphNode(clazz.type); return reportEdge(source, target, EdgeKind.ReferencedFrom); } @@ -232,13 +229,12 @@ } public KeepReasonWitness reportReachableClassInitializer( - DexProgramClass clazz, DexEncodedMethod initializer) { + DexProgramClass clazz, ProgramMethod initializer) { if (initializer != null) { - assert clazz.type == initializer.holder(); - assert initializer.isClassInitializer(); + assert initializer.getDefinition().isClassInitializer(); if (keptGraphConsumer != null) { ClassGraphNode source = getClassGraphNode(clazz.type); - MethodGraphNode target = getMethodGraphNode(initializer.method); + MethodGraphNode target = getMethodGraphNode(initializer.getReference()); return reportEdge(source, target, EdgeKind.ReachableFromLiveType); } } else {
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 62a67ee..31f7611 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -29,10 +29,18 @@ return new InstantiatedIn(method); } + static KeepReason instantiatedIn(ProgramMethod method) { + return new InstantiatedIn(method.getDefinition()); + } + public static KeepReason invokedViaSuperFrom(DexEncodedMethod from) { return new InvokedViaSuper(from); } + public static KeepReason invokedViaSuperFrom(ProgramMethod from) { + return new InvokedViaSuper(from.getDefinition()); + } + public static KeepReason reachableFromLiveType(DexType type) { return new ReachableFromLiveType(type); } @@ -45,12 +53,12 @@ return invokedFrom(context.getHolder(), context.getDefinition()); } - public static KeepReason invokedFromLambdaCreatedIn(DexEncodedMethod method) { - return new InvokedFromLambdaCreatedIn(method); + public static KeepReason invokedFromLambdaCreatedIn(ProgramMethod method) { + return new InvokedFromLambdaCreatedIn(method.getDefinition()); } - public static KeepReason fieldReferencedIn(DexEncodedMethod method) { - return new ReferencedFrom(method); + public static KeepReason fieldReferencedIn(ProgramMethod method) { + return new ReferencedFrom(method.getDefinition()); } public static KeepReason referencedInAnnotation(DexItem holder) { @@ -65,16 +73,16 @@ return false; } - public static KeepReason targetedBySuperFrom(DexEncodedMethod from) { - return new TargetedBySuper(from); + public static KeepReason targetedBySuperFrom(ProgramMethod from) { + return new TargetedBySuper(from.getDefinition()); } - public static KeepReason reflectiveUseIn(DexEncodedMethod method) { - return new ReflectiveUseFrom(method); + public static KeepReason reflectiveUseIn(ProgramMethod method) { + return new ReflectiveUseFrom(method.getDefinition()); } - public static KeepReason methodHandleReferencedIn(DexEncodedMethod method) { - return new MethodHandleReferencedFrom(method); + public static KeepReason methodHandleReferencedIn(ProgramMethod method) { + return new MethodHandleReferencedFrom(method.getDefinition()); } private abstract static class BasedOnOtherMethod extends KeepReason {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java index e686f98..205cf71 100644 --- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java +++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -4,12 +4,13 @@ package com.android.tools.r8.shaking; +import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; + import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; @@ -18,7 +19,9 @@ 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.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.utils.BooleanBox; import java.util.Set; import java.util.function.Consumer; @@ -39,30 +42,28 @@ public void run(Set<DexType> roots) { for (DexType type : roots) { - DexClass clazz = appInfo.definitionFor(type); + DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(type)); // Should only happen for library classes, which are filtered out. assert clazz != null; consumer.accept(type); // Super and interfaces are live, no need to add them. traceAnnotationsDirectDependencies(clazz.annotations()); clazz.forEachField(field -> consumer.accept(field.field.type)); - clazz.forEachMethod(method -> { - traceMethodDirectDependencies(method.method, consumer); - method.registerCodeReferences(codeDirectReferenceCollector); - }); + clazz.forEachProgramMethodMatching( + definition -> { + traceMethodDirectDependencies(definition.getReference(), consumer); + return definition.hasCode(); + }, + method -> method.registerCodeReferences(codeDirectReferenceCollector)); } } - public void runOnCode(DexEncodedMethod method) { + public void runOnCode(ProgramMethod method) { method.registerCodeReferences(codeDirectReferenceCollector); } - private static class BooleanBox { - boolean value = false; - } - public static boolean hasReferencesOutsideFromCode( - AppInfoWithClassHierarchy appInfo, DexEncodedMethod method, Set<DexType> classes) { + AppInfoWithClassHierarchy appInfo, ProgramMethod method, Set<DexType> classes) { BooleanBox result = new BooleanBox(); @@ -73,13 +74,13 @@ if (baseType.isClassType() && !classes.contains(baseType)) { DexClass cls = appInfo.definitionFor(baseType); if (cls != null && cls.isProgramClass()) { - result.value = true; + result.set(true); } } }) .runOnCode(method); - return result.value; + return result.get(); } private void traceAnnotationsDirectDependencies(DexAnnotationSet annotations) {
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 846710b..e105350 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1314,15 +1314,20 @@ public void forEachDependentInstanceConstructor( DexProgramClass clazz, AppView<?> appView, - Consumer3<DexProgramClass, DexEncodedMethod, Set<ProguardKeepRuleBase>> fn) { + Consumer3<DexProgramClass, ProgramMethod, Set<ProguardKeepRuleBase>> fn) { getDependentItems(clazz) .forEach( (reference, reasons) -> { - DexDefinition definition = appView.definitionFor(reference); - if (definition != null - && definition.isDexEncodedMethod() - && definition.asDexEncodedMethod().isInstanceInitializer()) { - fn.accept(clazz, definition.asDexEncodedMethod(), reasons); + if (reference.isDexMethod()) { + DexMethod methodReference = reference.asDexMethod(); + DexProgramClass holder = + asProgramClassOrNull(appView.definitionForHolder(methodReference)); + if (holder != null) { + ProgramMethod method = holder.lookupProgramMethod(methodReference); + if (method != null && method.getDefinition().isInstanceInitializer()) { + fn.accept(clazz, method, reasons); + } + } } }); }
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java index 5031d82..dc69de3 100644 --- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -20,6 +20,7 @@ import com.android.tools.r8.utils.MethodJavaSignatureEquivalence; import com.android.tools.r8.utils.MethodSignatureEquivalence; import com.android.tools.r8.utils.SingletonEquivalence; +import com.android.tools.r8.utils.TraversalContinuation; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.BiMap; @@ -171,8 +172,8 @@ } boolean classHasSynchronizedMethods = false; for (DexEncodedMethod method : clazz.methods()) { - assert !hasSynchronizedMethods || !method.accessFlags.isSynchronized(); - classHasSynchronizedMethods |= method.accessFlags.isSynchronized(); + assert !hasSynchronizedMethods || !method.isSynchronized(); + classHasSynchronizedMethods |= method.isSynchronized(); Wrapper<DexMethod> wrapper = methodEquivalence.wrap(method.method); methodBuckets.add(wrapper); } @@ -443,14 +444,17 @@ // Check that no methods access package-private or protected members. IllegalAccessDetector registry = new IllegalAccessDetector(appView, clazz); - for (DexEncodedMethod method : clazz.methods()) { - registry.setContext(method); - method.registerCodeReferences(registry); - if (registry.foundIllegalAccess()) { - return false; - } - } - return true; + TraversalContinuation result = + clazz.traverseProgramMethods( + method -> { + registry.setContext(method); + method.registerCodeReferences(registry); + if (registry.foundIllegalAccess()) { + return TraversalContinuation.BREAK; + } + return TraversalContinuation.CONTINUE; + }); + return result.shouldContinue(); } private void moveMembersFromSourceToTarget( @@ -465,8 +469,8 @@ // TODO(b/136457753) This check is a bit weird for protected, since it is moving access. assert targetClass.accessFlags.isAtLeastAsVisibleAs(sourceClass.accessFlags); - assert sourceClass.instanceFields().size() == 0; - assert targetClass.instanceFields().size() == 0; + assert sourceClass.instanceFields().isEmpty(); + assert targetClass.instanceFields().isEmpty(); numberOfMergedClasses++;
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 7c679e0..6f2010c 100644 --- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java +++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -291,13 +291,13 @@ // Also some other kinds of methods cannot be abstract, so keep them around. boolean allowAbstract = (!options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() - || clazz.accessFlags.isAbstract()) - && !method.accessFlags.isFinal() + || clazz.isAbstract()) + && !method.isFinal() && !method.accessFlags.isNative() && !method.accessFlags.isStrict() - && !method.accessFlags.isSynchronized() + && !method.isSynchronized() && !method.accessFlags.isPrivate() - && !method.accessFlags.isStatic() + && !method.isStatic() && !appInfo.failedResolutionTargets.contains(method.method); // Private methods and static methods can only be targeted yet non-live as the result of // an invalid invoke. They will not actually be called at runtime but we have to keep them
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 bb90d42..31d8074 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; @@ -32,6 +33,7 @@ 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.ProgramMethod; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.graph.SubtypingInfo; @@ -51,6 +53,7 @@ import com.android.tools.r8.utils.FieldSignatureEquivalence; import com.android.tools.r8.utils.MethodSignatureEquivalence; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.TraversalContinuation; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.Iterables; @@ -376,19 +379,21 @@ // * Have access to the no-arg constructor of its first non-serializable superclass return false; } - for (DexEncodedMethod method : sourceClass.directMethods()) { - // We rename constructors to private methods and mark them to be forced-inlined, so we have to - // check if we can force-inline all constructors. - if (method.isInstanceInitializer()) { - AbortReason reason = disallowInlining(method, targetClass.type); - if (reason != null) { - // Cannot guarantee that markForceInline() will work. - if (Log.ENABLED) { - reason.printLogMessageForClass(sourceClass); - } - return false; - } - } + TraversalContinuation result = + sourceClass.traverseProgramInstanceInitializers( + method -> { + AbortReason reason = disallowInlining(method, targetClass.type); + if (reason != null) { + // Cannot guarantee that markForceInline() will work. + if (Log.ENABLED) { + reason.printLogMessageForClass(sourceClass); + } + return TraversalContinuation.BREAK; + } + return TraversalContinuation.CONTINUE; + }); + if (result.shouldBreak()) { + return false; } if (sourceClass.getEnclosingMethod() != null || !sourceClass.getInnerClasses().isEmpty()) { // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes. @@ -472,15 +477,13 @@ return true; } - private boolean mergeMayLeadToIllegalAccesses(DexClass source, DexClass target) { + private boolean mergeMayLeadToIllegalAccesses(DexProgramClass source, DexProgramClass target) { if (source.type.isSamePackage(target.type)) { // When merging two classes from the same package, we only need to make sure that [source] // does not get less visible, since that could make a valid access to [source] from another // package illegal after [source] has been merged into [target]. - int accessLevel = - source.accessFlags.isPrivate() ? 0 : (source.accessFlags.isPublic() ? 2 : 1); - int otherAccessLevel = - target.accessFlags.isPrivate() ? 0 : (target.accessFlags.isPublic() ? 2 : 1); + int accessLevel = source.isPrivate() ? 0 : (source.isPublic() ? 2 : 1); + int otherAccessLevel = target.isPrivate() ? 0 : (target.isPublic() ? 2 : 1); return accessLevel > otherAccessLevel; } @@ -489,22 +492,22 @@ // [source] are either private or public. // // (Deliberately not checking all accesses to [source] since that would be expensive.) - if (!target.accessFlags.isPublic()) { + if (!target.isPublic()) { return true; } for (DexEncodedField field : source.fields()) { - if (!(field.accessFlags.isPublic() || field.accessFlags.isPrivate())) { + if (!(field.isPublic() || field.isPrivate())) { return true; } } for (DexEncodedMethod method : source.methods()) { - if (!(method.accessFlags.isPublic() || method.accessFlags.isPrivate())) { + if (!(method.isPublic() || method.isPrivate())) { return true; } // Check if the target is overriding and narrowing the access. - if (method.accessFlags.isPublic()) { + if (method.isPublic()) { DexEncodedMethod targetOverride = target.lookupVirtualMethod(method.method); - if (targetOverride != null && !targetOverride.accessFlags.isPublic()) { + if (targetOverride != null && !targetOverride.isPublic()) { return true; } } @@ -513,15 +516,17 @@ // [source] will continue to work. This is guaranteed if the methods of [source] do not access // any private or protected classes or members from the current package of [source]. IllegalAccessDetector registry = new IllegalAccessDetector(appView, source); - for (DexEncodedMethod method : source.methods()) { - registry.setContext(method); - method.registerCodeReferences(registry); - if (registry.foundIllegalAccess()) { - return true; - } - } - - return false; + TraversalContinuation result = + source.traverseProgramMethods( + method -> { + registry.setContext(method); + method.registerCodeReferences(registry); + if (registry.foundIllegalAccess()) { + return TraversalContinuation.BREAK; + } + return TraversalContinuation.CONTINUE; + }); + return result.shouldBreak(); } private Collection<DexMethod> getInvokes() { @@ -1650,15 +1655,16 @@ } } - private AbortReason disallowInlining(DexEncodedMethod method, DexType invocationContext) { + private AbortReason disallowInlining(ProgramMethod method, DexType invocationContext) { if (appView.options().enableInlining) { - if (method.getCode().isCfCode()) { - CfCode code = method.getCode().asCfCode(); + Code code = method.getDefinition().getCode(); + if (code.isCfCode()) { + CfCode cfCode = code.asCfCode(); ConstraintWithTarget constraint = - code.computeInliningConstraint( + cfCode.computeInliningConstraint( method, appView, - new SingleTypeMapperGraphLense(method.holder(), invocationContext), + new SingleTypeMapperGraphLense(method.getHolderType(), invocationContext), invocationContext); if (constraint == ConstraintWithTarget.NEVER) { return AbortReason.UNSAFE_INLINING; @@ -1761,8 +1767,8 @@ // as [source]. public static class IllegalAccessDetector extends UseRegistry { - private boolean foundIllegalAccess = false; - private DexMethod context = null; + private boolean foundIllegalAccess; + private ProgramMethod context; private final AppView<?> appView; private final DexClass source; @@ -1777,8 +1783,8 @@ return foundIllegalAccess; } - public void setContext(DexEncodedMethod context) { - this.context = context.method; + public void setContext(ProgramMethod context) { + this.context = context; } private boolean checkFieldReference(DexField field) { @@ -1840,7 +1846,7 @@ public boolean registerInvokeVirtual(DexMethod method) { assert context != null; GraphLenseLookupResult lookup = - appView.graphLense().lookupMethod(method, context, Type.VIRTUAL); + appView.graphLense().lookupMethod(method, context.getReference(), Type.VIRTUAL); return checkMethodReference(lookup.getMethod()); } @@ -1848,7 +1854,7 @@ public boolean registerInvokeDirect(DexMethod method) { assert context != null; GraphLenseLookupResult lookup = - appView.graphLense().lookupMethod(method, context, Type.DIRECT); + appView.graphLense().lookupMethod(method, context.getReference(), Type.DIRECT); return checkMethodReference(lookup.getMethod()); } @@ -1856,7 +1862,7 @@ public boolean registerInvokeStatic(DexMethod method) { assert context != null; GraphLenseLookupResult lookup = - appView.graphLense().lookupMethod(method, context, Type.STATIC); + appView.graphLense().lookupMethod(method, context.getReference(), Type.STATIC); return checkMethodReference(lookup.getMethod()); } @@ -1864,7 +1870,7 @@ public boolean registerInvokeInterface(DexMethod method) { assert context != null; GraphLenseLookupResult lookup = - appView.graphLense().lookupMethod(method, context, Type.INTERFACE); + appView.graphLense().lookupMethod(method, context.getReference(), Type.INTERFACE); return checkMethodReference(lookup.getMethod()); } @@ -1872,7 +1878,7 @@ public boolean registerInvokeSuper(DexMethod method) { assert context != null; GraphLenseLookupResult lookup = - appView.graphLense().lookupMethod(method, context, Type.SUPER); + appView.graphLense().lookupMethod(method, context.getReference(), Type.SUPER); return checkMethodReference(lookup.getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanBox.java b/src/main/java/com/android/tools/r8/utils/BooleanBox.java new file mode 100644 index 0000000..d31e352 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
@@ -0,0 +1,24 @@ +// 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.utils; + +public class BooleanBox { + + private boolean value; + + public BooleanBox() {} + + public BooleanBox(boolean initialValue) { + set(initialValue); + } + + public boolean get() { + return value; + } + + public void set(boolean value) { + this.value = value; + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/IntBox.java b/src/main/java/com/android/tools/r8/utils/IntBox.java new file mode 100644 index 0000000..9f4ac85 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/IntBox.java
@@ -0,0 +1,28 @@ +// 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.utils; + +public class IntBox { + + private int value; + + public IntBox() {} + + public IntBox(int initialValue) { + set(initialValue); + } + + public int get() { + return value; + } + + public int getAndIncrement() { + return value++; + } + + public void set(int value) { + this.value = value; + } +}
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 7e34093..62aa29a 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -34,6 +34,7 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.inspector.internal.InspectorImpl; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration; @@ -47,6 +48,7 @@ import com.android.tools.r8.shaking.ProguardConfigurationRule; import com.android.tools.r8.utils.IROrdering.IdentityIROrdering; import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; @@ -60,7 +62,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Deque; @@ -254,7 +255,7 @@ public boolean enableKotlinMetadataRewritingForRenamedClasses = true; public boolean encodeChecksums = false; public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true; - public boolean enableCfInterfaceMethodDesugaring = false; + public boolean cfToCfDesugar = false; public int callGraphLikelySpuriousCallEdgeThreshold = 50; @@ -375,7 +376,8 @@ } public boolean shouldKeepStackMapTable() { - return isDesugaredLibraryCompilation() + assert cfToCfDesugar || isRelocatorCompilation() || getProguardConfiguration() != null; + return cfToCfDesugar || isRelocatorCompilation() || getProguardConfiguration().getKeepAttributes().stackMapTable; } @@ -645,8 +647,8 @@ private final Map<Origin, List<InvalidParameterAnnotationInfo>> warningInvalidParameterAnnotations = new HashMap<>(); - private final Map<Origin, List<Pair<DexEncodedMethod, String>>> warningInvalidDebugInfo - = new HashMap<>(); + private final Map<Origin, List<Pair<ProgramMethod, String>>> warningInvalidDebugInfo = + new HashMap<>(); // Don't read code from dex files. Used to extract non-code information from vdex files where // the code contains unsupported byte codes. @@ -900,7 +902,7 @@ } public void warningInvalidDebugInfo( - DexEncodedMethod method, Origin origin, InvalidDebugInfoException e) { + ProgramMethod method, Origin origin, InvalidDebugInfoException e) { if (invalidDebugInfoFatal) { throw new CompilationError("Fatal warning: Invalid debug info", e); } @@ -962,7 +964,7 @@ } if (warningInvalidDebugInfo.size() > 0) { int count = 0; - for (List<Pair<DexEncodedMethod, String>> methods : warningInvalidDebugInfo.values()) { + for (List<Pair<ProgramMethod, String>> methods : warningInvalidDebugInfo.values()) { count += methods.size(); } reporter.info( @@ -972,7 +974,7 @@ + (count == 1 ? " method." : " methods."))); for (Origin origin : new TreeSet<>(warningInvalidDebugInfo.keySet())) { StringBuilder builder = new StringBuilder("Methods with invalid locals information:"); - for (Pair<DexEncodedMethod, String> method : warningInvalidDebugInfo.get(origin)) { + for (Pair<ProgramMethod, String> method : warningInvalidDebugInfo.get(origin)) { builder.append("\n ").append(method.getFirst().toSourceString()); builder.append("\n ").append(method.getSecond()); } @@ -1089,7 +1091,7 @@ public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null; - public Consumer<Deque<Collection<DexEncodedMethod>>> waveModifier = waves -> {}; + public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {}; /** * If this flag is enabled, we will also compute the set of possible targets for invoke- @@ -1183,7 +1185,7 @@ public int numberOfProguardIfRuleMemberEvaluations = 0; } - public Consumer<DexEncodedMethod> callSiteOptimizationInfoInspector = null; + public Consumer<ProgramMethod> callSiteOptimizationInfoInspector = null; } @VisibleForTesting @@ -1276,7 +1278,7 @@ } return desugarState == DesugarState.ON && interfaceMethodDesugaring == OffOrAuto.Auto - && (!canUseDefaultAndStaticInterfaceMethods() || enableCfInterfaceMethodDesugaring); + && (!canUseDefaultAndStaticInterfaceMethods() || cfToCfDesugar); } public boolean isStringSwitchConversionEnabled() {
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java index f570956..f941485 100644 --- a/src/main/java/com/android/tools/r8/utils/ListUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -16,6 +16,15 @@ return list.get(0); } + public static <T> int firstIndexMatching(List<T> list, Predicate<T> tester) { + for (int i = 0; i < list.size(); i++) { + if (tester.test(list.get(i))) { + return i; + } + } + return -1; + } + public static <T> T last(List<T> list) { return list.get(list.size() - 1); } @@ -37,6 +46,15 @@ return result; } + public static <T> boolean removeFirstMatch(List<T> list, Predicate<T> element) { + int index = firstIndexMatching(list, element); + if (index >= 0) { + list.remove(index); + return true; + } + return false; + } + public static <T extends Comparable<T>> boolean verifyListIsOrdered(List<T> list) { for (int i = list.size() - 1; i > 0; i--) { if (list.get(i).compareTo(list.get(i - 1)) < 0) {
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java index e6076ca..1349732 100644 --- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -24,6 +24,13 @@ return processItemsWithResults(items::forEach, consumer, executorService); } + public static <T, U, R, E extends Exception> Collection<R> processItemsWithResults( + Map<T, U> items, ThrowingBiFunction<T, U, R, E> consumer, ExecutorService executorService) + throws ExecutionException { + return processItemsWithResults( + items.entrySet(), arg -> consumer.apply(arg.getKey(), arg.getValue()), executorService); + } + public static <T, R, E extends Exception> Collection<R> processItemsWithResults( ForEachable<T> items, ThrowingFunction<T, R, E> consumer, ExecutorService executorService) throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingBiFunction.java b/src/main/java/com/android/tools/r8/utils/ThrowingBiFunction.java new file mode 100644 index 0000000..7cfccf0 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/ThrowingBiFunction.java
@@ -0,0 +1,16 @@ +// 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.utils; + +/** + * Similar to a {@link java.util.function.BiFunction} but throws a single {@link Throwable}. + * + * @param <S> the type of the first input + * @param <T> the type of the second input + * @param <E> the type of the {@link Throwable} + */ +@FunctionalInterface +public interface ThrowingBiFunction<S, T, R, E extends Throwable> { + R apply(S s, T t) throws E; +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java new file mode 100644 index 0000000..b76c14c --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -0,0 +1,42 @@ +// 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.utils.collections; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.google.common.collect.Sets; +import java.util.Set; + +public class LongLivedProgramMethodSetBuilder { + + private Set<DexMethod> methods = Sets.newIdentityHashSet(); + + public LongLivedProgramMethodSetBuilder() {} + + public void add(ProgramMethod method) { + methods.add(method.getReference()); + } + + public void addAll(Iterable<ProgramMethod> methods) { + methods.forEach(this::add); + } + + public ProgramMethodSet build(AppView<AppInfoWithLiveness> appView) { + ProgramMethodSet result = ProgramMethodSet.create(methods.size()); + for (DexMethod oldMethod : methods) { + DexMethod method = appView.graphLense().getRenamedMethodSignature(oldMethod); + DexProgramClass holder = appView.definitionForHolder(method).asProgramClass(); + result.createAndAdd(holder, holder.lookupMethod(method)); + } + return result; + } + + public boolean isEmpty() { + return methods.isEmpty(); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java new file mode 100644 index 0000000..1795899 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -0,0 +1,119 @@ +// 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.utils.collections; + +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +public class ProgramMethodSet implements Iterable<ProgramMethod> { + + private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap.of()); + + private Map<DexMethod, ProgramMethod> backing; + + private ProgramMethodSet(Map<DexMethod, ProgramMethod> backing) { + this.backing = backing; + } + + public static ProgramMethodSet create() { + return new ProgramMethodSet(new IdentityHashMap<>()); + } + + public static ProgramMethodSet create(int capacity) { + return new ProgramMethodSet(new IdentityHashMap<>(capacity)); + } + + public static ProgramMethodSet create(ProgramMethod element) { + ProgramMethodSet result = create(); + result.add(element); + return result; + } + + public static ProgramMethodSet createConcurrent() { + return new ProgramMethodSet(new ConcurrentHashMap<>()); + } + + public static ProgramMethodSet createLinked() { + return new ProgramMethodSet(new LinkedHashMap<>()); + } + + public static ProgramMethodSet empty() { + return EMPTY; + } + + public boolean add(ProgramMethod method) { + ProgramMethod existing = backing.put(method.getReference(), method); + assert existing == null || existing.isStructurallyEqualTo(method); + return existing == null; + } + + public void addAll(Iterable<ProgramMethod> methods) { + methods.forEach(this::add); + } + + public void addAll(ProgramMethodSet methods) { + backing.putAll(methods.backing); + } + + public boolean createAndAdd(DexProgramClass clazz, DexEncodedMethod definition) { + return add(new ProgramMethod(clazz, definition)); + } + + public boolean contains(DexEncodedMethod method) { + return backing.containsKey(method.getReference()); + } + + public boolean contains(ProgramMethod method) { + return backing.containsKey(method.getReference()); + } + + public void clear() { + backing.clear(); + } + + public boolean isEmpty() { + return backing.isEmpty(); + } + + @Override + public Iterator<ProgramMethod> iterator() { + return backing.values().iterator(); + } + + public boolean remove(DexMethod method) { + ProgramMethod existing = backing.remove(method); + return existing != null; + } + + public boolean remove(DexEncodedMethod method) { + return remove(method.getReference()); + } + + public int size() { + return backing.size(); + } + + public Stream<ProgramMethod> stream() { + return backing.values().stream(); + } + + public Set<DexEncodedMethod> toDefinitionSet() { + assert backing instanceof IdentityHashMap; + Set<DexEncodedMethod> definitions = Sets.newIdentityHashSet(); + forEach(method -> definitions.add(method.getDefinition())); + return definitions; + } +}
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java index d6732bd..edfb00c 100644 --- a/src/test/java/com/android/tools/r8/D8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -19,12 +19,12 @@ extends TestCompilerBuilder< D8Command, Builder, D8TestCompileResult, D8TestRunResult, D8TestBuilder> { - private D8TestBuilder(TestState state, Builder builder) { - super(state, builder, Backend.DEX); + private D8TestBuilder(TestState state, Builder builder, Backend backend) { + super(state, builder, backend); } - public static D8TestBuilder create(TestState state) { - return new D8TestBuilder(state, D8Command.builder(state.getDiagnosticsHandler())); + public static D8TestBuilder create(TestState state, Backend backend) { + return new D8TestBuilder(state, D8Command.builder(state.getDiagnosticsHandler()), backend); } @Override
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index 7c3ddff..7938666 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -134,8 +134,12 @@ return ExternalR8TestBuilder.create(new TestState(temp), backend, runtime); } + public static D8TestBuilder testForD8(TemporaryFolder temp, Backend backend) { + return D8TestBuilder.create(new TestState(temp), backend); + } + public static D8TestBuilder testForD8(TemporaryFolder temp) { - return D8TestBuilder.create(new TestState(temp)); + return D8TestBuilder.create(new TestState(temp), Backend.DEX); } public static DXTestBuilder testForDX(TemporaryFolder temp) { @@ -171,7 +175,11 @@ } public D8TestBuilder testForD8() { - return testForD8(temp); + return testForD8(temp, Backend.DEX); + } + + public D8TestBuilder testForD8(Backend backend) { + return testForD8(temp, backend); } public DXTestBuilder testForDX() {
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java index 44f046b..eba971d 100644 --- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java +++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -50,7 +50,11 @@ @Override public void warning(Diagnostic warning) { - warnings.add(warning); + // When testing D8 with class file output this warning is always emitted. Discard this, as + // for tests this is not relevant. + if (!warning.equals("Compiling to Java class files with D8 is not officially supported")) { + warnings.add(warning); + } } @Override @@ -58,14 +62,17 @@ errors.add(error); } + @Override public List<Diagnostic> getInfos() { return infos; } + @Override public List<Diagnostic> getWarnings() { return warnings; } + @Override public List<Diagnostic> getErrors() { return errors; } @@ -80,6 +87,7 @@ messages.size()); } + @Override public TestDiagnosticMessages assertNoMessages() { assertEmpty("info", getInfos()); assertEmpty("warning", getWarnings()); @@ -87,6 +95,7 @@ return this; } + @Override public TestDiagnosticMessages assertOnlyInfos() { assertNotEquals(0, getInfos().size()); assertEmpty("warning", getWarnings()); @@ -94,6 +103,7 @@ return this; } + @Override public TestDiagnosticMessages assertOnlyWarnings() { assertEmpty("info", getInfos()); assertNotEquals(0, getWarnings().size()); @@ -101,6 +111,7 @@ return this; } + @Override public TestDiagnosticMessages assertOnlyErrors() { assertEmpty("info", getInfos()); assertEmpty("warning", getWarnings()); @@ -108,16 +119,19 @@ return this; } + @Override public TestDiagnosticMessages assertInfosCount(int count) { assertEquals(count, getInfos().size()); return this; } + @Override public TestDiagnosticMessages assertWarningsCount(int count) { assertEquals(count, getWarnings().size()); return this; } + @Override public TestDiagnosticMessages assertErrorsCount(int count) { assertEquals(count, getErrors().size()); return this;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 5c50407..c992015 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -171,7 +171,7 @@ public static final Path R8LIB_EXCLUDE_DEPS_JAR = Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar"); public static final Path R8LIB_EXCLUDE_DEPS_MAP = Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar.map"); - public static final Path DEPS = Paths.get(LIBS_DIR, "deps.jar"); + public static final Path DEPS = Paths.get(LIBS_DIR, "deps_all.jar"); public static final Path DESUGAR_LIB_CONVERSIONS = Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
diff --git a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java index 07a33ab..c25dc40 100644 --- a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java +++ b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
@@ -116,11 +116,7 @@ } private static int getVersion(CodeInspector inspector, Class<?> clazz) { - return inspector - .clazz(clazz) - .getDexProgramClass() - .asProgramClass() - .getInitialClassFileVersion(); + return inspector.clazz(clazz).getDexProgramClass().getInitialClassFileVersion(); } private static void checkVersion(CodeInspector inspector, Class<?> clazz, int version) {
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java index 8769dc2..7cde792 100644 --- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java +++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -120,7 +120,7 @@ internalOptions -> { if (parameters.isCfRuntime()) { internalOptions.desugarState = DesugarState.ON; - internalOptions.enableCfInterfaceMethodDesugaring = true; + internalOptions.cfToCfDesugar = true; } }); if (parameters.isDexRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java index ec742ee..e8ac332 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
@@ -4,16 +4,11 @@ package com.android.tools.r8.desugar; -import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; -import com.android.tools.r8.OutputMode; 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.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.nio.file.Path; import org.junit.Test; @@ -23,45 +18,43 @@ @RunWith(Parameterized.class) public class DesugarToClassFile extends TestBase { - private final TestParameters parameters; - @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); } + private final TestParameters parameters; + public DesugarToClassFile(TestParameters parameters) { this.parameters = parameters; } - private void checkSomething(CodeInspector inspector) { - ClassSubject classSubject = inspector.clazz(TestClass.class); - assertThat(classSubject, isPresent()); + private void checkHasCompanionClass(CodeInspector inspector) { + assertTrue( + inspector.allClasses().stream() + .anyMatch(subject -> subject.getOriginalName().endsWith("$-CC"))); } - private void checkDiagnostics(TestDiagnosticMessages messages) { - messages.assertOnlyWarnings(); - messages.assertWarningsCount(1); - assertThat( - messages.getWarnings().get(0).getDiagnosticMessage(), - containsString("not officially supported")); + private void checkHasLambdaClass(CodeInspector inspector) { + assertTrue( + inspector.allClasses().stream() + .anyMatch(subject -> subject.getOriginalName().contains("-$$Lambda$"))); } @Test public void test() throws Exception { // Use D8 to desugar with Java classfile output. Path jar = - testForD8() + testForD8(Backend.CF) .addInnerClasses(DesugarToClassFile.class) .setMinApi(parameters.getApiLevel()) - .setOutputMode(OutputMode.ClassFile) .compile() - .inspectDiagnosticMessages(this::checkDiagnostics) - .inspect(this::checkSomething) + .inspect(this::checkHasCompanionClass) + .inspect(this::checkHasLambdaClass) .writeToZip(); if (parameters.getRuntime().isCf()) { - // Run on the JVM + // Run on the JVM. testForJvm() .addProgramFiles(jar) .run(parameters.getRuntime(), TestClass.class) @@ -71,8 +64,8 @@ // Convert to DEX without desugaring. testForD8() .addProgramFiles(jar) - .setEnableDesugaring(false) .setMinApi(parameters.getApiLevel()) + .setEnableDesugaring(false) .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutputLines("Hello, world!", "I::foo"); }
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.java new file mode 100644 index 0000000..0fd267d --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileInputCfVersion.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.desugar; + +import static org.objectweb.asm.Opcodes.V1_4; +import static org.objectweb.asm.Opcodes.V1_5; +import static org.objectweb.asm.Opcodes.V1_6; +import static org.objectweb.asm.Opcodes.V1_7; +import static org.objectweb.asm.Opcodes.V1_8; +import static org.objectweb.asm.Opcodes.V9; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; +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 DesugarToClassFileInputCfVersion extends TestBase { + + @Parameters(name = "{0}, input Cf version: {1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(), + ImmutableList.of(V1_4, V1_5, V1_6, V1_7, V1_8, V9)); + } + + private final TestParameters parameters; + private final int cfVersion; + + public DesugarToClassFileInputCfVersion(TestParameters parameters, int cfVersion) { + this.parameters = parameters; + this.cfVersion = cfVersion; + } + + @Test + public void test() throws Exception { + // Use D8 to desugar with Java classfile output. + Path jar = + testForD8(Backend.CF) + .addProgramClassFileData(transformer(TestClass.class).setVersion(cfVersion).transform()) + .setMinApi(parameters.getApiLevel()) + .compile() + .writeToZip(); + + if (parameters.getRuntime().isCf()) { + // Run on the JVM given that Cf version is supported. + if (cfVersion <= parameters.getRuntime().asCf().getVm().getClassfileVersion()) { + testForJvm() + .addProgramFiles(jar) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello, world!"); + } else { + testForJvm() + .addProgramFiles(jar) + .run(parameters.getRuntime(), TestClass.class) + .assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class); + } + } else { + assert parameters.getRuntime().isDex(); + // Convert to DEX without desugaring. + testForD8() + .addProgramFiles(jar) + .setMinApi(parameters.getApiLevel()) + .setEnableDesugaring(false) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutputLines("Hello, world!"); + } + } + + static class TestClass { + + public static void main(String[] args) { + if (System.currentTimeMillis() > 0) { + System.out.println("Hello, world!"); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java new file mode 100644 index 0000000..5dac547 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.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.desugar.backports; + +import static org.hamcrest.core.StringContains.containsString; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.utils.AndroidApiLevel; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +@RunWith(Parameterized.class) +public class ApiLevelBackportsTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimesStartingFromIncluding(Version.V9_0_0).build(); + } + + public ApiLevelBackportsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void backportSucceedsOnSupportedApiLevel() throws Exception { + testForD8() + .addProgramClassFileData(Dump.mainWithMathMultiplyExactLongInt()) + .setMinApi(AndroidApiLevel.B) + .run(parameters.getRuntime(), "Test") + .assertSuccessWithOutputLines("4"); + } + + @Test + public void warningForNonPlatformBuild() throws Exception { + testForD8() + .addProgramClassFileData(Dump.mainWithMathMultiplyExactLongInt()) + .setMinApi(30) + .compile() + .assertOnlyWarnings() + .assertWarningMessageThatMatches( + containsString("An API level of 30 is not supported by this compiler")) + .run(parameters.getRuntime(), "Test") + .assertFailureWithErrorThatMatches( + containsString("java.lang.NoSuchMethodError: No static method multiplyExact(JI)J")); + } + + @Test + public void noWarningForPlatformBuild() throws Exception { + testForD8() + .addProgramClassFileData(Dump.mainWithMathMultiplyExactLongInt()) + .setMinApi(AndroidApiLevel.magicApiLevelUsedByAndroidPlatformBuild) + .run(parameters.getRuntime(), "Test") + .assertFailureWithErrorThatMatches( + containsString("java.lang.NoSuchMethodError: No static method multiplyExact(JI)J")); + } + + static class Dump implements Opcodes { + + // Code for: + // + // class Test { + // public static void main(String[] args) { + // // Call Math.multiplyExact(long, int), which is not in Android Q. + // System.out.println(Math.multiplyExact(2L, 2)); + // } + // } + // + static byte[] mainWithMathMultiplyExactLongInt() { + + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + + classWriter.visit(V1_8, ACC_SUPER, "Test", null, "java/lang/Object", null); + + classWriter.visitSource("Test.java", null); + + { + methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(1, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + methodVisitor.visitInsn(RETURN); + methodVisitor.visitMaxs(1, 1); + methodVisitor.visitEnd(); + } + { + methodVisitor = + classWriter.visitMethod( + ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(3, label0); + methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + methodVisitor.visitLdcInsn(Long.valueOf(2L)); + methodVisitor.visitLdcInsn(Integer.valueOf(2)); + methodVisitor.visitMethodInsn( + INVOKESTATIC, "java/lang/Math", "multiplyExact", "(JI)J", false); + methodVisitor.visitMethodInsn( + INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V", false); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(4, label1); + methodVisitor.visitInsn(RETURN); + methodVisitor.visitMaxs(5, 1); + methodVisitor.visitEnd(); + } + classWriter.visitEnd(); + + return classWriter.toByteArray(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/NoBackportForAndroidPlatform.java b/src/test/java/com/android/tools/r8/desugar/backports/NoBackportForAndroidPlatform.java deleted file mode 100644 index 47081ab..0000000 --- a/src/test/java/com/android/tools/r8/desugar/backports/NoBackportForAndroidPlatform.java +++ /dev/null
@@ -1,128 +0,0 @@ -// 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.backports; - -import static org.hamcrest.core.StringContains.containsString; - -import com.android.tools.r8.TestBase; -import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.utils.AndroidApiLevel; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -@RunWith(Parameterized.class) -public class NoBackportForAndroidPlatform extends TestBase implements Opcodes { - - private final TestParameters parameters; - - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - // The use of high API level will produce dex files with high DEX version, so only run on high - // API level VMs. - return getTestParameters() - .withDexRuntimes() - .withApiLevelsStartingAtIncluding(AndroidApiLevel.P) - .build(); - } - - public NoBackportForAndroidPlatform(TestParameters parameters) { - this.parameters = parameters; - } - - @Test - public void backportSucceedsOnSupportedApiLevel() throws Exception { - testForD8() - .addProgramClassFileData(mainWithMathMultiplyExactLongInt()) - .setMinApi(AndroidApiLevel.B) - .run(parameters.getRuntime(), "Test") - .assertSuccessWithOutputLines("4"); - } - - @Test - public void warningForNonPlatformBuild() throws Exception { - testForD8() - .addProgramClassFileData(mainWithMathMultiplyExactLongInt()) - .setMinApi(30) - .compile() - .assertOnlyWarnings() - .assertWarningMessageThatMatches( - containsString("An API level of 30 is not supported by this compiler")) - .run(parameters.getRuntime(), "Test") - .assertFailureWithErrorThatMatches( - containsString("java.lang.NoSuchMethodError: No static method multiplyExact(JI)J")); - } - - @Test - public void noWarningForPlatformBuild() throws Exception { - testForD8() - .addProgramClassFileData(mainWithMathMultiplyExactLongInt()) - .setMinApi(AndroidApiLevel.magicApiLevelUsedByAndroidPlatformBuild) - .run(parameters.getRuntime(), "Test") - .assertFailureWithErrorThatMatches( - containsString("java.lang.NoSuchMethodError: No static method multiplyExact(JI)J")); - } - - // Code for: - // - // class Test { - // public static void main(String[] args) { - // // Call Math.multiplyExact(long, int), which is not in Android Q. - // System.out.println(Math.multiplyExact(2L, 2)); - // } - // } - // - private byte[] mainWithMathMultiplyExactLongInt() { - - ClassWriter classWriter = new ClassWriter(0); - MethodVisitor methodVisitor; - - classWriter.visit(V1_8, ACC_SUPER, "Test", null, "java/lang/Object", null); - - classWriter.visitSource("Test.java", null); - - { - methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(1, label0); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); - methodVisitor.visitInsn(RETURN); - methodVisitor.visitMaxs(1, 1); - methodVisitor.visitEnd(); - } - { - methodVisitor = - classWriter.visitMethod( - ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); - methodVisitor.visitCode(); - Label label0 = new Label(); - methodVisitor.visitLabel(label0); - methodVisitor.visitLineNumber(3, label0); - methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); - methodVisitor.visitLdcInsn(Long.valueOf(2L)); - methodVisitor.visitLdcInsn(Integer.valueOf(2)); - methodVisitor.visitMethodInsn( - INVOKESTATIC, "java/lang/Math", "multiplyExact", "(JI)J", false); - methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V", false); - Label label1 = new Label(); - methodVisitor.visitLabel(label1); - methodVisitor.visitLineNumber(4, label1); - methodVisitor.visitInsn(RETURN); - methodVisitor.visitMaxs(5, 1); - methodVisitor.visitEnd(); - } - classWriter.visitEnd(); - - return classWriter.toByteArray(); - } -}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java index bf823d4..6177bf3 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -19,10 +19,10 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; 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.DexItemFactory; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DirectMappedDexApplication; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; @@ -89,10 +89,11 @@ DexProgramClass clazz = appView.appInfo().classes().iterator().next(); assertEquals(TestClass.class.getTypeName(), clazz.type.toSourceString()); - for (DexEncodedMethod method : clazz.methods()) { - IRCode code = method.buildIR(appView, Origin.unknown()); - fieldAccessAnalysis.recordFieldAccesses(code, feedback, new MethodProcessorMock()); - } + clazz.forEachProgramMethod( + method -> { + IRCode code = method.buildIR(appView); + fieldAccessAnalysis.recordFieldAccesses(code, feedback, new MethodProcessorMock()); + }); int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField")); assertTrue(BitUtils.isBitSet(bitsReadInBitField, 1)); @@ -216,12 +217,12 @@ } @Override - public boolean shouldApplyCodeRewritings(DexEncodedMethod method) { + public boolean shouldApplyCodeRewritings(ProgramMethod method) { return false; } @Override - public boolean isProcessedConcurrently(DexEncodedMethod method) { + public boolean isProcessedConcurrently(ProgramMethod method) { return false; } }
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java index 7b7036f..2d56233 100644 --- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java +++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -4,30 +4,59 @@ package com.android.tools.r8.ir.conversion; import com.android.tools.r8.TestBase; +import com.android.tools.r8.graph.ClassAccessFlags; import com.android.tools.r8.graph.DexAnnotationSet; +import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.CallGraph.Node; +import com.android.tools.r8.origin.SynthesizedOrigin; +import java.util.Collections; class CallGraphTestBase extends TestBase { + private DexItemFactory dexItemFactory = new DexItemFactory(); + private DexProgramClass clazz = + new DexProgramClass( + dexItemFactory.createType("LCallGraphTest;"), + null, + new SynthesizedOrigin("test", CallGraphTestBase.class), + ClassAccessFlags.fromSharedAccessFlags(0), + dexItemFactory.objectType, + DexTypeList.empty(), + null, + null, + Collections.emptyList(), + null, + Collections.emptyList(), + DexAnnotationSet.empty(), + DexEncodedField.EMPTY_ARRAY, + DexEncodedField.EMPTY_ARRAY, + DexEncodedMethod.EMPTY_ARRAY, + DexEncodedMethod.EMPTY_ARRAY, + false, + DexProgramClass::invalidChecksumRequest); Node createNode(String methodName) { DexMethod signature = dexItemFactory.createMethod( - dexItemFactory.objectType, - dexItemFactory.createProto(dexItemFactory.voidType), - methodName); - return new Node( - new DexEncodedMethod( - signature, null, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null)); + clazz.type, dexItemFactory.createProto(dexItemFactory.voidType), methodName); + ProgramMethod method = + new ProgramMethod( + clazz, + new DexEncodedMethod( + signature, null, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null)); + return new Node(method); } Node createForceInlinedNode(String methodName) { Node node = createNode(methodName); - node.method.getMutableOptimizationInfo().markForceInline(); + node.getMethod().getMutableOptimizationInfo().markForceInline(); return node; } }
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CycleEliminationTest.java b/src/test/java/com/android/tools/r8/ir/conversion/CycleEliminationTest.java index c9ae9fd..2611dcd 100644 --- a/src/test/java/com/android/tools/r8/ir/conversion/CycleEliminationTest.java +++ b/src/test/java/com/android/tools/r8/ir/conversion/CycleEliminationTest.java
@@ -161,9 +161,9 @@ for (Node node : configuration.nodes) { if (configuration.forceInline.contains(node)) { - node.method.getMutableOptimizationInfo().markForceInline(); + node.getMethod().getMutableOptimizationInfo().markForceInline(); } else { - node.method.getMutableOptimizationInfo().unsetForceInline(); + node.getMethod().getMutableOptimizationInfo().unsetForceInline(); } }
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/NodeExtractionTest.java b/src/test/java/com/android/tools/r8/ir/conversion/NodeExtractionTest.java index 4088693..1459b20 100644 --- a/src/test/java/com/android/tools/r8/ir/conversion/NodeExtractionTest.java +++ b/src/test/java/com/android/tools/r8/ir/conversion/NodeExtractionTest.java
@@ -47,20 +47,20 @@ nodes.add(n6); CallGraph cg = new CallGraph(nodes); - Set<DexEncodedMethod> wave = cg.extractLeaves(); + Set<DexEncodedMethod> wave = cg.extractLeaves().toDefinitionSet(); assertEquals(3, wave.size()); - assertThat(wave, hasItem(n3.method)); - assertThat(wave, hasItem(n4.method)); - assertThat(wave, hasItem(n6.method)); + assertThat(wave, hasItem(n3.getMethod())); + assertThat(wave, hasItem(n4.getMethod())); + assertThat(wave, hasItem(n6.getMethod())); - wave = cg.extractLeaves(); + wave = cg.extractLeaves().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n2.method)); - assertThat(wave, hasItem(n5.method)); + assertThat(wave, hasItem(n2.getMethod())); + assertThat(wave, hasItem(n5.getMethod())); - wave = cg.extractLeaves(); + wave = cg.extractLeaves().toDefinitionSet(); assertEquals(1, wave.size()); - assertThat(wave, hasItem(n1.method)); + assertThat(wave, hasItem(n1.getMethod())); assertTrue(nodes.isEmpty()); } @@ -91,27 +91,27 @@ nodes.add(n6); n1.addCallerConcurrently(n3); - n3.method.getMutableOptimizationInfo().markForceInline(); + n3.getMethod().getMutableOptimizationInfo().markForceInline(); CycleEliminator cycleEliminator = new CycleEliminator(); assertEquals(1, cycleEliminator.breakCycles(nodes).numberOfRemovedCallEdges()); CallGraph cg = new CallGraph(nodes); - Set<DexEncodedMethod> wave = cg.extractLeaves(); + Set<DexEncodedMethod> wave = cg.extractLeaves().toDefinitionSet(); assertEquals(3, wave.size()); - assertThat(wave, hasItem(n3.method)); - assertThat(wave, hasItem(n4.method)); - assertThat(wave, hasItem(n6.method)); + assertThat(wave, hasItem(n3.getMethod())); + assertThat(wave, hasItem(n4.getMethod())); + assertThat(wave, hasItem(n6.getMethod())); wave.clear(); - wave = cg.extractLeaves(); + wave = cg.extractLeaves().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n2.method)); - assertThat(wave, hasItem(n5.method)); + assertThat(wave, hasItem(n2.getMethod())); + assertThat(wave, hasItem(n5.getMethod())); wave.clear(); - wave = cg.extractLeaves(); + wave = cg.extractLeaves().toDefinitionSet(); assertEquals(1, wave.size()); - assertThat(wave, hasItem(n1.method)); + assertThat(wave, hasItem(n1.getMethod())); assertTrue(nodes.isEmpty()); } @@ -142,20 +142,20 @@ nodes.add(n6); CallGraph callGraph = new CallGraph(nodes, null); - Set<DexEncodedMethod> wave = callGraph.extractRoots(); + Set<DexEncodedMethod> wave = callGraph.extractRoots().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n1.method)); - assertThat(wave, hasItem(n5.method)); + assertThat(wave, hasItem(n1.getMethod())); + assertThat(wave, hasItem(n5.getMethod())); - wave = callGraph.extractRoots(); + wave = callGraph.extractRoots().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n2.method)); - assertThat(wave, hasItem(n6.method)); + assertThat(wave, hasItem(n2.getMethod())); + assertThat(wave, hasItem(n6.getMethod())); - wave = callGraph.extractRoots(); + wave = callGraph.extractRoots().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n3.method)); - assertThat(wave, hasItem(n4.method)); + assertThat(wave, hasItem(n3.getMethod())); + assertThat(wave, hasItem(n4.getMethod())); assertTrue(nodes.isEmpty()); } @@ -186,25 +186,25 @@ nodes.add(n6); n1.addCallerConcurrently(n3); - n3.method.getMutableOptimizationInfo().markForceInline(); + n3.getMethod().getMutableOptimizationInfo().markForceInline(); CycleEliminator cycleEliminator = new CycleEliminator(); assertEquals(1, cycleEliminator.breakCycles(nodes).numberOfRemovedCallEdges()); CallGraph callGraph = new CallGraph(nodes, null); - Set<DexEncodedMethod> wave = callGraph.extractRoots(); + Set<DexEncodedMethod> wave = callGraph.extractRoots().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n1.method)); - assertThat(wave, hasItem(n5.method)); + assertThat(wave, hasItem(n1.getMethod())); + assertThat(wave, hasItem(n5.getMethod())); - wave = callGraph.extractRoots(); + wave = callGraph.extractRoots().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n2.method)); - assertThat(wave, hasItem(n6.method)); + assertThat(wave, hasItem(n2.getMethod())); + assertThat(wave, hasItem(n6.getMethod())); - wave = callGraph.extractRoots(); + wave = callGraph.extractRoots().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(n3.method)); - assertThat(wave, hasItem(n4.method)); + assertThat(wave, hasItem(n3.getMethod())); + assertThat(wave, hasItem(n4.getMethod())); assertTrue(nodes.isEmpty()); } }
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java b/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java index 1ea4e67..c838a0d 100644 --- a/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java +++ b/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
@@ -11,8 +11,9 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.CallGraph.Node; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.ProguardConfigurationParser; @@ -21,8 +22,8 @@ import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -66,37 +67,41 @@ assertNotNull(m5); assertNotNull(m6); - Set<DexEncodedMethod> wave = cg.extractLeaves(); + Set<DexEncodedMethod> wave = cg.extractLeaves().toDefinitionSet(); assertEquals(4, wave.size()); // including <init> - assertThat(wave, hasItem(m3.method)); - assertThat(wave, hasItem(m4.method)); - assertThat(wave, hasItem(m6.method)); + assertThat(wave, hasItem(m3.getMethod())); + assertThat(wave, hasItem(m4.getMethod())); + assertThat(wave, hasItem(m6.getMethod())); - wave = cg.extractLeaves(); + wave = cg.extractLeaves().toDefinitionSet(); assertEquals(2, wave.size()); - assertThat(wave, hasItem(m2.method)); - assertThat(wave, hasItem(m5.method)); + assertThat(wave, hasItem(m2.getMethod())); + assertThat(wave, hasItem(m5.getMethod())); - wave = cg.extractLeaves(); + wave = cg.extractLeaves().toDefinitionSet(); assertEquals(1, wave.size()); - assertThat(wave, hasItem(m1.method)); + assertThat(wave, hasItem(m1.getMethod())); assertTrue(cg.nodes.isEmpty()); } @Test public void testPartialGraph() throws Exception { - DexEncodedMethod em1 = findMethod("m1"); - DexEncodedMethod em2 = findMethod("m2"); - DexEncodedMethod em4 = findMethod("m4"); - DexEncodedMethod em5 = findMethod("m5"); + ProgramMethod em1 = findMethod("m1"); + ProgramMethod em2 = findMethod("m2"); + ProgramMethod em4 = findMethod("m4"); + ProgramMethod em5 = findMethod("m5"); assertNotNull(em1); assertNotNull(em2); assertNotNull(em4); assertNotNull(em5); + ProgramMethodSet seeds = ProgramMethodSet.create(); + seeds.add(em1); + seeds.add(em2); + seeds.add(em4); + seeds.add(em5); CallGraph pg = - new PartialCallGraphBuilder(appView, ImmutableSet.of(em1, em2, em4, em5)) - .build(executorService, Timing.empty()); + new PartialCallGraphBuilder(appView, seeds).build(executorService, Timing.empty()); Node m1 = findNode(pg.nodes, "m1"); Node m2 = findNode(pg.nodes, "m2"); @@ -109,37 +114,37 @@ Set<DexEncodedMethod> wave = Sets.newIdentityHashSet(); - wave.addAll(pg.extractRoots()); + wave.addAll(pg.extractRoots().toDefinitionSet()); assertEquals(2, wave.size()); - assertThat(wave, hasItem(m1.method)); - assertThat(wave, hasItem(m5.method)); + assertThat(wave, hasItem(m1.getMethod())); + assertThat(wave, hasItem(m5.getMethod())); wave.clear(); - wave.addAll(pg.extractRoots()); + wave.addAll(pg.extractRoots().toDefinitionSet()); assertEquals(1, wave.size()); - assertThat(wave, hasItem(m2.method)); + assertThat(wave, hasItem(m2.getMethod())); wave.clear(); - wave.addAll(pg.extractRoots()); + wave.addAll(pg.extractRoots().toDefinitionSet()); assertEquals(1, wave.size()); - assertThat(wave, hasItem(m4.method)); + assertThat(wave, hasItem(m4.getMethod())); assertTrue(pg.nodes.isEmpty()); } private Node findNode(Iterable<Node> nodes, String name) { for (Node n : nodes) { - if (n.method.method.name.toString().equals(name)) { + if (n.getMethod().method.name.toString().equals(name)) { return n; } } return null; } - private DexEncodedMethod findMethod(String name) { - for (DexClass clazz : appView.appInfo().classes()) { + private ProgramMethod findMethod(String name) { + for (DexProgramClass clazz : appView.appInfo().classes()) { for (DexEncodedMethod method : clazz.methods()) { if (method.method.name.toString().equals(name)) { - return method; + return new ProgramMethod(clazz, method); } } }
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 b5997bf..0c158b9 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
@@ -7,7 +7,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -41,11 +41,11 @@ .assertSuccessWithOutputLines("10"); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { + private void callSiteOptimizationInfoInspect(ProgramMethod method) { // TODO(b/139246447): should avoid visiting A#<init>, which is trivial, default init! - assert encodedMethod.holder().toSourceString().endsWith("A") - && encodedMethod.toSourceString().contains("<init>") - : "Unexpected revisit: " + encodedMethod.toSourceString(); + assert method.getHolderType().toSourceString().endsWith("A") + && method.toSourceString().contains("<init>") + : "Unexpected revisit: " + method.toSourceString(); } static class TestClass {
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 54a3b8e..2edbb0b 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
@@ -14,7 +14,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -60,11 +60,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); - if (encodedMethod.holder().toSourceString().endsWith("$C")) { + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); + if (method.getHolderType().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 96b85ec..e36941d 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
@@ -14,7 +14,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -57,11 +57,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); - if (encodedMethod.holder().toSourceString().endsWith("$C")) { + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); + if (method.getHolderType().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/KeptMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java index 51797eb..8d405c4 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; @@ -55,8 +55,8 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert false : "Unexpected revisit: " + encodedMethod.toSourceString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert false : "Unexpected revisit: " + method.toSourceString(); } private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java index 19172e7..8a057dd 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper.DexVm.Version; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; @@ -66,8 +66,8 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert false : "Unexpected revisit: " + encodedMethod.toSourceString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert false : "Unexpected revisit: " + method.toSourceString(); } private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java index 19a9034..2e76fa2 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -55,10 +55,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown(); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java index 30aec5e..bdc3694 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.InternalOptions; @@ -58,11 +58,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); - assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1); assert abstractValue.isSingleStringValue() && abstractValue.asSingleStringValue().getDexString().toString().equals("nul");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java index ba6b644..75dd8a8 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -60,10 +60,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown(); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java index a966e62..e837610 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.InternalOptions; @@ -61,10 +61,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1); assert abstractValue.isSingleStringValue()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java index cacb6a3..df1fb98 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -52,10 +52,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull(); assert callSiteOptimizationInfo.getAbstractArgumentValue(0).isUnknown(); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java index 1d92c6e..a1e936b 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.InternalOptions; @@ -55,10 +55,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull(); AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(0); assert abstractValue.isSingleStringValue()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java index 28523aa..3ae302a 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -57,11 +57,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - String methodName = encodedMethod.method.name.toString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + String methodName = method.getReference().name.toString(); assert methodName.equals("m") || methodName.equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); if (methodName.equals("m")) { assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); assert callSiteOptimizationInfo.getAbstractArgumentValue(1).isUnknown();
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 14ff937..30764845 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
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.InternalOptions; @@ -59,13 +59,14 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1); - if (encodedMethod.holder().toSourceString().endsWith("$A")) { + if (method.getHolderType().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/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java index 349c613..0cc9bb1 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -55,10 +55,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isDefinitelyNotNull(); assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java index 9119e88..e131be5 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -57,11 +57,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - String methodName = encodedMethod.method.name.toString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + String methodName = method.getReference().name.toString(); assert methodName.equals("<init>") || methodName.equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType; if (methodName.equals("test")) { upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java index b341d62..4b4c534 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -60,10 +60,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isDefinitelyNotNull(); assert upperBoundType.isClassType()
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 204f41b..bda0a3a 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
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -60,13 +60,14 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isDefinitelyNotNull(); - if (encodedMethod.holder().toSourceString().endsWith("$A")) { + if (method.getHolderType().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/dynamicupperboundtype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java index d98e911..3c94778 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -52,10 +52,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(0); assert upperBoundType.isDefinitelyNotNull(); assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java index d014d5a..6ea0888 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -54,11 +54,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - String methodName = encodedMethod.method.name.toString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + String methodName = method.getReference().name.toString(); assert methodName.equals("<init>") || methodName.equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); // `arg` for `test` or the receiver of `Base#<init>`. // TODO(b/139246447): should avoid visiting <init>, which is trivial, default init! // For testing purpose, `Base` is not merged and kept. The system correctly caught that, when
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java index c2fffc1..51145b2 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -57,11 +57,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - String methodName = encodedMethod.method.name.toString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + String methodName = method.getReference().name.toString(); assert methodName.equals("m") || methodName.equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); if (methodName.equals("m")) { TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isDefinitelyNotNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java index 22a4faa..8c17c3a 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -57,11 +57,12 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - String methodName = encodedMethod.method.name.toString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + String methodName = method.getReference().name.toString(); assert methodName.equals("<init>") || methodName.equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType; if (methodName.equals("m")) { upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java index 5b79156..96614da 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; @@ -53,8 +53,8 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert false : "Unexpected revisit: " + encodedMethod.toSourceString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert false : "Unexpected revisit: " + method.toSourceString(); } private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java index 9630bab..0b7ff7d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -54,10 +54,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java index e7c5597..79a49e0 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -60,10 +60,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isNullable(); assert upperBoundType.isClassType()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java index 17cea42..27612a2 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -57,10 +57,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull(); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java index 1f3d6cc..0a18b69 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; @@ -50,8 +50,8 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert false : "Unexpected revisit: " + encodedMethod.toSourceString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert false : "Unexpected revisit: " + method.toSourceString(); } private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java index 4743089..700ec34 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -51,10 +51,11 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("test") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull(); }
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 4882c32..26bb65e 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
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -57,16 +57,17 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - String methodName = encodedMethod.method.name.toString(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + String methodName = method.getReference().name.toString(); assert methodName.equals("m") || methodName.equals("test") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); if (methodName.equals("m")) { TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isNullable(); assert upperBoundType.isClassType() - && upperBoundType.asClassType().getClassType().equals(encodedMethod.holder()); + && upperBoundType.asClassType().getClassType().equals(method.getHolderType()); } 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 0009024..00601a9 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
@@ -13,7 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -57,17 +57,18 @@ .inspect(this::inspect); } - private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) { - assert encodedMethod.method.name.toString().equals("m") - : "Unexpected revisit: " + encodedMethod.toSourceString(); - CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo(); + private void callSiteOptimizationInfoInspect(ProgramMethod method) { + assert method.getReference().name.toString().equals("m") + : "Unexpected revisit: " + method.toSourceString(); + CallSiteOptimizationInfo callSiteOptimizationInfo = + method.getDefinition().getCallSiteOptimizationInfo(); TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1); assert upperBoundType.isClassType() && upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A"); - if (encodedMethod.holder().toSourceString().endsWith("$A")) { + if (method.getHolderType().toSourceString().endsWith("$A")) { assert upperBoundType.isDefinitelyNotNull(); } else { - assert encodedMethod.holder().toSourceString().endsWith("$B"); + assert method.getHolderType().toSourceString().endsWith("$B"); assert upperBoundType.isNullable(); } }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java index df33e2b..8943139 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWithDefaultValueAssignmentAfterDefaultsOptimizationTest.java
@@ -10,9 +10,8 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; -import java.util.ArrayList; -import java.util.Collection; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.Deque; import java.util.Optional; import org.junit.Test; @@ -46,16 +45,16 @@ .assertSuccessWithOutputLines("42"); } - private void waveModifier(Deque<Collection<DexEncodedMethod>> waves) { - Collection<DexEncodedMethod> initialWave = waves.getFirst(); - Optional<DexEncodedMethod> printFieldMethod = + private void waveModifier(Deque<ProgramMethodSet> waves) { + ProgramMethodSet initialWave = waves.getFirst(); + Optional<ProgramMethod> printFieldMethod = initialWave.stream() - .filter(method -> method.method.name.toSourceString().equals("printField")) + .filter(method -> method.getReference().name.toSourceString().equals("printField")) .findFirst(); assertTrue(printFieldMethod.isPresent()); - initialWave.remove(printFieldMethod.get()); + initialWave.remove(printFieldMethod.get().getDefinition()); - ArrayList<DexEncodedMethod> lastWave = new ArrayList<>(); + ProgramMethodSet lastWave = ProgramMethodSet.create(); lastWave.add(printFieldMethod.get()); waves.addLast(lastWave); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java index d230621..b493f1d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
@@ -15,11 +15,10 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; -import java.util.Collection; +import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.function.Function; import java.util.function.Predicate; import org.junit.Test; @@ -50,13 +49,12 @@ options -> { options.testing.waveModifier = (waves) -> { - Function<String, Predicate<Collection<DexEncodedMethod>>> wavePredicate = + Function<String, Predicate<ProgramMethodSet>> wavePredicate = methodName -> wave -> wave.stream() .anyMatch( - method -> - method.method.toSourceString().contains(methodName)); + method -> method.toSourceString().contains(methodName)); int readFieldsWaveIndex = IterableUtils.firstIndexMatching(waves, wavePredicate.apply("readFields")); assertTrue(readFieldsWaveIndex >= 0);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java index fcb5958..32a0e6b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; +import com.android.tools.r8.kotlin.KotlinMetadataWriter; import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -77,13 +78,12 @@ assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName()); // We cannot assert equality of the data since it may be ordered differently. Instead we use // the KotlinMetadataWriter. - // TODO(b/155571455): Deactivating the method call to kotlinMetadataString until resolved. - // String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata); - // String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata); - // // TODO(b/155534905): For invalid synthetic class lambdas, we emit null after rewriting. - // if (clazzSubject.getKotlinClassMetadata().getHeader().getKind() != 3) { - // assertEquals(expected, actual); - // } + String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata); + String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata); + // TODO(b/155534905): For invalid synthetic class lambdas, we emit null after rewriting. + if (clazzSubject.getKotlinClassMetadata().getHeader().getKind() != 3) { + assertEquals(expected, actual); + } } } }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java index 2b5270d..cacf66b 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -50,6 +50,7 @@ import com.android.tools.r8.graph.InitClassLens; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Position; @@ -83,6 +84,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -806,10 +808,31 @@ options.intermediate = intermediate; DexItemFactory factory = options.itemFactory; AppInfo appInfo = new AppInfo(DexApplication.builder(options, timing).build()); - DexApplication.Builder builder = DexApplication.builder(options, timing); + AppView<?> appView = AppView.createForR8(appInfo, options); + DexApplication.Builder<?> builder = DexApplication.builder(options, timing); for (String clazz : classes) { DexString desc = factory.createString(DescriptorUtils.javaTypeToDescriptor(clazz)); DexType type = factory.createType(desc); + DexProgramClass programClass = + new DexProgramClass( + type, + null, + new SynthesizedOrigin("test", MainDexListTests.class), + ClassAccessFlags.fromSharedAccessFlags(0), + factory.objectType, + DexTypeList.empty(), + null, + null, + Collections.emptyList(), + null, + Collections.emptyList(), + DexAnnotationSet.empty(), + DexEncodedField.EMPTY_ARRAY, + DexEncodedField.EMPTY_ARRAY, + DexEncodedMethod.EMPTY_ARRAY, + DexEncodedMethod.EMPTY_ARRAY, + false, + DexProgramClass::invalidChecksumRequest); DexEncodedMethod[] directMethods = new DexEncodedMethod[methodCount]; for (int i = 0; i < methodCount; i++) { MethodAccessFlags access = MethodAccessFlags.fromSharedAccessFlags(0, false); @@ -831,32 +854,13 @@ DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code); - AppView<?> appView = AppView.createForR8(appInfo, options); - IRCode ir = code.buildIR(method, appView, Origin.unknown()); + ProgramMethod programMethod = new ProgramMethod(programClass, method); + IRCode ir = code.buildIR(programMethod, appView, Origin.unknown()); RegisterAllocator allocator = new LinearScanRegisterAllocator(appView, ir); method.setCode(ir, allocator, appView); directMethods[i] = method; } - DexProgramClass programClass = - new DexProgramClass( - type, - null, - new SynthesizedOrigin("test", MainDexListTests.class), - ClassAccessFlags.fromSharedAccessFlags(0), - factory.objectType, - DexTypeList.empty(), - null, - null, - Collections.emptyList(), - null, - Collections.emptyList(), - DexAnnotationSet.empty(), - DexEncodedField.EMPTY_ARRAY, - DexEncodedField.EMPTY_ARRAY, - directMethods, - DexEncodedMethod.EMPTY_ARRAY, - false, - DexProgramClass::invalidChecksumRequest); + programClass.getMethodCollection().addDirectMethods(Arrays.asList(directMethods)); builder.addProgramClass(programClass); } DirectMappedDexApplication application = builder.build().toDirect();
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java index 96d429c..759082f 100644 --- a/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java +++ b/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java
@@ -48,7 +48,7 @@ public RelocatorServiceLoaderTest(TestParameters parameters) {} @Test - public void testNotRewritingServiceForNotFoundClass() + public void testRewritingOfServicesForNotFoundClasses() throws IOException, CompilationFailedException, ResourceException { File testJar = temp.newFile("test.jar"); Path testJarPath = testJar.toPath(); @@ -72,8 +72,13 @@ Reference.packageFromString("foo.bar"), Reference.packageFromString("baz.qux")) .build()); zip = new ZipFile(relocatedJar.toFile()); - ZipEntry serviceEntry = zip.getEntry(SERVICE_FILE); + ZipEntry serviceEntry = zip.getEntry("META-INF/services/baz.qux.Baz"); assertNotNull(serviceEntry); + InputStream inputStream = zip.getInputStream(serviceEntry); + Scanner scanner = new Scanner(inputStream); + assertEquals("baz.qux.BazImpl", scanner.next()); + assertEquals("foo.baz.OtherImpl", scanner.next()); + assertFalse(scanner.hasNext()); } @Test
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java index 9690ea6..62d8695 100644 --- a/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java +++ b/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java
@@ -17,6 +17,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; +import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.BooleanUtils; @@ -75,8 +76,15 @@ Map<String, String> mapping = new HashMap<>(); mapping.put(originalPrefix, newPrefix); runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output); - inspectAllClassesRelocated( - ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, newPrefix + "."); + // TODO(b/155618698): Extend relocator with a richer language such that java.lang.Object is not + // relocated. + CompilationError compilationError = + assertThrows( + CompilationError.class, + () -> + inspectAllClassesRelocated( + ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, newPrefix + ".")); + assertThat(compilationError.getMessage(), containsString("must extend class java.lang.Object")); } @Test
diff --git a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java index 2d85c6b..17cce81 100644 --- a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java +++ b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
@@ -11,13 +11,12 @@ import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexApplication; -import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.code.Return; import com.android.tools.r8.ir.code.Value; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.smali.SmaliBuilder.MethodSignature; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.InternalOptions; @@ -78,10 +77,9 @@ DexApplication application = new ApplicationReader(originalApplication, options, Timing.empty()).read(); - DexEncodedMethod method = getMethod(originalApplication, methodSig); + ProgramMethod method = getProgramMethod(originalApplication, methodSig); // Get the IR pre-optimization. - IRCode code = - method.buildIR(AppView.createForD8(new AppInfo(application), options), Origin.unknown()); + IRCode code = method.buildIR(AppView.createForD8(new AppInfo(application), options)); // Find the exit block and assert that the value is a phi merging the exceptional edge // with the normal edge.
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java index 36c7766..8ef02dc 100644 --- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java +++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -17,6 +17,7 @@ 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.ProgramMethod; import com.android.tools.r8.origin.EmbeddedOrigin; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.ProguardConfiguration; @@ -165,6 +166,10 @@ return getMethodSubject(application, signature).getMethod(); } + protected ProgramMethod getProgramMethod(AndroidApp application, MethodSignature signature) { + return getMethodSubject(application, signature).getProgramMethod(); + } + /** * Create an application with one method, and processed that application using R8. *
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java index e08c9b7..ed7ac4d 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.naming.MemberNaming.Signature; @@ -94,6 +95,11 @@ } @Override + public ProgramMethod getProgramMethod() { + return null; + } + + @Override public MethodSignature getOriginalSignature() { return null; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java index 00d00ca..df3f287 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -35,7 +35,8 @@ private final DexClass dexClass; final ClassNamingForNameMapper naming; - FoundClassSubject(CodeInspector codeInspector, DexClass dexClass, ClassNamingForNameMapper naming) { + FoundClassSubject( + CodeInspector codeInspector, DexClass dexClass, ClassNamingForNameMapper naming) { this.codeInspector = codeInspector; this.dexClass = dexClass; this.naming = naming;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java index 782bbf0..0e504ca 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -25,11 +25,11 @@ 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.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.naming.MemberNaming; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.naming.signature.GenericSignatureParser; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.InternalOptions; @@ -60,13 +60,8 @@ @Override public IRCode buildIR(InternalOptions options) { options.programConsumer = DexIndexedConsumer.emptyConsumer(); - DexEncodedMethod method = getMethod(); - return method - .getCode() - .buildIR( - method, - AppView.createForD8(new AppInfo(codeInspector.application), options), - Origin.unknown()); + return getProgramMethod() + .buildIR(AppView.createForD8(new AppInfo(codeInspector.application), options)); } @Override @@ -145,6 +140,11 @@ } @Override + public ProgramMethod getProgramMethod() { + return new ProgramMethod(clazz.getDexProgramClass(), getMethod()); + } + + @Override public MethodSignature getOriginalSignature() { MethodSignature signature = getFinalSignature(); if (clazz.naming == null) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java index 50911f3..b158404 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.utils.InternalOptions; @@ -54,6 +55,8 @@ public abstract DexEncodedMethod getMethod(); + public abstract ProgramMethod getProgramMethod(); + public Iterator<InstructionSubject> iterateInstructions() { return null; }
diff --git a/src/test/sampleApks/simple/res/values-da/strings.xml b/src/test/sampleApks/simple/res/values-da/strings.xml new file mode 100644 index 0000000..26c62ec --- /dev/null +++ b/src/test/sampleApks/simple/res/values-da/strings.xml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<resources> + <string name="app_name">R8 simpel app</string> + <string name="referenced_from_layout">Tryk</string> + <!-- Do not inlcude referenced_from_code --> + <string name="not_used">ikke brugt</string> +</resources>
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py index c035520..a08a4ec 100755 --- a/tools/build_sample_apk.py +++ b/tools/build_sample_apk.py
@@ -20,6 +20,7 @@ DEFAULT_AAPT = 'aapt' # Assume in path. +DEFAULT_AAPT2 = 'aapt2' # Assume in path. DEFAULT_D8 = os.path.join(utils.REPO_ROOT, 'tools', 'd8.py') DEFAULT_DEXSPLITTER = os.path.join(utils.REPO_ROOT, 'tools', 'dexsplitter.py') DEFAULT_JAVAC = jdk.GetJavacExecutable() @@ -39,6 +40,9 @@ result.add_option('--aapt', help='aapt executable to use', default=DEFAULT_AAPT) + result.add_option('--aapt2', + help='aapt2 executable to use', + default=DEFAULT_AAPT2) result.add_option('--api', help='Android api level', default=21, @@ -50,6 +54,9 @@ result.add_option('--split', help='Split the app using the split.spec file', default=False, action='store_true') + result.add_option('--generate-proto-apk', + help='Use aapt2 to generate the proto version of the apk.', + default=False, action='store_true') result.add_option('--install', help='Install the app (including featuresplit)', default=False, action='store_true') @@ -273,6 +280,19 @@ if 'adb logcat' in output: raise Exception('You have adb logcat running, please close it and rerun') +def generate_proto_apks(apks, options): + proto_apks = [] + for apk in apks: + proto_apk = apk + '.proto' + cmd = [options.aapt2, 'convert', + '-o', proto_apk, + '--output-format', 'proto', + apk] + utils.PrintCmd(cmd) + subprocess.check_call(cmd) + proto_apks.append(proto_apk) + return proto_apks + def Main(): (options, args) = parse_options() apks = [] @@ -287,13 +307,11 @@ if is_split: split(options.app) dex_path = get_split_path(options.app, 'base') - temp_apk_path = create_temp_apk(options.app, '') aapt_add_dex(options.aapt, dex_path, temp_apk_path) apk_path = os.path.join(get_bin_path(options.app), '%s.apk' % options.app) apk_utils.sign(temp_apk_path, apk_path, options.keystore) apks.append(apk_path) - if is_split: split_temp_apk_path = create_temp_apk(options.app, 'split_') aapt_add_dex(options.aapt, @@ -302,7 +320,9 @@ split_apk_path = os.path.join(get_bin_path(options.app), 'featuresplit.apk') apk_utils.sign(temp_apk_path, split_apk_path, options.keystore) apks.append(split_apk_path) - + if options.generate_proto_apk: + proto_apks = generate_proto_apks(apks, options) + print('Generated proto apks available at: %s' % ' '.join(proto_apks)) print('Generated apks available at: %s' % ' '.join(apks)) if options.install or options.benchmark: adb_install(apks)