Merge commit '34e27f5ef1e7849efd311fd649791a0024ef686a' into dev-release
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index cc80809..7837f26 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -47,10 +47,26 @@
""" % FMT_CMD))
return results
+def CheckDeterministicDebuggingChanged(input_api, output_api):
+ for f in input_api.AffectedFiles():
+ path = f.LocalPath()
+ if not path.endswith('InternalOptions.java'):
+ continue
+ branch = (
+ check_output(['git', 'cl', 'upstream'])
+ .strip()
+ .replace('refs/heads/', ''))
+ diff = check_output(
+ ['git', 'diff', '--no-prefix', '-U0', branch, '--', path])
+ if 'DETERMINISTIC_DEBUGGING' in diff:
+ return [output_api.PresubmitError(diff)]
+ return []
+
def CheckChange(input_api, output_api):
results = []
results.extend(CheckFormatting(input_api, output_api))
results.extend(CheckDoNotMerge(input_api, output_api))
+ results.extend(CheckDeterministicDebuggingChanged(input_api, output_api))
return results
def CheckChangeOnCommit(input_api, output_api):
diff --git a/build.gradle b/build.gradle
index fd47c97..77b5865 100644
--- a/build.gradle
+++ b/build.gradle
@@ -35,7 +35,7 @@
ext {
androidSupportVersion = '25.4.0'
- asmVersion = '7.1'
+ asmVersion = '7.2'
espressoVersion = '3.0.0'
fastutilVersion = '7.2.0'
guavaVersion = '23.0'
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index a6d704b..bf55656 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -132,7 +132,7 @@
List<DexEncodedMethod> directMethods = new ArrayList<>();
List<DexEncodedMethod> virtualMethods = new ArrayList<>();
for (DexEncodedMethod method : methods) {
- assert method.method.holder == clazz.type;
+ assert method.holder() == clazz.type;
CfCode code = null;
if (!method.accessFlags.isAbstract() /*&& !method.accessFlags.isNative()*/) {
code = buildEmptyThrowingCfCode(method.method);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 4b310aa..51b6ed7 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -203,7 +203,7 @@
isStatic
? appInfo.lookupStaticTarget(field.holder, field)
: appInfo.lookupInstanceTarget(field.holder, field);
- if (baseField != null && baseField.field.holder != field.holder) {
+ if (baseField != null && baseField.holder() != field.holder) {
field = baseField.field;
}
addType(field.holder);
@@ -214,7 +214,7 @@
noObfuscationTypes.add(field.holder);
}
if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(baseField.field.holder.getPackageName());
+ keepPackageNames.add(baseField.holder().getPackageName());
}
typeFields.add(field);
}
@@ -234,7 +234,7 @@
noObfuscationTypes.add(method.holder);
}
if (encodedMethod.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(encodedMethod.method.holder.getPackageName());
+ keepPackageNames.add(encodedMethod.holder().getPackageName());
}
typeMethods.add(method);
}
@@ -247,7 +247,7 @@
private void registerMethod(DexEncodedMethod method) {
DexEncodedMethod superTarget =
appInfo
- .resolveMethod(method.method.holder, method.method)
+ .resolveMethod(method.holder(), method.method)
.lookupInvokeSpecialTarget(context, appInfo);
if (superTarget != null) {
addMethod(superTarget.method);
@@ -477,7 +477,7 @@
if (encodedMethod.accessFlags.isStatic()) {
append("<clinit>");
} else {
- String holderName = encodedMethod.method.holder.toSourceString();
+ String holderName = encodedMethod.holder().toSourceString();
String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1);
append(constructorName);
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5bca79b1..15af102 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -34,7 +34,9 @@
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.NestedPrivateMethodLense;
import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -45,6 +47,7 @@
import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.UninstantiatedTypeOptimizationGraphLense;
import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector;
import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector.UnusedArgumentsGraphLense;
+import com.android.tools.r8.ir.optimize.enums.EnumUnboxingCfMethods;
import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.jar.CfApplicationWriter;
@@ -280,6 +283,11 @@
if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView);
}
+ InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options);
+ BackportedMethodRewriter.registerAssumedLibraryTypes(options);
+ if (options.enableEnumUnboxing) {
+ EnumUnboxingCfMethods.registerSynthesizedCodeReferences(options.itemFactory);
+ }
List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
timing.begin("Strip unused code");
@@ -364,21 +372,22 @@
application = pruner.run(application);
// Recompute the subtyping information.
+ Set<DexType> removedClasses = pruner.getRemovedClasses();
appView.setAppInfo(
appView
.appInfo()
.withLiveness()
.prunedCopyFrom(
application,
- pruner.getRemovedClasses(),
+ removedClasses,
pruner.getMethodsToKeepForConfigurationDebugging()));
- appView.setAppServices(appView.appServices().prunedCopy(pruner.getRemovedClasses()));
+ appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
new AbstractMethodRemover(appView.appInfo().withLiveness()).run();
AnnotationRemover annotationRemover =
annotationRemoverBuilder
.computeClassesToRetainInnerClassAttributeFor(appViewWithLiveness)
- .build(appViewWithLiveness);
+ .build(appViewWithLiveness, removedClasses);
annotationRemover.ensureValid().run();
classesToRetainInnerClassAttributeFor =
annotationRemover.getClassesToRetainInnerClassAttributeFor();
@@ -659,6 +668,7 @@
TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration);
application = pruner.run(application);
+ Set<DexType> removedClasses = pruner.getRemovedClasses();
if (options.usageInformationConsumer != null) {
ExceptionUtils.withFinishedResourceHandler(
@@ -669,9 +679,9 @@
.appInfo()
.prunedCopyFrom(
application,
- CollectionUtils.mergeSets(prunedTypes, pruner.getRemovedClasses()),
+ CollectionUtils.mergeSets(prunedTypes, removedClasses),
pruner.getMethodsToKeepForConfigurationDebugging()));
- appView.setAppServices(appView.appServices().prunedCopy(pruner.getRemovedClasses()));
+ appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
// TODO(b/130721661): Enable this assert.
// assert Inliner.verifyNoMethodsInlinedDueToSingleCallSite(appView);
@@ -694,7 +704,7 @@
assert classesToRetainInnerClassAttributeFor != null;
AnnotationRemover.builder()
.setClassesToRetainInnerClassAttributeFor(classesToRetainInnerClassAttributeFor)
- .build(appView.withLiveness())
+ .build(appView.withLiveness(), removedClasses)
.run();
if (!mainDexClasses.isEmpty()) {
// Remove types that no longer exists from the computed main dex list.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index fea04c7..3e9ae7e 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
+
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
@@ -854,6 +856,7 @@
internal.enableVerticalClassMerging = false;
internal.enableClassStaticizer = false;
internal.outline.enabled = false;
+ internal.enableEnumUnboxing = false;
}
// Amend the proguard-map consumer with options from the proguard configuration.
@@ -925,8 +928,10 @@
internal.synthesizedClassPrefix = synthesizedClassPrefix;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
- assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
- internal.threadCount = getThreadCount();
+ if (!DETERMINISTIC_DEBUGGING) {
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+ }
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index 93ee213..21a5392 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -75,6 +75,7 @@
private Set<String> imports = new HashSet<>();
private List<String> methods = new ArrayList<>();
private Set<String> methodNames = new HashSet<>();
+ private Set<String> synthesizedTypes = new HashSet<>();
// Per method structures.
@@ -261,7 +262,8 @@
if (field != null) {
return "options.itemFactory." + field;
}
- return "options.itemFactory.createSynthesizedType(" + quote(descriptor) + ")";
+ synthesizedTypes.add(descriptor);
+ return "options.itemFactory.createType(" + quote(descriptor) + ")";
}
private String dexProto(DexProto proto) {
@@ -523,4 +525,8 @@
public void print(CfConstMethodType type) {
throw new Unimplemented(type.getClass().getSimpleName());
}
+
+ public Set<String> getSynthesizedTypes() {
+ return synthesizedTypes;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index 5b4d9f2..93973c3 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -238,8 +238,8 @@
if (argumentIndex < 0) {
argumentType =
code.method.isInstanceInitializer()
- ? new ThisInstanceInfo(instruction.asArgument(), code.method.method.holder)
- : createInitializedType(code.method.method.holder);
+ ? new ThisInstanceInfo(instruction.asArgument(), code.method.holder())
+ : createInitializedType(code.method.holder());
} else {
argumentType =
createInitializedType(code.method.method.proto.parameters.values[argumentIndex]);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index a7c3ec0..bd4c6da 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -351,10 +351,10 @@
String originalClassName;
if (proguardMap != null) {
signature = proguardMap.originalSignatureOf(method.method);
- originalClassName = proguardMap.originalNameOf(method.method.holder);
+ originalClassName = proguardMap.originalNameOf(method.holder());
} else {
signature = MethodSignature.fromDexMethod(method.method);
- originalClassName = method.method.holder.toSourceString();
+ originalClassName = method.holder().toSourceString();
}
codeToSignatureMap.put(code, originalClassName + signature);
}
diff --git a/src/main/java/com/android/tools/r8/errors/InvalidLibrarySuperclassDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InvalidLibrarySuperclassDiagnostic.java
new file mode 100644
index 0000000..2392f0a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InvalidLibrarySuperclassDiagnostic.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.errors;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+
+/**
+ * Diagnostic for super types of library classes which are not library classes but required for
+ * desugaring.
+ */
+@Keep
+public class InvalidLibrarySuperclassDiagnostic implements DesugarDiagnostic {
+
+ private final Origin origin;
+ private final List<MethodReference> methods;
+ private final ClassReference libraryType;
+ private final ClassReference invalidSuperType;
+ private final String message;
+
+ public InvalidLibrarySuperclassDiagnostic(
+ Origin origin,
+ ClassReference libraryType,
+ ClassReference invalidSuperType,
+ String message,
+ List<MethodReference> methods) {
+ assert origin != null;
+ assert libraryType != null;
+ assert invalidSuperType != null;
+ assert message != null;
+ this.origin = origin;
+ this.libraryType = libraryType;
+ this.invalidSuperType = invalidSuperType;
+ this.message = message;
+ this.methods = methods;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder =
+ new StringBuilder()
+ .append("Superclass `")
+ .append(invalidSuperType.getTypeName())
+ .append("` of library class `")
+ .append(libraryType.getTypeName())
+ .append("` is ")
+ .append(message)
+ .append(
+ ". A superclass of a library class should be a library class. This is required for"
+ + " the desugaring of ");
+ StringUtils.append(builder, methods, ", ", StringUtils.BraceType.NONE);
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index a41e119..8b63cbf 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -141,6 +141,10 @@
@Override
public DexClass definitionFor(DexType type) {
+ return definitionForWithoutExistenceAssert(type);
+ }
+
+ public final DexClass definitionForWithoutExistenceAssert(DexType type) {
assert checkIfObsolete();
DexProgramClass cached = synthesizedClasses.get(type);
if (cached != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 58611d4..4edc070 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -357,7 +357,7 @@
return true;
}
- public boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) {
+ public boolean isInstantiatedInterface(DexProgramClass clazz) {
assert checkIfObsolete();
return true; // Don't know, there might be.
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 0c4267c..897763a 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -400,7 +400,7 @@
@Override
public void registerCodeReferences(DexEncodedMethod method, UseRegistry registry) {
for (CfInstruction instruction : instructions) {
- instruction.registerUse(registry, method.method.holder);
+ instruction.registerUse(registry, method.holder());
}
for (CfTryCatch tryCatch : tryCatchRanges) {
for (DexType guard : tryCatch.guards) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 85810ed..9dd0398 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -332,7 +332,7 @@
}
private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
- assert field.field.holder == type
+ assert field.holder() == type
: "Expected field `"
+ field.field.toSourceString()
+ "` to have holder `"
@@ -435,8 +435,7 @@
}
private boolean isSignaturePolymorphicMethod(DexEncodedMethod method, DexItemFactory factory) {
- assert method.method.holder == factory.methodHandleType
- || method.method.holder == factory.varHandleType;
+ assert method.holder() == factory.methodHandleType || method.holder() == factory.varHandleType;
return method.accessFlags.isVarargs()
&& method.accessFlags.isNative()
&& method.method.proto.parameters.size() == 1
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 37f8853..6c05f12 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -12,7 +12,7 @@
private final DexEncodedMethod method;
DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
- assert holder.type == method.method.holder;
+ assert holder.type == method.holder();
this.holder = holder;
this.method = method;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index 49611c6..e73c267 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -73,7 +73,7 @@
int argumentRegister = code.registerSize - code.incomingRegisterSize;
if (!method.accessFlags.isStatic()) {
DexString name = factory.thisName;
- DexType type = method.method.holder;
+ DexType type = method.holder();
startArgument(argumentRegister, name, type);
argumentRegister += ValueType.fromDexType(type).requiredRegisters();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 10427a3..0d5c50d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -159,6 +159,10 @@
return isStatic();
}
+ public boolean isVolatile() {
+ return accessFlags.isVolatile();
+ }
+
public boolean hasAnnotation() {
return !annotations().isEmpty();
}
@@ -206,7 +210,7 @@
&& singleValue.asSingleFieldValue().getField() == field) {
return null;
}
- if (singleValue.isMaterializableInContext(appView, code.method.method.holder)) {
+ if (singleValue.isMaterializableInContext(appView, code.method.holder())) {
TypeElement type = TypeElement.fromDexType(field.type, maybeNull(), appView);
return singleValue.createMaterializingInstruction(
appView, code, TypeAndLocalInfoSupplier.create(type, local));
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 01b0e84..bb1ee15 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -450,7 +450,7 @@
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
checkIfObsolete();
return isInliningCandidate(
- container.method.holder, inliningReason, appInfo, whyAreYouNotInliningReporter);
+ container.holder(), inliningReason, appInfo, whyAreYouNotInliningReporter);
}
public boolean isInliningCandidate(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 30d9369..b0a86c6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -26,6 +26,8 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.optimize.enums.EnumUnboxingRewriter;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.LRUCacheTable;
@@ -156,6 +158,7 @@
public final DexString isEmptyMethodName = createString("isEmpty");
public final DexString lengthMethodName = createString("length");
+ public final DexString concatMethodName = createString("concat");
public final DexString containsMethodName = createString("contains");
public final DexString startsWithMethodName = createString("startsWith");
public final DexString endsWithMethodName = createString("endsWith");
@@ -254,6 +257,8 @@
public final DexString throwableDescriptor = createString(throwableDescriptorString);
public final DexString illegalAccessErrorDescriptor =
createString("Ljava/lang/IllegalAccessError;");
+ public final DexString illegalArgumentExceptionDescriptor =
+ createString("Ljava/lang/IllegalArgumentException;");
public final DexString icceDescriptor = createString("Ljava/lang/IncompatibleClassChangeError;");
public final DexString exceptionInInitializerErrorDescriptor =
createString("Ljava/lang/ExceptionInInitializerError;");
@@ -381,6 +386,8 @@
public final DexType throwableType = createStaticallyKnownType(throwableDescriptor);
public final DexType illegalAccessErrorType =
createStaticallyKnownType(illegalAccessErrorDescriptor);
+ public final DexType illegalArgumentExceptionType =
+ createStaticallyKnownType(illegalArgumentExceptionDescriptor);
public final DexType icceType = createStaticallyKnownType(icceDescriptor);
public final DexType exceptionInInitializerErrorType =
createStaticallyKnownType(exceptionInInitializerErrorDescriptor);
@@ -398,6 +405,15 @@
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
+ public final DexString nestConstructorDescriptor =
+ createString("L" + NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME + ";");
+ public final DexType nestConstructorType = createStaticallyKnownType(nestConstructorDescriptor);
+
+ public final DexString enumUnboxingUtilityDescriptor =
+ createString("L" + EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_CLASS_NAME + ";");
+ public final DexType enumUnboxingUtilityType =
+ createStaticallyKnownType(enumUnboxingUtilityDescriptor);
+
public final StringBuildingMethods stringBuilderMethods =
new StringBuildingMethods(stringBuilderType);
public final StringBuildingMethods stringBufferMethods =
@@ -415,6 +431,8 @@
public final ConstructorMethods constructorMethods = new ConstructorMethods();
public final EnumMethods enumMethods = new EnumMethods();
public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods();
+ public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods =
+ new IllegalArgumentExceptionMethods();
public final PrimitiveTypesBoxedTypeFields primitiveTypesBoxedTypeFields =
new PrimitiveTypesBoxedTypeFields();
public final AtomicFieldUpdaterMethods atomicFieldUpdaterMethods =
@@ -965,6 +983,13 @@
createMethod(npeType, createProto(voidType, stringType), constructorMethodName);
}
+ public class IllegalArgumentExceptionMethods {
+
+ public final DexMethod initWithMessage =
+ createMethod(
+ illegalArgumentExceptionType, createProto(voidType, stringType), initMethodName);
+ }
+
/**
* All boxed types (Boolean, Byte, ...) have a field named TYPE which contains the Class object
* for the primitive type.
@@ -1051,6 +1076,7 @@
public final DexMethod isEmpty;
public final DexMethod length;
+ public final DexMethod concat;
public final DexMethod contains;
public final DexMethod startsWith;
public final DexMethod endsWith;
@@ -1083,6 +1109,7 @@
DexString[] needsOneObject = { objectDescriptor };
DexString[] needsOneInt = { intDescriptor };
+ concat = createMethod(stringDescriptor, concatMethodName, stringDescriptor, needsOneString);
contains = createMethod(
stringDescriptor, containsMethodName, booleanDescriptor, needsOneCharSequence);
startsWith = createMethod(
@@ -1414,6 +1441,12 @@
return type;
}
+ // Registration of a type that is only dynamically known (eg, in the desugared lib spec), but
+ // will be referenced during desugaring.
+ public void registerTypeNeededForDesugaring(DexType type) {
+ addPossiblySynthesizedType(type);
+ }
+
private void addPossiblySynthesizedType(DexType type) {
if (type.isArrayType()) {
type = type.toBaseType(this);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 80605e5..5236191 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -6,6 +6,11 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import java.util.ArrayList;
+import java.util.List;
public class DexMethod extends DexMember<DexEncodedMethod, DexMethod> {
@@ -28,6 +33,22 @@
return "Method " + holder + "." + name + " " + proto.toString();
}
+ public MethodReference asMethodReference(AppView<?> appView) {
+ List<TypeReference> parameters = new ArrayList<>();
+ for (DexType value : proto.parameters.values) {
+ parameters.add(Reference.typeFromDescriptor(value.toDescriptorString()));
+ }
+ TypeReference returnType =
+ proto.returnType == appView.dexItemFactory().voidType
+ ? null
+ : Reference.typeFromDescriptor(proto.returnType.toDescriptorString());
+ return Reference.method(
+ Reference.classFromDescriptor(holder.toDescriptorString()),
+ name.toString(),
+ parameters,
+ returnType);
+ }
+
public int getArity() {
return proto.parameters.size();
}
diff --git a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
index 4fd96f7..23bfb99 100644
--- a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java
@@ -7,6 +7,7 @@
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
public class EnumValueInfoMapCollection {
@@ -94,6 +95,10 @@
return map.get(field);
}
+ public void forEach(BiConsumer<DexField, EnumValueInfo> consumer) {
+ map.forEach(consumer);
+ }
+
EnumValueInfoMap rewrittenWithLens(GraphLense lens) {
ImmutableMap.Builder<DexField, EnumValueInfo> builder = ImmutableMap.builder();
map.forEach(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 049cb7a..97d49a5 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -96,6 +96,8 @@
*/
public class GenericSignature {
+ private static final List<FormalTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of();
+
interface DexDefinitionSignature<T extends DexDefinition> {
default boolean isClassSignature() {
return false;
@@ -122,19 +124,51 @@
}
}
+ public static class FormalTypeParameter {
+
+ final String name;
+ final FieldTypeSignature classBound;
+ final List<FieldTypeSignature> interfaceBounds;
+
+ FormalTypeParameter(
+ String name, FieldTypeSignature classBound, List<FieldTypeSignature> interfaceBounds) {
+ this.name = name;
+ this.classBound = classBound;
+ this.interfaceBounds = interfaceBounds;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public FieldTypeSignature getClassBound() {
+ return classBound;
+ }
+
+ public List<FieldTypeSignature> getInterfaceBounds() {
+ return interfaceBounds;
+ }
+ }
+
public static class ClassSignature implements DexDefinitionSignature<DexClass> {
static final ClassSignature UNKNOWN_CLASS_SIGNATURE =
- new ClassSignature(ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE, ImmutableList.of());
+ new ClassSignature(
+ ImmutableList.of(),
+ ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE,
+ ImmutableList.of());
- // TODO(b/129925954): encoding formal type parameters
+ final List<FormalTypeParameter> formalTypeParameters;
final ClassTypeSignature superClassSignature;
final List<ClassTypeSignature> superInterfaceSignatures;
ClassSignature(
+ List<FormalTypeParameter> formalTypeParameters,
ClassTypeSignature superClassSignature,
List<ClassTypeSignature> superInterfaceSignatures) {
+ assert formalTypeParameters != null;
assert superClassSignature != null;
assert superInterfaceSignatures != null;
+ this.formalTypeParameters = formalTypeParameters;
this.superClassSignature = superClassSignature;
this.superInterfaceSignatures = superInterfaceSignatures;
}
@@ -221,6 +255,10 @@
public TypeVariableSignature asTypeVariableSignature() {
return null;
}
+
+ public boolean isUnknown() {
+ return this == ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
+ }
}
public static class ClassTypeSignature extends FieldTypeSignature {
@@ -331,7 +369,7 @@
public static class TypeVariableSignature extends FieldTypeSignature {
final String typeVariable;
- TypeVariableSignature(String typeVariable) {
+ private TypeVariableSignature(String typeVariable) {
assert typeVariable != null;
this.typeVariable = typeVariable;
}
@@ -350,6 +388,10 @@
public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
return new ArrayTypeSignature(this);
}
+
+ public String getTypeVariable() {
+ return typeVariable;
+ }
}
// TODO(b/129925954): Canonicalization?
@@ -400,20 +442,24 @@
public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> {
static final MethodTypeSignature UNKNOWN_METHOD_TYPE_SIGNATURE =
- new MethodTypeSignature(ImmutableList.of(), ReturnType.VOID, ImmutableList.of());
+ new MethodTypeSignature(
+ ImmutableList.of(), ImmutableList.of(), ReturnType.VOID, ImmutableList.of());
- // TODO(b/129925954): encoding formal type parameters
+ final List<FormalTypeParameter> formalTypeParameters;
final List<TypeSignature> typeSignatures;
final ReturnType returnType;
final List<TypeSignature> throwsSignatures;
MethodTypeSignature(
+ final List<FormalTypeParameter> formalTypeParameters,
List<TypeSignature> typeSignatures,
ReturnType returnType,
List<TypeSignature> throwsSignatures) {
+ assert formalTypeParameters != null;
assert typeSignatures != null;
assert returnType != null;
assert throwsSignatures != null;
+ this.formalTypeParameters = formalTypeParameters;
this.typeSignatures = typeSignatures;
this.returnType = returnType;
this.throwsSignatures = throwsSignatures;
@@ -443,6 +489,10 @@
public MethodTypeSignature asMethodTypeSignature() {
return this;
}
+
+ public List<FormalTypeParameter> getFormalTypeParameters() {
+ return formalTypeParameters;
+ }
}
enum Kind {
@@ -518,7 +568,7 @@
public static FieldTypeSignature toFieldTypeSignature(
DexEncodedField field, AppView<AppInfoWithLiveness> appView) {
- DexClass currentClassContext = appView.definitionFor(field.field.holder);
+ DexClass currentClassContext = appView.definitionFor(field.holder());
DexDefinitionSignature<?> signature =
toGenericSignature(currentClassContext, field, appView);
if (signature != null) {
@@ -530,7 +580,7 @@
public static MethodTypeSignature toMethodTypeSignature(
DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) {
- DexClass currentClassContext = appView.definitionFor(method.method.holder);
+ DexClass currentClassContext = appView.definitionFor(method.holder());
DexDefinitionSignature<?> signature =
toGenericSignature(currentClassContext, method, appView);
if (signature != null) {
@@ -688,7 +738,7 @@
private ClassSignature parseClassSignature() {
// ClassSignature ::= FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*.
- parseOptFormalTypeParameters();
+ List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
// SuperclassSignature ::= ClassTypeSignature.
ClassTypeSignature superClassSignature =
@@ -700,42 +750,53 @@
builder.add(parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION));
}
- return new ClassSignature(superClassSignature, builder.build());
+ return new ClassSignature(formalTypeParameters, superClassSignature, builder.build());
}
- private void parseOptFormalTypeParameters() {
+ private List<FormalTypeParameter> parseOptFormalTypeParameters() {
// FormalTypeParameters ::= "<" FormalTypeParameter+ ">".
-
- if (symbol == '<') {
- scanSymbol();
-
- updateFormalTypeParameter();
-
- while ((symbol != '>') && (symbol > 0)) {
- updateFormalTypeParameter();
- }
-
- expect('>');
+ if (symbol != '<') {
+ return EMPTY_TYPE_PARAMS;
}
+ scanSymbol();
+
+ ImmutableList.Builder<FormalTypeParameter> builder = ImmutableList.builder();
+ while ((symbol != '>') && (symbol > 0)) {
+ builder.add(updateFormalTypeParameter());
+ }
+ expect('>');
+ return builder.build();
}
- private void updateFormalTypeParameter() {
+ private FormalTypeParameter updateFormalTypeParameter() {
// FormalTypeParameter ::= Identifier ClassBound InterfaceBound*.
scanIdentifier();
assert identifier != null;
+ String typeParameterIdentifier = identifier;
+
// ClassBound ::= ":" FieldTypeSignature?.
expect(':');
+ FieldTypeSignature classBound = ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
if (symbol == 'L' || symbol == '[' || symbol == 'T') {
- parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+ classBound = parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
+ // Only build the interfacebound builder, which is uncommon, if we actually see an interface.
+ ImmutableList.Builder<FieldTypeSignature> builder = null;
while (symbol == ':') {
// InterfaceBound ::= ":" FieldTypeSignature.
+ if (builder == null) {
+ builder = ImmutableList.builder();
+ }
scanSymbol();
- parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+ builder.add(parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION));
}
+ if (builder == null) {
+ return new FormalTypeParameter(typeParameterIdentifier, classBound, null);
+ }
+ return new FormalTypeParameter(typeParameterIdentifier, classBound, builder.build());
}
private FieldTypeSignature parseFieldTypeSignature(ParserPosition parserPosition) {
@@ -862,7 +923,7 @@
private MethodTypeSignature parseMethodTypeSignature() {
// MethodTypeSignature ::=
// FormalTypeParameters? "(" TypeSignature* ")" ReturnType ThrowsSignature*.
- parseOptFormalTypeParameters();
+ List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
expect('(');
@@ -890,7 +951,10 @@
}
return new MethodTypeSignature(
- parameterSignatureBuilder.build(), returnType, throwsSignatureBuilder.build());
+ formalTypeParameters,
+ parameterSignatureBuilder.build(),
+ returnType,
+ throwsSignatureBuilder.build());
}
private ReturnType updateReturnType() {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 7d2d844..e5de7ff 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -15,7 +15,6 @@
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
-import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -710,7 +709,7 @@
@Override
public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) {
- Set<DexMethod> result = new HashSet<>();
+ Set<DexMethod> result = Sets.newIdentityHashSet();
for (DexMethod previous : previousLense.lookupMethodInAllContexts(method)) {
result.add(methodMap.getOrDefault(previous, previous));
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 269b36b..6fecde6 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -228,7 +228,7 @@
}
private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
- assert method.method.holder == holder.type
+ assert method.holder() == holder.type
: "Expected method `"
+ method.method.toSourceString()
+ "` to have holder `"
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
index 2ed505e..546bf90 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -20,6 +20,12 @@
boolean isInstantiatedDirectly(DexProgramClass clazz);
+ boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz);
+
+ boolean isInterfaceWithUnknownSubtypeHierarchy(DexProgramClass clazz);
+
+ boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass clazz);
+
ObjectAllocationInfoCollection rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLense lens);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 3869d23..5999bbf 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -21,28 +21,101 @@
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
-import java.util.function.Predicate;
-/** Stores the set of instantiated classes along with their allocation sites. */
-public class ObjectAllocationInfoCollectionImpl implements ObjectAllocationInfoCollection {
+/**
+ * Provides information about all possibly instantiated classes and lambdas, their allocation sites,
+ * if known, as well as the full subtyping hierarchy of types above them.
+ */
+public abstract class ObjectAllocationInfoCollectionImpl implements ObjectAllocationInfoCollection {
- private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking;
- private final Set<DexProgramClass> classesWithoutAllocationSiteTracking;
+ /** Instantiated classes with the contexts of the instantiations. */
+ final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking =
+ new IdentityHashMap<>();
- private ObjectAllocationInfoCollectionImpl(
- Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking,
- Set<DexProgramClass> classesWithoutAllocationSiteTracking) {
- this.classesWithAllocationSiteTracking = classesWithAllocationSiteTracking;
- this.classesWithoutAllocationSiteTracking = classesWithoutAllocationSiteTracking;
+ /** Instantiated classes without contexts. */
+ final Set<DexProgramClass> classesWithoutAllocationSiteTracking = Sets.newIdentityHashSet();
+
+ /**
+ * Set of interface types for which the subtype hierarchy is unknown from that type.
+ *
+ * <p>E.g., the type is kept thus there could be instantiations of subtypes.
+ *
+ * <p>TODO(b/145344105): Generalize this to typesWithUnknownSubtypeHierarchy.
+ */
+ final Set<DexProgramClass> interfacesWithUnknownSubtypeHierarchy = Sets.newIdentityHashSet();
+
+ /** Map of types directly implemented by lambdas to those lambdas. */
+ final Map<DexType, List<LambdaDescriptor>> instantiatedLambdas = new IdentityHashMap<>();
+
+ /**
+ * Hierarchy for instantiated types mapping a type to the set of immediate subtypes for which some
+ * subtype is either an instantiated class, kept interface or is implemented by an instantiated
+ * lambda.
+ */
+ Map<DexType, Set<DexClass>> instantiatedHierarchy = new IdentityHashMap<>();
+
+ private ObjectAllocationInfoCollectionImpl() {
+ // Only builder can allocate an instance.
}
public static Builder builder(boolean trackAllocationSites, GraphReporter reporter) {
return new Builder(trackAllocationSites, reporter);
}
- public void markNoLongerInstantiated(DexProgramClass clazz) {
- classesWithAllocationSiteTracking.remove(clazz);
- classesWithoutAllocationSiteTracking.remove(clazz);
+ public abstract void mutate(Consumer<Builder> mutator, AppInfo appInfo);
+
+ /**
+ * True if a class type might be instantiated directly at the given type.
+ *
+ * <p>Should not be called on interface types.
+ *
+ * <p>TODO(b/145344105): Extend this to not be called on any abstract types.
+ */
+ @Override
+ public boolean isInstantiatedDirectly(DexProgramClass clazz) {
+ if (clazz.isInterface()) {
+ return false;
+ }
+ if (classesWithAllocationSiteTracking.containsKey(clazz)) {
+ assert !classesWithAllocationSiteTracking.get(clazz).isEmpty();
+ return true;
+ }
+ return classesWithoutAllocationSiteTracking.contains(clazz);
+ }
+
+ /** True if the type or subtype of it might be instantiated. */
+ @Override
+ public boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz) {
+ return (!clazz.isInterface() && isInstantiatedDirectly(clazz))
+ || hasInstantiatedStrictSubtype(clazz);
+ }
+
+ /** True if there might exist an instantiated (strict) subtype of the given type. */
+ public boolean hasInstantiatedStrictSubtype(DexProgramClass clazz) {
+ if (instantiatedHierarchy.get(clazz.type) != null) {
+ return true;
+ }
+ if (!clazz.isInterface()) {
+ return false;
+ }
+ return interfacesWithUnknownSubtypeHierarchy.contains(clazz)
+ || isImmediateInterfaceOfInstantiatedLambda(clazz);
+ }
+
+ /** True if the type is an interface that has unknown instantiations, eg, by being kept. */
+ @Override
+ public boolean isInterfaceWithUnknownSubtypeHierarchy(DexProgramClass clazz) {
+ return clazz.isInterface() && interfacesWithUnknownSubtypeHierarchy.contains(clazz);
+ }
+
+ /** Returns true if the type is an immediate interface of an instantiated lambda. */
+ @Override
+ public boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass iface) {
+ return iface.isInterface() && instantiatedLambdas.get(iface.type) != null;
+ }
+
+ public Set<DexClass> getImmediateSubtypesInInstantiatedHierarchy(DexType type) {
+ return instantiatedHierarchy.get(type);
}
@Override
@@ -57,238 +130,15 @@
}
@Override
- public boolean isInstantiatedDirectly(DexProgramClass clazz) {
- if (classesWithAllocationSiteTracking.containsKey(clazz)) {
- assert !classesWithAllocationSiteTracking.get(clazz).isEmpty();
- return true;
- }
- return classesWithoutAllocationSiteTracking.contains(clazz);
- }
-
- @Override
public ObjectAllocationInfoCollectionImpl rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLense lens) {
- return builder(true, null).rewrittenWithLens(this, definitions, lens).build();
+ return builder(true, null).rewrittenWithLens(this, definitions, lens).build(definitions);
}
- public static class Builder {
-
- private final boolean trackAllocationSites;
-
- /** Instantiated classes with the contexts of the instantiations. */
- private final Map<DexProgramClass, Set<DexEncodedMethod>> classesWithAllocationSiteTracking =
- new IdentityHashMap<>();
-
- /** Instantiated classes without contexts. */
- private final Set<DexProgramClass> classesWithoutAllocationSiteTracking =
- Sets.newIdentityHashSet();
-
- /** Set of types directly implemented by a lambda. */
- private final Map<DexType, List<LambdaDescriptor>> instantiatedLambdas =
- new IdentityHashMap<>();
-
- /**
- * Hierarchy for instantiated types mapping a type to the set of immediate subtypes for which
- * some subtype is either instantiated or is implemented by an instantiated lambda.
- */
- private final Map<DexType, Set<DexClass>> instantiatedHierarchy = new IdentityHashMap<>();
-
- /**
- * Set of interface types for which there may be instantiations, such as lambda expressions or
- * explicit keep rules.
- */
- private final Set<DexProgramClass> instantiatedInterfaceTypes = Sets.newIdentityHashSet();
-
- /** Subset of the above that are marked instantiated by usages that are not lambdas. */
- public final Set<DexProgramClass> unknownInstantiatedInterfaceTypes = Sets.newIdentityHashSet();
-
- private GraphReporter reporter;
-
- private Builder(boolean trackAllocationSites, GraphReporter reporter) {
- this.trackAllocationSites = trackAllocationSites;
- this.reporter = reporter;
- }
-
- private boolean shouldTrackAllocationSitesForClass(
- DexProgramClass clazz, InstantiationReason instantiationReason) {
- if (!trackAllocationSites) {
- return false;
- }
- if (instantiationReason != InstantiationReason.NEW_INSTANCE_INSTRUCTION) {
- // There is an allocation site which is not a new-instance instruction.
- return false;
- }
- if (classesWithoutAllocationSiteTracking.contains(clazz)) {
- // We already gave up on tracking the allocation sites for `clazz` previously.
- return false;
- }
- // We currently only use allocation site information for instance field value propagation.
- return !clazz.instanceFields().isEmpty();
- }
-
- public boolean isInstantiatedDirectlyOrIsInstantiationLeaf(DexProgramClass clazz) {
- if (clazz.isInterface()) {
- return instantiatedInterfaceTypes.contains(clazz);
- }
- return isInstantiatedDirectly(clazz);
- }
-
- public boolean isInstantiatedDirectly(DexProgramClass clazz) {
- assert !clazz.isInterface();
- if (classesWithAllocationSiteTracking.containsKey(clazz)) {
- assert !classesWithAllocationSiteTracking.get(clazz).isEmpty();
- return true;
- }
- return classesWithoutAllocationSiteTracking.contains(clazz);
- }
-
- public boolean isInstantiatedDirectlyOrHasInstantiatedSubtype(DexProgramClass clazz) {
- return isInstantiatedDirectlyOrIsInstantiationLeaf(clazz)
- || instantiatedHierarchy.containsKey(clazz.type);
- }
-
- public void forEachInstantiatedSubType(
- DexType type,
- Consumer<DexProgramClass> onClass,
- Consumer<LambdaDescriptor> onLambda,
- AppInfo appInfo) {
- internalForEachInstantiatedSubType(
- type,
- onClass,
- onLambda,
- instantiatedHierarchy,
- instantiatedLambdas,
- this::isInstantiatedDirectlyOrIsInstantiationLeaf,
- appInfo);
- }
-
- public Set<DexClass> getImmediateSubtypesInInstantiatedHierarchy(DexType type) {
- return instantiatedHierarchy.get(type);
- }
-
- /**
- * Records that {@param clazz} is instantiated in {@param context}.
- *
- * @return true if {@param clazz} was not instantiated before.
- */
- public boolean recordDirectAllocationSite(
- DexProgramClass clazz,
- DexEncodedMethod context,
- InstantiationReason instantiationReason,
- KeepReason keepReason,
- AppInfo appInfo) {
- assert !clazz.isInterface();
- if (reporter != null) {
- reporter.registerClass(clazz, keepReason);
- }
- populateInstantiatedHierarchy(appInfo, clazz);
- if (shouldTrackAllocationSitesForClass(clazz, instantiationReason)) {
- assert context != null;
- Set<DexEncodedMethod> allocationSitesForClass =
- classesWithAllocationSiteTracking.computeIfAbsent(
- clazz, ignore -> Sets.newIdentityHashSet());
- allocationSitesForClass.add(context);
- return allocationSitesForClass.size() == 1;
- }
- if (classesWithoutAllocationSiteTracking.add(clazz)) {
- Set<DexEncodedMethod> allocationSitesForClass =
- classesWithAllocationSiteTracking.remove(clazz);
- return allocationSitesForClass == null;
- }
- return false;
- }
-
- public boolean recordInstantiatedInterface(DexProgramClass iface) {
- assert iface.isInterface();
- assert !iface.isAnnotation();
- unknownInstantiatedInterfaceTypes.add(iface);
- return instantiatedInterfaceTypes.add(iface);
- }
-
- public void recordInstantiatedLambdaInterface(
- DexType iface, LambdaDescriptor lambda, AppInfo appInfo) {
- instantiatedLambdas.computeIfAbsent(iface, key -> new ArrayList<>()).add(lambda);
- populateInstantiatedHierarchy(appInfo, iface);
- }
-
- private void populateInstantiatedHierarchy(AppInfo appInfo, DexType type) {
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz != null) {
- populateInstantiatedHierarchy(appInfo, clazz);
- }
- }
-
- private void populateInstantiatedHierarchy(AppInfo appInfo, DexClass clazz) {
- if (clazz.superType != null) {
- populateInstantiatedHierarchy(appInfo, clazz.superType, clazz);
- }
- for (DexType iface : clazz.interfaces.values) {
- populateInstantiatedHierarchy(appInfo, iface, clazz);
- }
- }
-
- private void populateInstantiatedHierarchy(AppInfo appInfo, DexType type, DexClass subtype) {
- if (type == appInfo.dexItemFactory().objectType) {
- return;
- }
- Set<DexClass> subtypes = instantiatedHierarchy.get(type);
- if (subtypes != null) {
- subtypes.add(subtype);
- return;
- }
- // This is the first time an instantiation appears below 'type', recursively populate.
- subtypes = Sets.newIdentityHashSet();
- subtypes.add(subtype);
- instantiatedHierarchy.put(type, subtypes);
- populateInstantiatedHierarchy(appInfo, type);
- }
-
- Builder rewrittenWithLens(
- ObjectAllocationInfoCollectionImpl objectAllocationInfos,
- DexDefinitionSupplier definitions,
- GraphLense lens) {
- objectAllocationInfos.classesWithAllocationSiteTracking.forEach(
- (clazz, allocationSitesForClass) -> {
- DexType type = lens.lookupType(clazz.type);
- if (type.isPrimitiveType()) {
- return;
- }
- DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type));
- assert rewrittenClass != null;
- assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass);
- classesWithAllocationSiteTracking.put(
- rewrittenClass,
- LensUtils.rewrittenWithRenamedSignature(
- allocationSitesForClass, definitions, lens));
- });
- objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach(
- clazz -> {
- DexType type = lens.lookupType(clazz.type);
- if (type.isPrimitiveType()) {
- return;
- }
- DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type));
- assert rewrittenClass != null;
- assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass);
- assert !classesWithoutAllocationSiteTracking.contains(rewrittenClass);
- classesWithoutAllocationSiteTracking.add(rewrittenClass);
- });
- return this;
- }
-
- public ObjectAllocationInfoCollectionImpl build() {
- return new ObjectAllocationInfoCollectionImpl(
- classesWithAllocationSiteTracking, classesWithoutAllocationSiteTracking);
- }
- }
-
- private static void internalForEachInstantiatedSubType(
+ public void forEachInstantiatedSubType(
DexType type,
- Consumer<DexProgramClass> subTypeConsumer,
- Consumer<LambdaDescriptor> lambdaConsumer,
- Map<DexType, Set<DexClass>> instantiatedHierarchy,
- Map<DexType, List<LambdaDescriptor>> instantiatedLambdas,
- Predicate<DexProgramClass> isInstantiatedDirectly,
+ Consumer<DexProgramClass> onClass,
+ Consumer<LambdaDescriptor> onLambda,
AppInfo appInfo) {
WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
if (type == appInfo.dexItemFactory().objectType) {
@@ -307,7 +157,7 @@
// If no definition for the type is found, populate the worklist with any
// instantiated subtypes and callback with any lambda instance.
worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(type, Collections.emptySet()));
- instantiatedLambdas.getOrDefault(type, Collections.emptyList()).forEach(lambdaConsumer);
+ instantiatedLambdas.getOrDefault(type, Collections.emptyList()).forEach(onLambda);
} else {
worklist.addIfNotSeen(initialClass);
}
@@ -317,12 +167,287 @@
DexClass clazz = worklist.next();
if (clazz.isProgramClass()) {
DexProgramClass programClass = clazz.asProgramClass();
- if (isInstantiatedDirectly.test(programClass)) {
- subTypeConsumer.accept(programClass);
+ if (isInstantiatedDirectly(programClass)
+ || isInterfaceWithUnknownSubtypeHierarchy(programClass)) {
+ onClass.accept(programClass);
}
}
worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(clazz.type, Collections.emptySet()));
- instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList()).forEach(lambdaConsumer);
+ instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList()).forEach(onLambda);
+ }
+ }
+
+ public static class Builder extends ObjectAllocationInfoCollectionImpl {
+
+ private static class Data {
+
+ private final boolean trackAllocationSites;
+ private final GraphReporter reporter;
+
+ private Data(boolean trackAllocationSites, GraphReporter reporter) {
+ this.trackAllocationSites = trackAllocationSites;
+ this.reporter = reporter;
+ }
+ }
+
+ // Pointer to data valid during the duration of the builder.
+ private Data data;
+
+ private Builder(boolean trackAllocationSites, GraphReporter reporter) {
+ data = new Data(trackAllocationSites, reporter);
+ }
+
+ public ObjectAllocationInfoCollectionImpl build(DexDefinitionSupplier definitions) {
+ assert data != null;
+ if (instantiatedHierarchy == null) {
+ repopulateInstantiatedHierarchy(definitions);
+ }
+ assert validate(definitions);
+ data = null;
+ return this;
+ }
+
+ // Consider a mutation interface that has just the mutation methods.
+ @Override
+ public void mutate(Consumer<Builder> mutator, AppInfo appInfo) {
+ mutator.accept(this);
+ repopulateInstantiatedHierarchy(appInfo);
+ }
+
+ private boolean shouldTrackAllocationSitesForClass(
+ DexProgramClass clazz, InstantiationReason instantiationReason) {
+ if (!data.trackAllocationSites) {
+ return false;
+ }
+ if (instantiationReason != InstantiationReason.NEW_INSTANCE_INSTRUCTION) {
+ // There is an allocation site which is not a new-instance instruction.
+ return false;
+ }
+ if (classesWithoutAllocationSiteTracking.contains(clazz)) {
+ // We already gave up on tracking the allocation sites for `clazz` previously.
+ return false;
+ }
+ // We currently only use allocation site information for instance field value propagation.
+ return !clazz.instanceFields().isEmpty();
+ }
+
+ /**
+ * Records that {@param clazz} is instantiated in {@param context}.
+ *
+ * @return true if {@param clazz} was not instantiated before.
+ */
+ public boolean recordDirectAllocationSite(
+ DexProgramClass clazz,
+ DexEncodedMethod context,
+ InstantiationReason instantiationReason,
+ KeepReason keepReason,
+ AppInfo appInfo) {
+ assert !clazz.isInterface();
+ if (data.reporter != null) {
+ data.reporter.registerClass(clazz, keepReason);
+ }
+ populateInstantiatedHierarchy(appInfo, clazz);
+ if (shouldTrackAllocationSitesForClass(clazz, instantiationReason)) {
+ assert context != null;
+ Set<DexEncodedMethod> allocationSitesForClass =
+ classesWithAllocationSiteTracking.computeIfAbsent(
+ clazz, ignore -> Sets.newIdentityHashSet());
+ allocationSitesForClass.add(context);
+ return allocationSitesForClass.size() == 1;
+ }
+ if (classesWithoutAllocationSiteTracking.add(clazz)) {
+ Set<DexEncodedMethod> allocationSitesForClass =
+ classesWithAllocationSiteTracking.remove(clazz);
+ return allocationSitesForClass == null;
+ }
+ return false;
+ }
+
+ public boolean recordInstantiatedInterface(DexProgramClass iface, AppInfo appInfo) {
+ assert iface.isInterface();
+ assert !iface.isAnnotation();
+ if (interfacesWithUnknownSubtypeHierarchy.add(iface)) {
+ populateInstantiatedHierarchy(appInfo, iface);
+ return true;
+ }
+ return false;
+ }
+
+ public void recordInstantiatedLambdaInterface(
+ DexType iface, LambdaDescriptor lambda, AppInfo appInfo) {
+ instantiatedLambdas.computeIfAbsent(iface, key -> new ArrayList<>()).add(lambda);
+ populateInstantiatedHierarchy(appInfo, iface);
+ }
+
+ private void repopulateInstantiatedHierarchy(DexDefinitionSupplier definitions) {
+ instantiatedHierarchy = new IdentityHashMap<>();
+ classesWithAllocationSiteTracking
+ .keySet()
+ .forEach(clazz -> populateInstantiatedHierarchy(definitions, clazz));
+ classesWithoutAllocationSiteTracking.forEach(
+ clazz -> populateInstantiatedHierarchy(definitions, clazz));
+ interfacesWithUnknownSubtypeHierarchy.forEach(
+ clazz -> populateInstantiatedHierarchy(definitions, clazz));
+ instantiatedLambdas
+ .keySet()
+ .forEach(type -> populateInstantiatedHierarchy(definitions, type));
+ }
+
+ private void populateInstantiatedHierarchy(DexDefinitionSupplier definitions, DexType type) {
+ DexClass clazz = definitions.definitionFor(type);
+ if (clazz != null) {
+ populateInstantiatedHierarchy(definitions, clazz);
+ }
+ }
+
+ private void populateInstantiatedHierarchy(DexDefinitionSupplier definitions, DexClass clazz) {
+ if (clazz.superType != null) {
+ populateInstantiatedHierarchy(definitions, clazz.superType, clazz);
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ populateInstantiatedHierarchy(definitions, iface, clazz);
+ }
+ }
+
+ private void populateInstantiatedHierarchy(
+ DexDefinitionSupplier definitions, DexType type, DexClass subtype) {
+ if (type == definitions.dexItemFactory().objectType) {
+ return;
+ }
+ Set<DexClass> subtypes = instantiatedHierarchy.get(type);
+ if (subtypes != null) {
+ subtypes.add(subtype);
+ return;
+ }
+ // This is the first time an instantiation appears below 'type', recursively populate.
+ subtypes = Sets.newIdentityHashSet();
+ subtypes.add(subtype);
+ instantiatedHierarchy.put(type, subtypes);
+ populateInstantiatedHierarchy(definitions, type);
+ }
+
+ public void markNoLongerInstantiated(DexProgramClass clazz) {
+ classesWithAllocationSiteTracking.remove(clazz);
+ classesWithoutAllocationSiteTracking.remove(clazz);
+ instantiatedHierarchy = null;
+ }
+
+ Builder rewrittenWithLens(
+ ObjectAllocationInfoCollectionImpl objectAllocationInfos,
+ DexDefinitionSupplier definitions,
+ GraphLense lens) {
+ instantiatedHierarchy = null;
+ objectAllocationInfos.classesWithAllocationSiteTracking.forEach(
+ (clazz, allocationSitesForClass) -> {
+ DexType type = lens.lookupType(clazz.type);
+ if (type.isPrimitiveType()) {
+ assert !objectAllocationInfos.hasInstantiatedStrictSubtype(clazz);
+ return;
+ }
+ DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type));
+ assert rewrittenClass != null;
+ assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass);
+ classesWithAllocationSiteTracking.put(
+ rewrittenClass,
+ LensUtils.rewrittenWithRenamedSignature(
+ allocationSitesForClass, definitions, lens));
+ });
+ objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach(
+ clazz -> {
+ DexType type = lens.lookupType(clazz.type);
+ if (type.isPrimitiveType()) {
+ assert !objectAllocationInfos.hasInstantiatedStrictSubtype(clazz);
+ return;
+ }
+ DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type));
+ assert rewrittenClass != null;
+ assert !classesWithAllocationSiteTracking.containsKey(rewrittenClass);
+ assert !classesWithoutAllocationSiteTracking.contains(rewrittenClass);
+ classesWithoutAllocationSiteTracking.add(rewrittenClass);
+ });
+ for (DexProgramClass abstractType :
+ objectAllocationInfos.interfacesWithUnknownSubtypeHierarchy) {
+ DexType type = lens.lookupType(abstractType.type);
+ if (type.isPrimitiveType()) {
+ assert false;
+ continue;
+ }
+ DexProgramClass rewrittenClass = asProgramClassOrNull(definitions.definitionFor(type));
+ assert rewrittenClass != null;
+ assert !interfacesWithUnknownSubtypeHierarchy.contains(rewrittenClass);
+ interfacesWithUnknownSubtypeHierarchy.add(rewrittenClass);
+ }
+ objectAllocationInfos.instantiatedLambdas.forEach(
+ (iface, lambdas) -> {
+ DexType type = lens.lookupType(iface);
+ if (type.isPrimitiveType()) {
+ assert false;
+ return;
+ }
+ assert !instantiatedLambdas.containsKey(type);
+ // TODO(b/150277553): Rewrite lambda descriptor.
+ instantiatedLambdas.put(type, lambdas);
+ });
+ return this;
+ }
+
+ // Validation that all types are linked in the instantiated hierarchy map.
+ boolean validate(DexDefinitionSupplier definitions) {
+ classesWithAllocationSiteTracking.forEach(
+ (clazz, contexts) -> {
+ assert !clazz.isInterface();
+ assert !classesWithoutAllocationSiteTracking.contains(clazz);
+ assert verifyAllSuperTypesAreInHierarchy(definitions, clazz.allImmediateSupertypes());
+ });
+ classesWithoutAllocationSiteTracking.forEach(
+ clazz -> {
+ assert !clazz.isInterface();
+ assert !classesWithAllocationSiteTracking.containsKey(clazz);
+ assert verifyAllSuperTypesAreInHierarchy(definitions, clazz.allImmediateSupertypes());
+ });
+ instantiatedLambdas.forEach(
+ (iface, lambdas) -> {
+ assert !lambdas.isEmpty();
+ DexClass definition = definitions.definitionFor(iface);
+ if (definition != null) {
+ assert definition.isInterface();
+ assert verifyAllSuperTypesAreInHierarchy(
+ definitions, definition.allImmediateSupertypes());
+ }
+ });
+ for (DexProgramClass iface : interfacesWithUnknownSubtypeHierarchy) {
+ verifyAllSuperTypesAreInHierarchy(definitions, iface.allImmediateSupertypes());
+ }
+ instantiatedHierarchy.forEach(
+ (type, subtypes) -> {
+ assert !subtypes.isEmpty();
+ for (DexClass subtype : subtypes) {
+ assert isImmediateSuperType(type, subtype);
+ }
+ });
+ return true;
+ }
+
+ private boolean verifyAllSuperTypesAreInHierarchy(
+ DexDefinitionSupplier definitions, Iterable<DexType> dexTypes) {
+ for (DexType supertype : dexTypes) {
+ assert typeIsInHierarchy(definitions, supertype);
+ }
+ return true;
+ }
+
+ private boolean typeIsInHierarchy(DexDefinitionSupplier definitions, DexType type) {
+ return type == definitions.dexItemFactory().objectType
+ || instantiatedHierarchy.containsKey(type);
+ }
+
+ private boolean isImmediateSuperType(DexType type, DexClass subtype) {
+ for (DexType supertype : subtype.allImmediateSupertypes()) {
+ if (type == supertype) {
+ return true;
+ }
+ }
+ return false;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index e8cae8d..283777f 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -45,6 +45,10 @@
return false;
}
+ public boolean isIncompatibleClassChangeErrorResult() {
+ return false;
+ }
+
/** Returns non-null if isFailedResolution() is true, otherwise null. */
public FailedResolutionResult asFailedResolution() {
return null;
@@ -101,7 +105,7 @@
InstantiatedObject instance, AppInfoWithClassHierarchy appInfo);
public abstract DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo);
+ DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo);
public abstract LookupTarget lookupVirtualDispatchTarget(
LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo);
@@ -119,12 +123,12 @@
assert initialResolutionHolder != null;
assert resolvedHolder != null;
assert resolvedMethod != null;
- assert resolvedHolder.type == resolvedMethod.method.holder;
+ assert resolvedHolder.type == resolvedMethod.holder();
this.resolvedHolder = resolvedHolder;
this.resolvedMethod = resolvedMethod;
this.initialResolutionHolder = initialResolutionHolder;
assert !resolvedMethod.isPrivateMethod()
- || initialResolutionHolder.type == resolvedMethod.method.holder;
+ || initialResolutionHolder.type == resolvedMethod.holder();
}
public DexClass getResolvedHolder() {
@@ -324,7 +328,7 @@
// It appears as if this check is also in place for non-initializer methods too.
// See NestInvokeSpecialMethodAccessWithIntermediateTest.
if ((target.isInstanceInitializer() || target.isPrivateMethod())
- && target.method.holder != symbolicReference.type) {
+ && target.holder() != symbolicReference.type) {
return null;
}
// Runtime exceptions:
@@ -440,7 +444,7 @@
Consumer<DexProgramClass> lambdaInstantiatedConsumer =
subType -> {
subTypeConsumer.accept(subType);
- if (appInfo.hasAnyInstantiatedLambdas(subType)) {
+ if (appInfo.isInstantiatedInterface(subType)) {
hasInstantiatedLambdas.set(true);
}
};
@@ -516,7 +520,7 @@
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+ DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
return lookupVirtualDispatchTarget(dynamicInstance, appInfo, initialResolutionHolder.type);
}
@@ -542,9 +546,7 @@
}
private DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance,
- AppInfoWithClassHierarchy appInfo,
- DexType resolutionHolder) {
+ DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo, DexType resolutionHolder) {
assert appInfo.isSubtype(dynamicInstance.type, resolutionHolder)
: dynamicInstance.type + " is not a subtype of " + resolutionHolder;
// TODO(b/148591377): Enable this assertion.
@@ -567,7 +569,7 @@
}
if (candidate == null || candidate == DexEncodedMethod.SENTINEL) {
// We cannot find a target above the resolved method.
- if (current.type == overrideTarget.method.holder) {
+ if (current.type == overrideTarget.holder()) {
return null;
}
current = current.superType == null ? null : appInfo.definitionFor(current.superType);
@@ -584,7 +586,7 @@
}
private DexClassAndMethod lookupMaximallySpecificDispatchTarget(
- DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+ DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
return appInfo.lookupMaximallySpecificMethod(dynamicInstance, resolvedMethod.method);
}
@@ -646,7 +648,7 @@
}
// For package private methods, a valid override has to be inside the package.
assert resolvedMethod.accessFlags.isPackagePrivate();
- return resolvedMethod.method.holder.isSamePackage(candidate.method.holder);
+ return resolvedMethod.holder().isSamePackage(candidate.holder());
}
}
@@ -702,7 +704,7 @@
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- DexProgramClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
+ DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
return null;
}
@@ -808,6 +810,11 @@
? INSTANCE
: new IncompatibleClassResult(methodsCausingError);
}
+
+ @Override
+ public boolean isIncompatibleClassChangeErrorResult() {
+ return true;
+ }
}
public static class NoSuchMethodResult extends FailedResolutionResult {
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index fdb4dc1..061cbcf 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -127,7 +127,7 @@
}
private boolean hasKotlincClinitAssertionCode(DexEncodedMethod method) {
- if (method.method.holder == dexItemFactory.kotlin.kotlinAssertions) {
+ if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) {
CfCode code = method.getCode().asCfCode();
for (int i = 1; i < code.instructions.size(); i++) {
CfInstruction instruction = code.instructions.get(i - 1);
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index f32143d..7ecd527 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -74,7 +74,7 @@
// Record that the enclosing class is guaranteed to be initialized at the allocation site.
AppInfoWithSubtyping appInfo = appView.appInfo();
- DexType guaranteedToBeInitialized = context.method.holder;
+ DexType guaranteedToBeInitialized = context.holder();
DexType existingGuaranteedToBeInitialized =
mapping.getOrDefault(key, guaranteedToBeInitialized);
mapping.put(
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index 237f9ab..944d680 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -39,6 +39,10 @@
return mergedClasses.containsKey(type);
}
+ public boolean isTarget(DexType type) {
+ return !getSourcesFor(type).isEmpty();
+ }
+
@Override
public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
for (List<DexType> sourcesForTarget : sources.values()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 7167e72..94ee9cf 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -97,7 +97,7 @@
}
public boolean isClassDefinitelyLoadedBeforeInstruction(DexType type, Instruction instruction) {
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
BasicBlock block = instruction.getBlock();
// Visit the instructions in `block` prior to `instruction`.
@@ -335,7 +335,7 @@
if (!resolutionResult.isSingleResolution()) {
return false;
}
- DexType holder = resolutionResult.getSingleTarget().method.holder;
+ DexType holder = resolutionResult.getSingleTarget().holder();
return appView.isSubtype(holder, type).isTrue();
}
@@ -394,7 +394,7 @@
if (!resolutionResult.isSingleResolution()) {
return false;
}
- DexType holder = resolutionResult.getSingleTarget().method.holder;
+ DexType holder = resolutionResult.getSingleTarget().holder();
return appView.isSubtype(holder, type).isTrue();
}
@@ -430,7 +430,7 @@
if (!resolutionResult.isSingleResolution()) {
return false;
}
- DexType holder = resolutionResult.getSingleTarget().method.holder;
+ DexType holder = resolutionResult.getSingleTarget().holder();
return appView.isSubtype(holder, type).isTrue();
}
@@ -505,11 +505,11 @@
enqueue(clazz.type, visited, worklist);
} else if (definition.isDexEncodedField()) {
DexEncodedField field = definition.asDexEncodedField();
- enqueue(field.field.holder, visited, worklist);
+ enqueue(field.holder(), visited, worklist);
} else if (definition.isDexEncodedMethod()) {
assert instruction.isInvokeMethod();
DexEncodedMethod method = definition.asDexEncodedMethod();
- enqueue(method.method.holder, visited, worklist);
+ enqueue(method.holder(), visited, worklist);
enqueueInitializedClassesOnNormalExit(method, instruction.inValues(), visited, worklist);
} else {
assert false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
index 0fc8bd0..9ae6c86 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
@@ -37,7 +37,7 @@
}
if (instr.isInvokeMethod()) {
DexEncodedMethod target =
- instr.asInvokeMethod().lookupSingleTarget(appView, code.method.method.holder);
+ instr.asInvokeMethod().lookupSingleTarget(appView, code.method.holder());
if (target != null && target.getOptimizationInfo().returnValueOnlyDependsOnArguments()) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index f5cd48d..406c9e6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -36,7 +36,7 @@
public static Set<DexType> computeInitializedClassesOnNormalExit(
AppView<AppInfoWithLiveness> appView, IRCode code) {
DominatorTree dominatorTree = new DominatorTree(code, Assumption.MAY_HAVE_UNREACHABLE_BLOCKS);
- Visitor visitor = new Visitor(appView, code.method.method.holder);
+ Visitor visitor = new Visitor(appView, code.method.holder());
for (BasicBlock dominator : dominatorTree.normalExitDominatorBlocks()) {
if (dominator.hasCatchHandlers()) {
// When determining which classes that are guaranteed to be initialized from a given
@@ -115,8 +115,8 @@
public Void handleFieldInstruction(FieldInstruction instruction) {
DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
if (field != null) {
- if (field.field.holder.isClassType()) {
- markInitializedOnNormalExit(field.field.holder);
+ if (field.holder().isClassType()) {
+ markInitializedOnNormalExit(field.holder());
} else {
assert false : "Expected holder of field type to be a class type";
}
@@ -132,7 +132,7 @@
if (method.holder.isClassType()) {
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget != null) {
- markInitializedOnNormalExit(singleTarget.method.holder);
+ markInitializedOnNormalExit(singleTarget.holder());
markInitializedOnNormalExit(
singleTarget.getOptimizationInfo().getInitializedClassesOnNormalExit());
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 9fcee4a..05416df 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -84,7 +84,7 @@
public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) {
this.appView = appView;
this.code = code;
- this.context = code.method.method.holder;
+ this.context = code.method.holder();
}
public boolean valueMayDependOnEnvironment(Value value) {
@@ -433,7 +433,7 @@
if (definition.isStaticGet()) {
StaticGet staticGet = definition.asStaticGet();
DexEncodedField field = appView.appInfo().resolveField(staticGet.getField());
- if (field != null && field.field.holder == context) {
+ if (field != null && field.holder() == context) {
List<StaticPut> finalFieldPuts = computeFinalFieldPuts().get(field);
if (finalFieldPuts == null || finalFieldPuts.size() != 1) {
return false;
@@ -452,7 +452,7 @@
finalFieldPuts = new IdentityHashMap<>();
for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) {
DexEncodedField field = appView.appInfo().resolveField(staticPut.getField());
- if (field != null && field.field.holder == context && field.isFinal()) {
+ if (field != null && field.holder() == context && field.isFinal()) {
finalFieldPuts.computeIfAbsent(field, ignore -> new ArrayList<>()).add(staticPut);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index d5bd10a..75d5423 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -148,7 +148,7 @@
return;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context.holder());
if (singleTarget == null) {
// We just lost track.
abstractInstanceFieldValues.remove(clazz);
@@ -173,7 +173,7 @@
initializationInfo.asArgumentInitializationInfo();
Value argument = invoke.arguments().get(argumentInitializationInfo.getArgumentIndex());
AbstractValue abstractValue =
- entry.getValue().join(argument.getAbstractValue(appView, context.method.holder));
+ entry.getValue().join(argument.getAbstractValue(appView, context.holder()));
assert !abstractValue.isBottom();
if (!abstractValue.isUnknown()) {
entry.setValue(abstractValue);
@@ -294,7 +294,7 @@
if (abstractValue.isUnknown()) {
return true;
}
- assert abstractValue == value.getAbstractValue(appView, context.method.holder);
+ assert abstractValue == value.getAbstractValue(appView, context.holder());
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index af990b1..08989c0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -49,7 +49,7 @@
DexProgramClass clazz,
DexEncodedMethod method) {
assert clazz != null;
- assert clazz.type == method.method.holder;
+ assert clazz.type == method.holder();
this.appView = appView;
this.clazz = clazz;
this.code = code;
@@ -129,7 +129,7 @@
// Then check if any of the instructions that precede the given instruction in the current block
// may read the field.
- DexType context = method.method.holder;
+ DexType context = method.holder();
InstructionIterator instructionIterator = block.iterator();
while (instructionIterator.hasNext()) {
Instruction current = instructionIterator.next();
@@ -164,7 +164,7 @@
* and its transitive predecessors.
*/
private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
- DexType context = method.method.holder;
+ DexType context = method.holder();
Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
while (!worklist.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index 3aec803..58b4194 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -86,7 +86,7 @@
assert appView.enableWholeProgramOptimizations();
assert method.isInstanceInitializer();
- DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass();
if (!appView.options().enableValuePropagationForInstanceFields) {
return EmptyInstanceFieldInitializationInfoCollection.getInstance();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 4d57a5e..e2ab677 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -53,7 +53,7 @@
assert appView.enableWholeProgramOptimizations();
assert method.isClassInitializer();
timing.begin("Analyze class initializer");
- DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass();
new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method)
.computeFieldOptimizationInfo(classInitializerDefaultsResult);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 2a70440..08b29ce 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -130,7 +130,7 @@
*/
public void rewriteCode(DexEncodedMethod method, IRCode code) {
if (method.isClassInitializer()
- && classesWithRemovedExtensionFields.contains(method.method.holder)
+ && classesWithRemovedExtensionFields.contains(method.holder())
&& code.metadata().mayHaveStaticPut()) {
rewriteClassInitializer(code);
}
@@ -204,7 +204,7 @@
return false;
}
- DexClass clazz = appView.definitionFor(encodedField.field.holder);
+ DexClass clazz = appView.definitionFor(encodedField.holder());
if (clazz == null || !clazz.isProgramClass()) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index feca06c..0801f16 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.ir.optimize.inliner.FixedInliningReasonStrategy;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -64,11 +65,16 @@
/** Returns true if an action was deferred. */
public boolean deferDeadProtoBuilders(
- DexProgramClass clazz, DexEncodedMethod context, BooleanSupplier register) {
+ DexProgramClass clazz,
+ DexEncodedMethod context,
+ BooleanSupplier register,
+ Enqueuer enqueuer) {
if (references.isDynamicMethod(context) && references.isGeneratedMessageLiteBuilder(clazz)) {
if (register.getAsBoolean()) {
- assert builders.getOrDefault(clazz, context) == context;
- builders.put(clazz, context);
+ if (enqueuer.getMode().isFinalTreeShaking()) {
+ assert builders.getOrDefault(clazz, context) == context;
+ builders.put(clazz, context);
+ }
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 390f2b2..b2d813a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -99,7 +99,7 @@
* newMessageInfo is still pending.
*/
private void rewriteDynamicMethod(DexEncodedMethod method, IRCode code) {
- DexClass context = appView.definitionFor(method.method.holder);
+ DexClass context = appView.definitionFor(method.holder());
if (context == null || !context.isProgramClass()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index 71750f1..79dea35 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -43,6 +43,7 @@
@Override
public boolean registerConstClass(DexType type) {
if (references.isDynamicMethod(getContextMethod())) {
+ enqueuer.addDeadProtoTypeCandidate(type);
return false;
}
return super.registerConstClass(type);
@@ -58,6 +59,7 @@
@Override
public boolean registerStaticFieldRead(DexField field) {
if (references.isDynamicMethod(getContextMethod())) {
+ enqueuer.addDeadProtoTypeCandidate(field.holder);
return false;
}
return super.registerStaticFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
index de37f38..1321984 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
@@ -36,7 +36,7 @@
@Override
public Reason computeInliningReason(
InvokeMethod invoke, DexEncodedMethod target, DexEncodedMethod context) {
- DexProgramClass enclosingClass = appView.definitionFor(context.method.holder).asProgramClass();
+ DexProgramClass enclosingClass = appView.definitionFor(context.holder()).asProgramClass();
if (references.isAbstractGeneratedMessageLiteBuilder(enclosingClass)
&& invoke.isInvokeSuper()) {
// Aggressively inline invoke-super calls inside the GeneratedMessageLite builders. Such
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index e414f23..d6d2990 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -135,7 +135,7 @@
return;
}
- DexType holder = encodedMethod.method.holder;
+ DexType holder = encodedMethod.holder();
if (seenButNotLiveProtos.containsKey(holder)) {
// The proto is now live instead of dead.
liveProtos.put(holder, seenButNotLiveProtos.remove(holder));
@@ -150,7 +150,7 @@
private void createProtoMessageInfoFromDynamicMethod(
DexEncodedMethod dynamicMethod, Map<DexType, ProtoMessageInfo> protos) {
- DexType holder = dynamicMethod.method.holder;
+ DexType holder = dynamicMethod.holder();
assert !protos.containsKey(holder);
DexClass context = appView.definitionFor(holder);
@@ -254,7 +254,7 @@
for (DexEncodedMethod findLiteExtensionByNumberMethod : findLiteExtensionByNumberMethods) {
IRCode code =
findLiteExtensionByNumberMethod.buildIR(
- appView, appView.appInfo().originFor(findLiteExtensionByNumberMethod.method.holder));
+ appView, appView.appInfo().originFor(findLiteExtensionByNumberMethod.holder()));
for (BasicBlock block : code.blocks(BasicBlock::isReturnBlock)) {
Value returnValue = block.exit().asReturn().returnValue().getAliasedValue();
if (returnValue.isPhi()) {
@@ -275,7 +275,7 @@
continue;
}
- DexProgramClass holder = asProgramClassOrNull(appView.definitionFor(field.field.holder));
+ DexProgramClass holder = asProgramClassOrNull(appView.definitionFor(field.holder()));
if (holder == null) {
assert false;
continue;
@@ -365,7 +365,7 @@
}
DexEncodedMethod dynamicMethod = protoMessageInfo.getDynamicMethod();
- DexProgramClass clazz = appView.definitionFor(dynamicMethod.method.holder).asProgramClass();
+ DexProgramClass clazz = appView.definitionFor(dynamicMethod.holder()).asProgramClass();
for (ProtoFieldInfo protoFieldInfo : protoMessageInfo.getFields()) {
DexEncodedField valueStorage = protoFieldInfo.getValueStorage(appView, protoMessageInfo);
@@ -526,7 +526,7 @@
return;
}
- DexClass clazz = appView.definitionFor(encodedOneOfCaseField.field.holder);
+ DexClass clazz = appView.definitionFor(encodedOneOfCaseField.holder());
if (clazz == null || !clazz.isProgramClass()) {
assert false;
return;
@@ -555,7 +555,7 @@
return;
}
- if (encodedOneOfField.field.holder != encodedOneOfCaseField.field.holder) {
+ if (encodedOneOfField.holder() != encodedOneOfCaseField.holder()) {
assert false;
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
index ac88305..f026295 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
@@ -220,7 +220,7 @@
}
public DexType getType() {
- return dynamicMethod.method.holder;
+ return dynamicMethod.holder();
}
public boolean hasFields() {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
index 3449c30..0d500e9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -39,7 +39,7 @@
*/
public static ClassInitializerSideEffect classInitializerCanBePostponed(
AppView<?> appView, IRCode code) {
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
OptionalBool controlFlowMayDependOnEnvironment = OptionalBool.unknown();
boolean mayHaveSideEffects = false;
@@ -113,7 +113,7 @@
StaticPut staticPut = instruction.asStaticPut();
DexEncodedField field = appView.appInfo().resolveField(staticPut.getField());
if (field == null
- || field.field.holder != context
+ || field.holder() != context
|| environmentAnalysis.valueMayDependOnEnvironment(staticPut.value())
|| instruction.instructionInstanceCanThrow(appView, context).isThrowing()) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index 34a83d1..3f5640d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -72,7 +72,7 @@
Value returnedValue =
code.createValue(classClassType(appView, definitelyNotNull()), debugLocalInfo);
ConstClass instruction = new ConstClass(returnedValue, type);
- assert !instruction.instructionMayHaveSideEffects(appView, code.method.method.holder);
+ assert !instruction.instructionMayHaveSideEffects(appView, code.method.holder());
return instruction;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index ef38879..855694a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -85,7 +85,7 @@
public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
DexEncodedField encodedField = appView.appInfo().resolveField(field);
return isMemberVisibleFromOriginalContext(
- appView, context, encodedField.field.holder, encodedField.accessFlags);
+ appView, context, encodedField.holder(), encodedField.accessFlags);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index dc0b133..0994dc0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -90,7 +90,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index f4a8a14..cd29ec9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -192,7 +192,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 6611bc7..9fb9f27 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -279,7 +279,7 @@
assert super.verifyTypes(appView);
TypeElement inType = src().getType();
- TypeElement outType = outValue().getType();
+ TypeElement outType = getOutType();
if (isAssumeNone() || isAssumeDynamicType()) {
assert inType.isReferenceType() : inType;
assert outType.equals(inType)
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 37d9b9e..52aed30 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -540,6 +540,10 @@
return phis;
}
+ public boolean isEntry() {
+ return getPredecessors().isEmpty();
+ }
+
public boolean isFilled() {
return filled;
}
@@ -1750,7 +1754,7 @@
if (hasMoveException) {
// Remove the move-exception instruction.
move = entry().asMoveException();
- exceptionTypeLattice = move.outValue().getType();
+ exceptionTypeLattice = move.getOutType();
exceptionType = move.getExceptionType();
assert move.getDebugValues().isEmpty();
getInstructions().remove(0);
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 4e5c923..1537f85 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -228,7 +228,7 @@
throw new IllegalStateException();
}
- assert !current.hasOutValue() || current.outValue().getType().isInt();
+ assert !current.hasOutValue() || current.getOutType().isInt();
// Replace the instruction by const-number.
ConstNumber constNumber = code.createIntConstant(value, current.getLocalInfo());
@@ -250,7 +250,7 @@
// Replace the instruction by static-get.
TypeElement newType = TypeElement.fromDexType(field.type, maybeNull(), appView);
- TypeElement oldType = current.hasOutValue() ? current.outValue().getType() : null;
+ TypeElement oldType = current.getOutType();
Value value = code.createValue(newType, current.getLocalInfo());
StaticGet staticGet = new StaticGet(value, field);
for (Value inValue : current.inValues()) {
@@ -495,8 +495,8 @@
Set<BasicBlock> blocksToRemove,
DexType downcast) {
assert blocksToRemove != null;
- DexType codeHolder = code.method.method.holder;
- DexType inlineeHolder = inlinee.method.method.holder;
+ DexType codeHolder = code.method.holder();
+ DexType inlineeHolder = inlinee.method.holder();
if (codeHolder != inlineeHolder && inlinee.method.isOnlyInlinedIntoNestMembers()) {
// Should rewrite private calls to virtual calls.
assert NestUtils.sameNest(codeHolder, inlineeHolder, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 2d7e409..da952bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -171,7 +171,7 @@
assert inType.isPreciseType();
- TypeElement outType = outValue().getType();
+ TypeElement outType = getOutType();
TypeElement castType = TypeElement.fromDexType(getType(), inType.nullability(), appView);
if (inType.lessThanOrEqual(castType, appView)) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index e77f315..bb5788b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -44,10 +44,7 @@
public static ConstClass copyOf(IRCode code, ConstClass original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
@@ -142,7 +139,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 996bf38..a390b3f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -38,10 +38,7 @@
public static ConstMethodHandle copyOf(IRCode code, ConstMethodHandle original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index 6cd4d0b..774475d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -38,10 +38,7 @@
public static ConstMethodType copyOf(IRCode code, ConstMethodType original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 2683b66..06aef0a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -55,10 +55,7 @@
public static ConstNumber copyOf(IRCode code, ConstNumber original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
@@ -251,7 +248,7 @@
@Override
public String toString() {
if (outValue != null) {
- return super.toString() + " " + value + " (" + outValue().getType() + ")";
+ return super.toString() + " " + value + " (" + getOutType() + ")";
} else {
return super.toString() + " " + value + " (dead)";
}
@@ -316,13 +313,13 @@
@Override
public TypeElement evaluate(AppView<?> appView) {
- return outValue().getType();
+ return getOutType();
}
@Override
public boolean verifyTypes(AppView<?> appView) {
assert super.verifyTypes(appView);
- assert !isZero() || outValue().getType().isPrimitiveType() || outValue().getType().isNullType();
+ assert !isZero() || getOutType().isPrimitiveType() || getOutType().isNullType();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index c98d8b7..a90a67e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -42,10 +42,7 @@
public static ConstString copyOf(IRCode code, ConstString original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index 5884660..f8a36db 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -79,7 +79,7 @@
@Override
public boolean verifyTypes(AppView<?> appView) {
super.verifyTypes(appView);
- assert src().getType().lessThanOrEqual(outValue().getType(), appView);
+ assert src().getType().lessThanOrEqual(getOutType(), appView);
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 70ebd80..bc29749 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -48,10 +48,7 @@
public static DexItemBasedConstString copyOf(IRCode code, DexItemBasedConstString original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index d7e0067..18f8326 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -232,7 +232,7 @@
assert isFieldGet();
DexEncodedField field = appView.appInfo().resolveField(getField());
if (field != null) {
- DexClass holder = appView.definitionFor(field.field.holder);
+ DexClass holder = appView.definitionFor(field.holder());
if (holder != null && holder.isLibraryClass() && field.isStatic() && field.isFinal()) {
return appView.abstractValueFactory().createSingleFieldValue(field.field);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index 5c488c4..e46af2c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -116,6 +116,10 @@
return result;
}
+ public boolean mayHaveInitClass() {
+ return get(Opcodes.INIT_CLASS);
+ }
+
public boolean mayHaveInstanceGet() {
return get(Opcodes.INSTANCE_GET);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index da0592d..42068de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -199,14 +199,14 @@
public BasicBlock targetFromCondition(ConstNumber value) {
assert isZeroTest();
- assert verifyTypeCompatible(value.outValue().getType(), type);
+ assert verifyTypeCompatible(value.getOutType(), type);
return targetFromCondition(Long.signum(value.getRawValue()));
}
public BasicBlock targetFromCondition(ConstNumber left, ConstNumber right) {
assert !isZeroTest();
assert left.outType() == right.outType();
- assert verifyTypeCompatible(left.outValue().getType(), type);
+ assert verifyTypeCompatible(left.getOutType(), type);
return targetFromCondition(Long.signum(left.getRawValue() - right.getRawValue()));
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 6edc6da..0740db4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -127,7 +127,7 @@
// * IncompatibleClassChangeError (instance-* instruction for static fields)
// * IllegalAccessError (not visible from the access context)
// * NullPointerException (null receiver)
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 116a3e6..58bcd28 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -142,7 +142,7 @@
// * IllegalAccessError (not visible from the access context)
// * NullPointerException (null receiver)
// * not read at all
- boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.method.holder);
+ boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.holder());
assert appView.enableWholeProgramOptimizations() || haveSideEffects
: "Expected instance-put instruction to have side effects in D8";
return !haveSideEffects;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 654c2e8..b280728 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -593,7 +593,7 @@
/** Returns true is this instruction can be treated as dead code if its outputs are not used. */
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
// TODO(b/129530569): instructions with fine-grained side effect analysis may use:
- // return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ // return !instructionMayHaveSideEffects(appView, code.method.holder());
return !instructionInstanceCanThrow();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index ddc8952..d6aaf36 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -190,12 +190,12 @@
// Verify that the target method is accessible in the current context.
if (!isMemberVisibleFromOriginalContext(
- appView, context, target.method.holder, target.accessFlags)) {
+ appView, context, target.holder(), target.accessFlags)) {
return true;
}
// Verify that the target method does not have side-effects.
- DexClass clazz = appView.definitionFor(target.method.holder);
+ DexClass clazz = appView.definitionFor(target.holder());
if (clazz == null) {
assert false : "Expected to be able to find the enclosing class of a method definition";
return true;
@@ -218,7 +218,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
DexEncodedMethod method = code.method;
- if (instructionMayHaveSideEffects(appView, method.method.holder)) {
+ if (instructionMayHaveSideEffects(appView, method.holder())) {
return false;
}
@@ -236,7 +236,7 @@
if (appView.dexItemFactory().isConstructor(invoke.getInvokedMethod())
&& invoke.getReceiver() == getReceiver()) {
// If another constructor call than `this` is found, then it must not have side effects.
- if (invoke.instructionMayHaveSideEffects(appView, method.method.holder)) {
+ if (invoke.instructionMayHaveSideEffects(appView, method.holder())) {
return false;
}
if (otherInitCalls == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 9b708bc..63f6a89 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -118,9 +118,7 @@
methodTarget -> {
DexEncodedMethod target = methodTarget.getMethod();
if (target == refinedTarget
- || appView
- .isSubtype(target.method.holder, refinedReceiverType)
- .isPossiblyTrue()) {
+ || appView.isSubtype(target.holder(), refinedReceiverType).isPossiblyTrue()) {
result.add(target);
}
},
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index a71d185..41fac8c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -184,7 +184,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 7992a1c..b285dd6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -197,7 +197,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 2d29972..01a6a76 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -183,7 +183,7 @@
// Verify that the target method is accessible in the current context.
if (!isMemberVisibleFromOriginalContext(
- appView, context, target.method.holder, target.accessFlags)) {
+ appView, context, target.holder(), target.accessFlags)) {
return true;
}
@@ -200,12 +200,14 @@
return false;
}
- return target.method.holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized
- // already.
- type -> appView.isSubtype(context, type).isTrue(),
- Sets.newIdentityHashSet());
+ return target
+ .holder()
+ .classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized
+ // already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet());
}
return true;
@@ -213,6 +215,6 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 711c911..c30a8cb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -180,7 +180,7 @@
// Verify that the target method is accessible in the current context.
if (!isMemberVisibleFromOriginalContext(
- appView, context, target.method.holder, target.accessFlags)) {
+ appView, context, target.holder(), target.accessFlags)) {
return true;
}
@@ -200,6 +200,6 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index adcc848..24e831c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -73,7 +73,7 @@
@Override
public String toString() {
- return super.toString() + " (" + outValue().getType() + ")";
+ return super.toString() + " (" + getOutType() + ")";
}
@Override
@@ -128,7 +128,7 @@
super.verifyTypes(appView);
// DebugLocalWrite defines it's own verification of types but should be allowed to call super.
if (!this.isDebugLocalWrite()) {
- assert src().getType().equals(outValue().getType());
+ assert src().getType().equals(getOutType());
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index 2211a59..87643df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -151,6 +151,6 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index d8acd0b..1090554 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -196,7 +196,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
public void markNoSpilling() {
@@ -226,7 +226,7 @@
@Override
public boolean verifyTypes(AppView<?> appView) {
- TypeElement type = outValue().getType();
+ TypeElement type = getOutType();
assert type.isClassType();
assert type.asClassType().getClassType() == clazz || appView.options().testing.allowTypeErrors;
assert type.isDefinitelyNotNull();
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 2c246f92..84a86f2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -38,10 +38,7 @@
public static StaticGet copyOf(IRCode code, StaticGet original) {
Value newValue =
- new Value(
- code.valueNumberGenerator.next(),
- original.outValue().getType(),
- original.getLocalInfo());
+ new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
@@ -156,7 +153,7 @@
// * IncompatibleClassChangeError (static-* instruction for instance fields)
// * IllegalAccessError (not visible from the access context)
// * side-effects in <clinit>
- return !instructionMayHaveSideEffects(appView, code.method.method.holder);
+ return !instructionMayHaveSideEffects(appView, code.method.holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 16c102b..7249d94 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -139,7 +139,7 @@
// * IllegalAccessError (not visible from the access context)
// * side-effects in <clinit>
// * not read _globally_
- boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.method.holder);
+ boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.holder());
assert appView.enableWholeProgramOptimizations() || haveSideEffects
: "Expected static-put instruction to have side effects in D8";
return !haveSideEffects;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index e84f64a..57c923c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -826,7 +826,7 @@
builder.append("(");
if (isConstant && definition.asConstNumber().outValue != null) {
ConstNumber constNumber = definition.asConstNumber();
- if (constNumber.outValue().getType().isSinglePrimitive()) {
+ if (constNumber.getOutType().isSinglePrimitive()) {
builder.append((int) constNumber.getRawValue());
} else {
builder.append(constNumber.getRawValue());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 6ff7e2f..18b62e6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -178,7 +178,7 @@
if (singleTarget != null) {
assert !source.accessFlags.isBridge() || singleTarget != currentMethod.method;
DexProgramClass clazz =
- asProgramClassOrNull(appView.definitionFor(singleTarget.method.holder));
+ asProgramClassOrNull(appView.definitionFor(singleTarget.holder()));
if (clazz != null) {
// For static invokes, the class could be initialized.
if (type == Invoke.Type.STATIC) {
@@ -253,8 +253,7 @@
return;
}
- DexProgramClass clazz =
- asProgramClassOrNull(appView.definitionFor(encodedField.field.holder));
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(encodedField.holder()));
if (clazz == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index ae3e7cd..74f24c8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -328,7 +328,7 @@
}
}
return new CfCode(
- method.method.holder,
+ method.holder(),
stackHeightTracker.maxHeight,
registerAllocator.registersUsed(),
instructions,
@@ -386,7 +386,7 @@
|| constNumber == null
|| add == null
|| store == null
- || constNumber.outValue().getType() != TypeElement.getInt()) {
+ || constNumber.getOutType() != TypeElement.getInt()) {
it.next();
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 1f1d2e5..f13653a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -427,7 +427,7 @@
private void buildMethodEnterSynchronization(IRBuilder builder) {
assert needsGeneratedMethodSynchronization;
currentlyGeneratingMethodSynchronization = true;
- DexType type = method.method.holder;
+ DexType type = method.holder();
int monitorRegister;
if (isStatic()) {
monitorRegister = state.push(type).register;
@@ -633,7 +633,7 @@
return ((CfNew) instruction).getType();
}
if (type.isUninitializedThis()) {
- return method.method.holder;
+ return method.holder();
}
assert type.isTop();
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index a4ca85a..6e47281 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -1404,7 +1404,7 @@
@Override
public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
Move move = getMove();
- TypeElement moveType = move.outValue().getType();
+ TypeElement moveType = move.getOutType();
int src = srcRegister(builder);
int dest = destRegister(builder);
Instruction instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 993a69c..35250d1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -530,7 +530,7 @@
int argumentIndex = 0;
if (!method.isStatic()) {
- writeCallback.accept(register, method.method.holder);
+ writeCallback.accept(register, method.holder());
addThisArgument(register);
argumentIndex++;
register++;
@@ -932,7 +932,7 @@
void addThisArgument(int register) {
boolean receiverCouldBeNull = context != null && context != method;
Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull();
- TypeElement receiverType = TypeElement.fromDexType(method.method.holder, nullability, appView);
+ TypeElement receiverType = TypeElement.fromDexType(method.holder(), nullability, appView);
addThisArgument(register, receiverType);
}
@@ -1463,14 +1463,14 @@
// therefore we use an invoke-direct instead. We need to do this as the Android Runtime
// will not allow invoke-virtual of a private method.
DexMethod invocationMethod = (DexMethod) item;
- DexType holderType = method.method.holder;
+ DexType holderType = method.holder();
if (invocationMethod.holder == holderType) {
DexClass holderClass = appView.definitionFor(holderType);
assert holderClass != null && holderClass.isProgramClass();
if (holderClass != null) {
DexEncodedMethod directTarget = holderClass.lookupDirectMethod(invocationMethod);
if (directTarget != null && !directTarget.isStatic()) {
- assert invocationMethod.holder == directTarget.method.holder;
+ assert invocationMethod.holder == directTarget.holder();
type = Type.DIRECT;
}
}
@@ -2332,7 +2332,7 @@
private void addInstruction(Instruction ir, Position position) {
assert verifyOutValueType(ir);
- hasImpreciseValues |= ir.outValue() != null && !ir.outValue().getType().isPreciseType();
+ hasImpreciseValues |= ir.outValue() != null && !ir.getOutType().isPreciseType();
ir.setPosition(position);
attachLocalValues(ir);
currentBlock.add(ir, metadata);
@@ -2363,13 +2363,11 @@
}
private boolean verifyOutValueType(Instruction ir) {
- assert ir.outValue() == null
- || ir.isArrayGet()
- || ir.evaluate(appView) == ir.outValue().getType();
+ assert ir.outValue() == null || ir.isArrayGet() || ir.evaluate(appView) == ir.getOutType();
assert ir.outValue() == null
|| !ir.isArrayGet()
- || ir.evaluate(appView) == ir.outValue().getType()
- || (ir.outValue().getType().isBottom() && ir.evaluate(appView).isReferenceType());
+ || ir.evaluate(appView) == ir.getOutType()
+ || (ir.getOutType().isBottom() && ir.evaluate(appView).isReferenceType());
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index cae0c84..e29a1c1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -94,6 +94,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -181,7 +182,7 @@
OptimizationFeedbackSimple.getInstance();
private DexString highestSortingString;
- private List<com.android.tools.r8.utils.Action> onWaveDoneActions = null;
+ private List<Action> onWaveDoneActions = null;
private final List<DexString> neverMergePrefixes;
boolean seenNotNeverMergePrefix = false;
@@ -609,10 +610,10 @@
if (appView.options().enableNeverMergePrefixes) {
for (DexString neverMergePrefix : neverMergePrefixes) {
// Synthetic classes will always be merged.
- if (method.method.holder.isD8R8SynthesizedClassType()) {
+ if (method.holder().isD8R8SynthesizedClassType()) {
continue;
}
- if (method.method.holder.descriptor.startsWith(neverMergePrefix)) {
+ if (method.holder().descriptor.startsWith(neverMergePrefix)) {
seenNeverMergePrefix = true;
} else {
seenNotNeverMergePrefix = true;
@@ -904,7 +905,7 @@
ThreadUtils.processItems(
methods,
method -> {
- IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder));
+ IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder()));
assert code != null;
assert !method.getCode().isOutlineCode();
// Instead of repeating all the optimizations of rewriteCode(), only run the
@@ -928,7 +929,7 @@
}
private void forEachSynthesizedServiceLoaderMethod(DexEncodedMethod method) {
- IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder));
+ IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder()));
assert code != null;
codeRewriter.rewriteMoveResult(code);
removeDeadCodeAndFinalizeIR(
@@ -1006,7 +1007,7 @@
count++;
result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name));
- } while (appView.definitionFor(result) != null);
+ } while (appView.appInfo().definitionForWithoutExistenceAssert(result) != null);
return result;
}
@@ -1085,7 +1086,7 @@
private Timing rewriteCode(
DexEncodedMethod method, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
- Origin origin = appView.appInfo().originFor(method.method.holder);
+ Origin origin = appView.appInfo().originFor(method.holder());
return ExceptionUtils.withOriginAttachmentHandler(
origin,
new MethodPosition(method.method),
@@ -1147,6 +1148,10 @@
codeRewriter.simplifyDebugLocals(code);
}
+ if (enumUnboxer != null && methodProcessor.isPost()) {
+ enumUnboxer.rewriteCode(code);
+ }
+
if (appView.graphLense().hasCodeRewritings()) {
assert lensCodeRewriter != null;
timing.begin("Lens rewrite");
@@ -1154,10 +1159,6 @@
timing.end();
}
- if (enumUnboxer != null && methodProcessor.isPost()) {
- enumUnboxer.rewriteCode(code);
- }
-
if (method.isProcessed()) {
assert !appView.enableWholeProgramOptimizations()
|| !appView.appInfo().withLiveness().neverReprocess.contains(method.method);
@@ -1215,7 +1216,7 @@
if (memberValuePropagation != null) {
timing.begin("Propagate member values");
- memberValuePropagation.rewriteWithConstantValues(code, method.method.holder);
+ memberValuePropagation.rewriteWithConstantValues(code, method.holder());
timing.end();
}
@@ -1284,7 +1285,7 @@
if (devirtualizer != null) {
assert code.verifyTypes(appView);
timing.begin("Devirtualize invoke interface");
- devirtualizer.devirtualizeInvokeInterface(code, method.method.holder);
+ devirtualizer.devirtualizeInvokeInterface(code, method.holder());
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index bb541cf..7daa4a1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -7,6 +7,27 @@
import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_METHOD_HANDLE;
+import static com.android.tools.r8.ir.code.Opcodes.INIT_CLASS;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_OF;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_MULTI_NEW_ARRAY;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_POLYMORPHIC;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.MOVE_EXCEPTION;
+import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY;
+import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -86,7 +107,7 @@
private Value makeOutValue(Instruction insn, IRCode code) {
if (insn.outValue() != null) {
- TypeElement oldType = insn.outValue().getType();
+ TypeElement oldType = insn.getOutType();
TypeElement newType =
oldType.fixupClassTypeReferences(appView.graphLense()::lookupType, appView);
return code.createValue(newType, insn.getLocalInfo());
@@ -114,291 +135,370 @@
InstructionListIterator iterator = block.listIterator(code);
while (iterator.hasNext()) {
Instruction current = iterator.next();
- if (current.isInvokeCustom()) {
- InvokeCustom invokeCustom = current.asInvokeCustom();
- DexCallSite callSite = invokeCustom.getCallSite();
- DexCallSite newCallSite = rewriteCallSite(callSite, method);
- if (newCallSite != callSite) {
- Value newOutValue = makeOutValue(invokeCustom, code);
- InvokeCustom newInvokeCustom =
- new InvokeCustom(newCallSite, newOutValue, invokeCustom.inValues());
- iterator.replaceCurrentInstruction(newInvokeCustom);
- if (newOutValue != null && newOutValue.getType() != invokeCustom.outValue().getType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
- }
- }
- } else if (current.isConstMethodHandle()) {
- DexMethodHandle handle = current.asConstMethodHandle().getValue();
- DexMethodHandle newHandle = rewriteDexMethodHandle(
- handle, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
- if (newHandle != handle) {
- Value newOutValue = makeOutValue(current, code);
- iterator.replaceCurrentInstruction(new ConstMethodHandle(newOutValue, newHandle));
- if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
- }
- }
- } else if (current.isInitClass()) {
- InitClass initClass = current.asInitClass();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- initClass.getClassValue(), (t, v) -> new InitClass(v, t));
- } else if (current.isInvokeMethod()) {
- InvokeMethod invoke = current.asInvokeMethod();
- DexMethod invokedMethod = invoke.getInvokedMethod();
- DexType invokedHolder = invokedMethod.holder;
- if (invokedHolder.isArrayType()) {
- DexType baseType = invokedHolder.toBaseType(factory);
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- baseType,
- (t, v) -> {
- DexType mappedHolder = invokedHolder.replaceBaseType(t, factory);
- // Just reuse proto and name, as no methods on array types cant be renamed nor
- // change signature.
- DexMethod actualTarget =
- factory.createMethod(
- mappedHolder, invokedMethod.proto, invokedMethod.name);
- return Invoke.create(VIRTUAL, actualTarget, null, v, invoke.inValues());
- });
- continue;
- }
- if (!invokedHolder.isClassType()) {
- assert false;
- continue;
- }
- if (invoke.isInvokeDirect()) {
- checkInvokeDirect(method.method, invoke.asInvokeDirect());
- }
- GraphLenseLookupResult lenseLookup =
- graphLense.lookupMethod(invokedMethod, method.method, invoke.getType());
- DexMethod actualTarget = lenseLookup.getMethod();
- Invoke.Type actualInvokeType = lenseLookup.getType();
- if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) {
- RewrittenPrototypeDescription prototypeChanges =
- graphLense.lookupPrototypeChanges(actualTarget);
-
- List<Value> newInValues;
- ArgumentInfoCollection argumentInfoCollection =
- prototypeChanges.getArgumentInfoCollection();
- if (argumentInfoCollection.isEmpty()) {
- newInValues = invoke.inValues();
- } else {
- if (argumentInfoCollection.hasRemovedArguments()) {
- if (Log.ENABLED) {
- Log.info(
- getClass(),
- "Invoked method "
- + invokedMethod.toSourceString()
- + " with "
- + argumentInfoCollection.numberOfRemovedArguments()
- + " arguments removed");
- }
- }
- newInValues = new ArrayList<>(actualTarget.proto.parameters.size());
- for (int i = 0; i < invoke.inValues().size(); i++) {
- ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i);
- if (argumentInfo.isRewrittenTypeInfo()) {
- RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo();
- Value rewrittenValue =
- rewriteValueIfDefault(
- code,
- iterator,
- argInfo.getOldType(),
- argInfo.getNewType(),
- invoke.inValues().get(i));
- newInValues.add(rewrittenValue);
- } else if (!argumentInfo.isRemovedArgumentInfo()) {
- newInValues.add(invoke.inValues().get(i));
+ switch (current.opcode()) {
+ case INVOKE_CUSTOM:
+ {
+ InvokeCustom invokeCustom = current.asInvokeCustom();
+ DexCallSite callSite = invokeCustom.getCallSite();
+ DexCallSite newCallSite = rewriteCallSite(callSite, method);
+ if (newCallSite != callSite) {
+ Value newOutValue = makeOutValue(invokeCustom, code);
+ InvokeCustom newInvokeCustom =
+ new InvokeCustom(newCallSite, newOutValue, invokeCustom.inValues());
+ iterator.replaceCurrentInstruction(newInvokeCustom);
+ if (newOutValue != null && newOutValue.getType() != invokeCustom.getOutType()) {
+ affectedPhis.addAll(newOutValue.uniquePhiUsers());
}
}
}
+ break;
- ConstInstruction constantReturnMaterializingInstruction = null;
- if (prototypeChanges.hasBeenChangedToReturnVoid(appView) && invoke.outValue() != null) {
- constantReturnMaterializingInstruction =
- prototypeChanges.getConstantReturn(code, invoke.getPosition());
- if (invoke.outValue().hasLocalInfo()) {
- constantReturnMaterializingInstruction
- .outValue()
- .setLocalInfo(invoke.outValue().getLocalInfo());
- }
- invoke.outValue().replaceUsers(constantReturnMaterializingInstruction.outValue());
- if (graphLense.lookupType(invoke.getReturnType()) != invoke.getReturnType()) {
- affectedPhis.addAll(
- constantReturnMaterializingInstruction.outValue().uniquePhiUsers());
+ case CONST_METHOD_HANDLE:
+ {
+ DexMethodHandle handle = current.asConstMethodHandle().getValue();
+ DexMethodHandle newHandle =
+ rewriteDexMethodHandle(handle, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+ if (newHandle != handle) {
+ Value newOutValue = makeOutValue(current, code);
+ iterator.replaceCurrentInstruction(new ConstMethodHandle(newOutValue, newHandle));
+ if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ }
}
}
+ break;
- Value newOutValue =
- prototypeChanges.hasBeenChangedToReturnVoid(appView)
- ? null
- : makeOutValue(invoke, code);
-
- if (prototypeChanges.hasExtraNullParameter()) {
- iterator.previous();
- Value extraNullValue = iterator.insertConstNullInstruction(code, appView.options());
- iterator.next();
- newInValues.add(extraNullValue);
+ case INIT_CLASS:
+ {
+ InitClass initClass = current.asInitClass();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ initClass.getClassValue(), (t, v) -> new InitClass(v, t));
}
+ break;
- assert newInValues.size()
- == actualTarget.proto.parameters.size() + (actualInvokeType == STATIC ? 0 : 1);
+ case INVOKE_DIRECT:
+ case INVOKE_INTERFACE:
+ case INVOKE_POLYMORPHIC:
+ case INVOKE_STATIC:
+ case INVOKE_SUPER:
+ case INVOKE_VIRTUAL:
+ {
+ InvokeMethod invoke = current.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexType invokedHolder = invokedMethod.holder;
+ if (invokedHolder.isArrayType()) {
+ DexType baseType = invokedHolder.toBaseType(factory);
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ baseType,
+ (t, v) -> {
+ DexType mappedHolder = invokedHolder.replaceBaseType(t, factory);
+ // Just reuse proto and name, as no methods on array types cant be renamed
+ // nor change signature.
+ DexMethod actualTarget =
+ factory.createMethod(
+ mappedHolder, invokedMethod.proto, invokedMethod.name);
+ return Invoke.create(VIRTUAL, actualTarget, null, v, invoke.inValues());
+ });
+ continue;
+ }
+ if (!invokedHolder.isClassType()) {
+ assert false;
+ continue;
+ }
+ if (invoke.isInvokeDirect()) {
+ checkInvokeDirect(method.method, invoke.asInvokeDirect());
+ }
+ GraphLenseLookupResult lenseLookup =
+ graphLense.lookupMethod(invokedMethod, method.method, invoke.getType());
+ DexMethod actualTarget = lenseLookup.getMethod();
+ Invoke.Type actualInvokeType = lenseLookup.getType();
+ if (actualTarget != invokedMethod || invoke.getType() != actualInvokeType) {
+ RewrittenPrototypeDescription prototypeChanges =
+ graphLense.lookupPrototypeChanges(actualTarget);
- Invoke newInvoke =
- Invoke.create(actualInvokeType, actualTarget, null, newOutValue, newInValues);
- iterator.replaceCurrentInstruction(newInvoke);
- if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
- }
+ List<Value> newInValues;
+ ArgumentInfoCollection argumentInfoCollection =
+ prototypeChanges.getArgumentInfoCollection();
+ if (argumentInfoCollection.isEmpty()) {
+ newInValues = invoke.inValues();
+ } else {
+ if (argumentInfoCollection.hasRemovedArguments()) {
+ if (Log.ENABLED) {
+ Log.info(
+ getClass(),
+ "Invoked method "
+ + invokedMethod.toSourceString()
+ + " with "
+ + argumentInfoCollection.numberOfRemovedArguments()
+ + " arguments removed");
+ }
+ }
+ newInValues = new ArrayList<>(actualTarget.proto.parameters.size());
+ for (int i = 0; i < invoke.inValues().size(); i++) {
+ ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i);
+ if (argumentInfo.isRewrittenTypeInfo()) {
+ RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo();
+ Value rewrittenValue =
+ rewriteValueIfDefault(
+ code,
+ iterator,
+ argInfo.getOldType(),
+ argInfo.getNewType(),
+ invoke.inValues().get(i));
+ newInValues.add(rewrittenValue);
+ } else if (!argumentInfo.isRemovedArgumentInfo()) {
+ newInValues.add(invoke.inValues().get(i));
+ }
+ }
+ }
- if (constantReturnMaterializingInstruction != null) {
- if (block.hasCatchHandlers()) {
- // Split the block to ensure no instructions after throwing instructions.
- iterator
- .split(code, blocks)
- .listIterator(code)
- .add(constantReturnMaterializingInstruction);
- } else {
- iterator.add(constantReturnMaterializingInstruction);
+ ConstInstruction constantReturnMaterializingInstruction = null;
+ if (prototypeChanges.hasBeenChangedToReturnVoid(appView)
+ && invoke.outValue() != null) {
+ constantReturnMaterializingInstruction =
+ prototypeChanges.getConstantReturn(code, invoke.getPosition());
+ if (invoke.outValue().hasLocalInfo()) {
+ constantReturnMaterializingInstruction
+ .outValue()
+ .setLocalInfo(invoke.outValue().getLocalInfo());
+ }
+ invoke.outValue().replaceUsers(constantReturnMaterializingInstruction.outValue());
+ if (graphLense.lookupType(invoke.getReturnType()) != invoke.getReturnType()) {
+ affectedPhis.addAll(
+ constantReturnMaterializingInstruction.outValue().uniquePhiUsers());
+ }
+ }
+
+ Value newOutValue =
+ prototypeChanges.hasBeenChangedToReturnVoid(appView)
+ ? null
+ : makeOutValue(invoke, code);
+
+ if (prototypeChanges.hasExtraNullParameter()) {
+ iterator.previous();
+ Value extraNullValue =
+ iterator.insertConstNullInstruction(code, appView.options());
+ iterator.next();
+ newInValues.add(extraNullValue);
+ }
+
+ assert newInValues.size()
+ == actualTarget.proto.parameters.size() + (actualInvokeType == STATIC ? 0 : 1);
+
+ Invoke newInvoke =
+ Invoke.create(actualInvokeType, actualTarget, null, newOutValue, newInValues);
+ iterator.replaceCurrentInstruction(newInvoke);
+ if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ }
+
+ if (constantReturnMaterializingInstruction != null) {
+ if (block.hasCatchHandlers()) {
+ // Split the block to ensure no instructions after throwing instructions.
+ iterator
+ .split(code, blocks)
+ .listIterator(code)
+ .add(constantReturnMaterializingInstruction);
+ } else {
+ iterator.add(constantReturnMaterializingInstruction);
+ }
+ }
+
+ DexType actualReturnType = actualTarget.proto.returnType;
+ DexType expectedReturnType = graphLense.lookupType(invokedMethod.proto.returnType);
+ if (newInvoke.outValue() != null && actualReturnType != expectedReturnType) {
+ throw new Unreachable(
+ "Unexpected need to insert a cast. Possibly related to resolving"
+ + " b/79143143.\n"
+ + invokedMethod
+ + " type changed from "
+ + expectedReturnType
+ + " to "
+ + actualReturnType);
+ }
}
}
+ break;
- DexType actualReturnType = actualTarget.proto.returnType;
- DexType expectedReturnType = graphLense.lookupType(invokedMethod.proto.returnType);
- if (newInvoke.outValue() != null && actualReturnType != expectedReturnType) {
- throw new Unreachable(
- "Unexpected need to insert a cast. Possibly related to resolving b/79143143.\n"
- + invokedMethod
- + " type changed from " + expectedReturnType
- + " to " + actualReturnType);
+ case INSTANCE_GET:
+ {
+ InstanceGet instanceGet = current.asInstanceGet();
+ DexField field = instanceGet.getField();
+ DexField actualField = graphLense.lookupField(field);
+ DexMethod replacementMethod =
+ graphLense.lookupGetFieldForMethod(actualField, method.method);
+ if (replacementMethod != null) {
+ Value newOutValue = makeOutValue(current, code);
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(replacementMethod, newOutValue, current.inValues()));
+ if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ affectedPhis.addAll(current.outValue().uniquePhiUsers());
+ }
+ } else if (actualField != field) {
+ Value newOutValue = makeOutValue(instanceGet, code);
+ iterator.replaceCurrentInstruction(
+ new InstanceGet(newOutValue, instanceGet.object(), actualField));
+ if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ }
+ }
}
- }
- } else if (current.isInstanceGet()) {
- InstanceGet instanceGet = current.asInstanceGet();
- DexField field = instanceGet.getField();
- DexField actualField = graphLense.lookupField(field);
- DexMethod replacementMethod =
- graphLense.lookupGetFieldForMethod(actualField, method.method);
- if (replacementMethod != null) {
- Value newOutValue = makeOutValue(current, code);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, newOutValue, current.inValues()));
- if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) {
- affectedPhis.addAll(current.outValue().uniquePhiUsers());
+ break;
+
+ case INSTANCE_PUT:
+ {
+ InstancePut instancePut = current.asInstancePut();
+ DexField field = instancePut.getField();
+ DexField actualField = graphLense.lookupField(field);
+ DexMethod replacementMethod =
+ graphLense.lookupPutFieldForMethod(actualField, method.method);
+ if (replacementMethod != null) {
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(replacementMethod, null, current.inValues()));
+ } else if (actualField != field) {
+ Value rewrittenValue =
+ rewriteValueIfDefault(
+ code, iterator, field.type, actualField.type, instancePut.value());
+ InstancePut newInstancePut =
+ InstancePut.createPotentiallyInvalid(
+ actualField, instancePut.object(), rewrittenValue);
+ iterator.replaceCurrentInstruction(newInstancePut);
+ }
}
- } else if (actualField != field) {
- Value newOutValue = makeOutValue(instanceGet, code);
- iterator.replaceCurrentInstruction(
- new InstanceGet(newOutValue, instanceGet.object(), actualField));
- if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ break;
+
+ case STATIC_GET:
+ {
+ StaticGet staticGet = current.asStaticGet();
+ DexField field = staticGet.getField();
+ DexField actualField = graphLense.lookupField(field);
+ DexMethod replacementMethod =
+ graphLense.lookupGetFieldForMethod(actualField, method.method);
+ if (replacementMethod != null) {
+ Value newOutValue = makeOutValue(current, code);
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(replacementMethod, newOutValue, current.inValues()));
+ if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ }
+ } else if (actualField != field) {
+ Value newOutValue = makeOutValue(staticGet, code);
+ iterator.replaceCurrentInstruction(new StaticGet(newOutValue, actualField));
+ if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ }
+ }
}
- }
- } else if (current.isInstancePut()) {
- InstancePut instancePut = current.asInstancePut();
- DexField field = instancePut.getField();
- DexField actualField = graphLense.lookupField(field);
- DexMethod replacementMethod =
- graphLense.lookupPutFieldForMethod(actualField, method.method);
- if (replacementMethod != null) {
- iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, null, current.inValues()));
- } else if (actualField != field) {
- Value rewrittenValue =
- rewriteValueIfDefault(
- code, iterator, field.type, actualField.type, instancePut.value());
- InstancePut newInstancePut =
- InstancePut.createPotentiallyInvalid(
- actualField, instancePut.object(), rewrittenValue);
- iterator.replaceCurrentInstruction(newInstancePut);
- }
- } else if (current.isStaticGet()) {
- StaticGet staticGet = current.asStaticGet();
- DexField field = staticGet.getField();
- DexField actualField = graphLense.lookupField(field);
- DexMethod replacementMethod =
- graphLense.lookupGetFieldForMethod(actualField, method.method);
- if (replacementMethod != null) {
- Value newOutValue = makeOutValue(current, code);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, newOutValue, current.inValues()));
- if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ break;
+
+ case STATIC_PUT:
+ {
+ StaticPut staticPut = current.asStaticPut();
+ DexField field = staticPut.getField();
+ DexField actualField = graphLense.lookupField(field);
+ DexMethod replacementMethod =
+ graphLense.lookupPutFieldForMethod(actualField, method.method);
+ if (replacementMethod != null) {
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
+ } else if (actualField != field) {
+ Value rewrittenValue =
+ rewriteValueIfDefault(
+ code, iterator, field.type, actualField.type, staticPut.value());
+ StaticPut newStaticPut = new StaticPut(rewrittenValue, actualField);
+ iterator.replaceCurrentInstruction(newStaticPut);
+ }
}
- } else if (actualField != field) {
- Value newOutValue = makeOutValue(staticGet, code);
- iterator.replaceCurrentInstruction(new StaticGet(newOutValue, actualField));
- if (newOutValue != null && newOutValue.getType() != current.outValue().getType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
+ break;
+
+ case CHECK_CAST:
+ {
+ CheckCast checkCast = current.asCheckCast();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ checkCast.getType(), (t, v) -> new CheckCast(v, checkCast.object(), t));
}
- }
- } else if (current.isStaticPut()) {
- StaticPut staticPut = current.asStaticPut();
- DexField field = staticPut.getField();
- DexField actualField = graphLense.lookupField(field);
- DexMethod replacementMethod =
- graphLense.lookupPutFieldForMethod(actualField, method.method);
- if (replacementMethod != null) {
- iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
- } else if (actualField != field) {
- Value rewrittenValue =
- rewriteValueIfDefault(
- code, iterator, field.type, actualField.type, staticPut.value());
- StaticPut newStaticPut = new StaticPut(rewrittenValue, actualField);
- iterator.replaceCurrentInstruction(newStaticPut);
- }
- } else if (current.isCheckCast()) {
- CheckCast checkCast = current.asCheckCast();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- checkCast.getType(), (t, v) -> new CheckCast(v, checkCast.object(), t));
- } else if (current.isConstClass()) {
- ConstClass constClass = current.asConstClass();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- constClass.getValue(), (t, v) -> new ConstClass(v, t));
- } else if (current.isInstanceOf()) {
- InstanceOf instanceOf = current.asInstanceOf();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- instanceOf.type(), (t, v) -> new InstanceOf(v, instanceOf.value(), t));
- } else if (current.isInvokeMultiNewArray()) {
- InvokeMultiNewArray multiNewArray = current.asInvokeMultiNewArray();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- multiNewArray.getArrayType(),
- (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues()));
- } else if (current.isInvokeNewArray()) {
- InvokeNewArray newArray = current.asInvokeNewArray();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- newArray.getArrayType(), (t, v) -> new InvokeNewArray(t, v, newArray.inValues()));
- } else if (current.isMoveException()) {
- MoveException moveException = current.asMoveException();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- moveException.getExceptionType(),
- (t, v) -> new MoveException(v, t, appView.options()));
- } else if (current.isNewArrayEmpty()) {
- NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- newArrayEmpty.type, (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t));
- } else if (current.isNewInstance()) {
- DexType type = current.asNewInstance().clazz;
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(type, NewInstance::new);
- } else if (current.outValue() != null) {
- // For all other instructions, substitute any changed type.
- TypeElement typeLattice = current.outValue().getType();
- TypeElement substituted =
- typeLattice.fixupClassTypeReferences(graphLense::lookupType, appView);
- if (substituted != typeLattice) {
- current.outValue().setType(substituted);
- affectedPhis.addAll(current.outValue().uniquePhiUsers());
- }
+ break;
+
+ case CONST_CLASS:
+ {
+ ConstClass constClass = current.asConstClass();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ constClass.getValue(), (t, v) -> new ConstClass(v, t));
+ }
+ break;
+
+ case INSTANCE_OF:
+ {
+ InstanceOf instanceOf = current.asInstanceOf();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ instanceOf.type(), (t, v) -> new InstanceOf(v, instanceOf.value(), t));
+ }
+ break;
+
+ case INVOKE_MULTI_NEW_ARRAY:
+ {
+ InvokeMultiNewArray multiNewArray = current.asInvokeMultiNewArray();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ multiNewArray.getArrayType(),
+ (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues()));
+ }
+ break;
+
+ case INVOKE_NEW_ARRAY:
+ {
+ InvokeNewArray newArray = current.asInvokeNewArray();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ newArray.getArrayType(),
+ (t, v) -> new InvokeNewArray(t, v, newArray.inValues()));
+ }
+ break;
+
+ case MOVE_EXCEPTION:
+ {
+ MoveException moveException = current.asMoveException();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ moveException.getExceptionType(),
+ (t, v) -> new MoveException(v, t, appView.options()));
+ }
+ break;
+
+ case NEW_ARRAY_EMPTY:
+ {
+ NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ newArrayEmpty.type, (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t));
+ }
+ break;
+
+ case NEW_INSTANCE:
+ {
+ DexType type = current.asNewInstance().clazz;
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(type, NewInstance::new);
+ }
+ break;
+
+ default:
+ if (current.hasOutValue()) {
+ // For all other instructions, substitute any changed type.
+ TypeElement type = current.getOutType();
+ TypeElement substituted =
+ type.fixupClassTypeReferences(graphLense::lookupType, appView);
+ if (substituted != type) {
+ current.outValue().setType(substituted);
+ affectedPhis.addAll(current.outValue().uniquePhiUsers());
+ }
+ }
+ break;
}
}
}
@@ -685,7 +785,7 @@
Instruction newInstruction = constructor.apply(newType, newOutValue);
iterator.replaceCurrentInstruction(newInstruction);
if (newOutValue != null) {
- if (newOutValue.getType() != current.outValue().getType()) {
+ if (newOutValue.getType() != current.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
} else {
assert current.hasInvariantOutType();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index e742528..328365e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -175,7 +175,7 @@
OptimizationFeedback feedback) {
// TODO(b/140766440): Make IRConverter#process receive a list of CodeOptimization to conduct.
// Then, we can share IRCode creation there.
- Origin origin = appView.appInfo().originFor(method.method.holder);
+ Origin origin = appView.appInfo().originFor(method.holder());
if (appView.options().skipIR) {
feedback.markProcessed(method, ConstraintWithTarget.NEVER);
return;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
index d7edc8a..58f7155 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
@@ -114,7 +114,7 @@
}
}
for (Instruction instruction : block.getInstructions()) {
- if (instruction.outValue() != null && !instruction.outValue().getType().isPreciseType()) {
+ if (instruction.outValue() != null && !instruction.getOutType().isPreciseType()) {
impreciseValues.add(instruction.outValue());
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 43db91d..49c8000 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -125,6 +125,11 @@
}
}
+ public static void registerAssumedLibraryTypes(InternalOptions options) {
+ // TODO(b/150693139): Remove the pre-registration once fixed.
+ BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
+ }
+
public void desugar(IRCode code) {
if (!enabled) {
return; // Nothing to do!
@@ -154,7 +159,7 @@
if (provider.requiresGenerationOfCode()) {
DexMethod newMethod = provider.provideMethod(appView);
methodProviders.putIfAbsent(newMethod, provider);
- holders.add(code.method.method.holder);
+ holders.add(code.method.holder());
}
}
if (!affectedValues.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 3d16d19..82c7c6a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -18,9 +19,9 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.IncompatibleClassResult;
import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
@@ -74,14 +75,6 @@
return signatures.size() == merged.size() ? this : new MethodSignatures(merged);
}
- MethodSignatures merge(List<MethodSignatures> others) {
- MethodSignatures merged = this;
- for (MethodSignatures other : others) {
- merged = merged.merge(others);
- }
- return merged;
- }
-
boolean isEmpty() {
return signatures.isEmpty();
}
@@ -151,7 +144,7 @@
}
}
- // Specialized context to disable reporting when traversing the library strucure.
+ // Specialized context to disable reporting when traversing the library structure.
private static class LibraryReportingContext extends ReportingContext {
static final LibraryReportingContext LIBRARY_CONTEXT = new LibraryReportingContext();
@@ -281,22 +274,36 @@
return ClassInfo.create(superInfo, additionalForwards.build());
}
- // Resolves a method signature from the point of 'clazz', if it must target a default method
+ // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
// the 'addForward' call-back is called with the target of the forward.
private void resolveForwardForSignature(
DexClass clazz, DexMethod method, BiConsumer<DexClass, DexEncodedMethod> addForward) {
- ResolutionResult resolution = appView.appInfo().resolveMethod(clazz, method);
- // If resolution fails, install a method throwing IncompatibleClassChangeError.
- if (resolution.isFailedResolution()) {
- if (resolution instanceof IncompatibleClassResult) {
+ // Resolve the default method at base type as the symbolic holder at call sites is not known.
+ // The dispatch target is then looked up from the possible "instance" class.
+ // Doing so can cause an invalid invoke to become valid (at runtime resolution at a subtype
+ // might have failed which is hidden by the insertion of the forward method). However, not doing
+ // so could cause valid dispatches to become invalid by resolving to private overrides.
+ DexClassAndMethod virtualDispatchTarget =
+ appView
+ .appInfo()
+ .resolveMethodOnInterface(method.holder, method)
+ .lookupVirtualDispatchTarget(clazz, appView.appInfo());
+ if (virtualDispatchTarget == null) {
+ // If no target is found due to multiple default method targets, preserve ICCE behavior.
+ ResolutionResult resolutionFromSubclass = appView.appInfo().resolveMethod(clazz, method);
+ if (resolutionFromSubclass.isIncompatibleClassChangeErrorResult()) {
addICCEThrowingMethod(method, clazz);
+ return;
}
+ assert resolutionFromSubclass.isFailedResolution()
+ || resolutionFromSubclass.getSingleTarget().isPrivateMethod();
return;
}
- DexEncodedMethod target = resolution.getSingleTarget();
- DexClass targetHolder = appView.definitionFor(target.method.holder);
+
+ DexEncodedMethod target = virtualDispatchTarget.getMethod();
+ DexClass targetHolder = virtualDispatchTarget.getHolder();
// Don-t forward if the target is explicitly marked as 'dont-rewrite'
- if (targetHolder == null || dontRewrite(targetHolder, target)) {
+ if (dontRewrite(targetHolder, target)) {
return;
}
@@ -330,7 +337,7 @@
private boolean isRetargetMethod(DexLibraryClass holder, DexEncodedMethod method) {
assert needsLibraryInfo();
- assert holder.type == method.method.holder;
+ assert holder.type == method.holder();
assert method.isNonPrivateVirtualMethod();
if (method.isFinal()) {
return false;
@@ -375,6 +382,16 @@
if (!clazz.isProgramClass()) {
return;
}
+
+ DexEncodedMethod methodOnSelf = clazz.lookupMethod(target.method);
+ if (methodOnSelf != null) {
+ throw new CompilationError(
+ "Attempt to add forwarding method that conflicts with existing method.",
+ null,
+ clazz.getOrigin(),
+ new MethodPosition(methodOnSelf.method));
+ }
+
DexMethod method = target.method;
// NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
// even if this results in invalid code, these classes are never desugared.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 2be1832..b5c3f3e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -152,12 +152,12 @@
MethodAccessFlags newAccessFlags = method.accessFlags.copy();
newAccessFlags.setBridge();
newAccessFlags.setSynthetic();
- DexMethod newMethod = factory.createMethod(method.method.holder, newProto, method.method.name);
+ DexMethod newMethod = factory.createMethod(method.holder(), newProto, method.method.name);
ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
ForwardMethodSourceCode.builder(newMethod);
forwardSourceCodeBuilder
.setReceiver(clazz.type)
- .setTargetReceiver(method.method.holder)
+ .setTargetReceiver(method.holder())
.setTarget(method.method)
.setInvokeType(Invoke.Type.VIRTUAL)
.setCastResult();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
index 7b53a35..738f4aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -49,7 +49,7 @@
public void rewriteNestBasedAccesses(
DexEncodedMethod encodedMethod, IRCode code, AppView<?> appView) {
- DexClass currentClass = appView.definitionFor(encodedMethod.method.holder);
+ DexClass currentClass = appView.definitionFor(encodedMethod.holder());
assert currentClass != null;
if (!currentClass.isInANest()) {
return;
@@ -120,7 +120,7 @@
private void addDeferredBridges(Collection<DexEncodedMethod> bridges) {
for (DexEncodedMethod bridge : bridges) {
- DexClass holder = definitionFor(bridge.method.holder);
+ DexClass holder = definitionFor(bridge.holder());
assert holder != null && holder.isProgramClass();
holder.asProgramClass().addMethod(bridge);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index c81eedb..11180c4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -106,7 +106,7 @@
public void desugar(IRCode code) {
- if (wrapperSynthesizor.hasSynthesized(code.method.method.holder)) {
+ if (wrapperSynthesizor.hasSynthesized(code.method.holder())) {
return;
}
@@ -140,7 +140,7 @@
if (!shouldRegisterCallback(encodedMethod)) {
return true;
}
- DexProgramClass holderClass = appView.definitionForProgramType(encodedMethod.method.holder);
+ DexProgramClass holderClass = appView.definitionForProgramType(encodedMethod.holder());
DexMethod installedCallback =
methodWithVivifiedTypeInSignature(encodedMethod.method, holderClass.type, appView);
assert holderClass.lookupMethod(installedCallback) != null;
@@ -161,7 +161,7 @@
public void registerCallbackIfRequired(DexEncodedMethod encodedMethod) {
if (shouldRegisterCallback(encodedMethod)) {
- DexClass dexClass = appView.definitionFor(encodedMethod.method.holder);
+ DexClass dexClass = appView.definitionFor(encodedMethod.holder());
assert dexClass != null;
registerCallback(dexClass, encodedMethod);
}
@@ -197,7 +197,6 @@
return overridesLibraryMethod(dexClass, method);
}
-
private boolean overridesLibraryMethod(DexClass theClass, DexMethod method) {
// We look up everywhere to see if there is a supertype/interface implementing the method...
WorkList<DexType> workList = WorkList.newIdentityWorkList();
@@ -253,7 +252,7 @@
}
private synchronized void addCallBackSignature(DexClass dexClass, DexEncodedMethod method) {
- assert dexClass.type == method.method.holder;
+ assert dexClass.type == method.holder();
if (callBackMethods.computeIfAbsent(dexClass, key -> new HashSet<>()).add(method)) {
pendingCallBackMethods.computeIfAbsent(dexClass, key -> new ArrayList<>()).add(method);
}
@@ -347,22 +346,22 @@
appView.options().reporter.warning(new StringDiagnostic(sb.toString()));
}
- private void warnInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
+ public void reportInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView);
appView
.options()
.reporter
- .warning(
+ .info(
new StringDiagnostic(
"Invoke to "
+ invokedMethod.holder
+ "#"
+ invokedMethod.name
- + " may not work correctly at runtime ("
+ + " may not work correctly at runtime (Cannot convert "
+ debugString
- + " type "
+ + "type "
+ desugaredType
- + " is a desugared type)."));
+ + ")."));
}
public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) {
@@ -398,6 +397,7 @@
InstructionListIterator iterator,
ListIterator<BasicBlock> blockIterator) {
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
+ boolean invalidConversion = false;
if (trackedAPIs != null) {
trackedAPIs.add(invokedMethod);
}
@@ -412,12 +412,13 @@
// Return conversion added only if return value is used.
if (invokeMethod.outValue() != null
&& invokeMethod.outValue().numberOfUsers() + invokeMethod.outValue().numberOfPhiUsers()
- > 0) {
+ > 0) {
returnConversion =
createReturnConversionAndReplaceUses(code, invokeMethod, returnType, newReturnType);
}
} else {
- warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return");
+ reportInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return ");
+ invalidConversion = true;
newReturnType = returnType;
}
} else {
@@ -445,7 +446,8 @@
createParameterConversion(code, argType, argVivifiedType, inValue));
newInValues.add(parameterConversions.get(parameterConversions.size() - 1).outValue());
} else {
- warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter");
+ reportInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter ");
+ invalidConversion = true;
newInValues.add(invokeMethod.inValues().get(i + receiverShift));
}
} else {
@@ -465,7 +467,8 @@
invokeMethod.outValue(),
newInValues);
assert newDexMethod
- == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView);
+ == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView)
+ || invalidConversion;
// Insert and reschedule all instructions.
iterator.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 3949767..0e52152 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -5,7 +5,24 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.*;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -118,7 +135,7 @@
appView
.appInfo()
.withClassHierarchy()
- .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
+ .lookupSuperTarget(invoke.getInvokedMethod(), code.method.holder());
// Final methods can be rewritten as a normal invoke.
if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
DexMethod retargetMethod =
@@ -270,7 +287,7 @@
&& dexClass.isLibraryClass()
&& dexClass.type != appView.dexItemFactory().objectType) {
for (DexType dexType : map.keySet()) {
- if (inherit(dexClass.asLibraryClass(), dexType)) {
+ if (inherit(dexClass.asLibraryClass(), dexType, emulatedDispatchMethods)) {
addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType)));
}
}
@@ -282,13 +299,21 @@
converter.processMethodsConcurrently(addedMethods, executorService);
}
- private boolean inherit(DexLibraryClass clazz, DexType typeToInherit) {
+ private boolean inherit(DexLibraryClass clazz, DexType typeToInherit, Set<DexMethod> retarget) {
DexLibraryClass current = clazz;
while (current.type != appView.dexItemFactory().objectType) {
if (current.type == typeToInherit) {
return true;
}
- current = appView.definitionFor(current.superType).asLibraryClass();
+ DexClass dexClass = appView.definitionFor(current.superType);
+ if (dexClass == null || dexClass.isClasspathClass()) {
+ reportInvalidLibrarySupertype(current, retarget);
+ return false;
+ } else if (dexClass.isProgramClass()) {
+ // If dexClass is a program class, then it is already correctly desugared.
+ return false;
+ }
+ current = dexClass.asLibraryClass();
}
return false;
}
@@ -413,6 +438,29 @@
}
}
+ private void reportInvalidLibrarySupertype(
+ DexLibraryClass libraryClass, Set<DexMethod> retarget) {
+ DexClass dexClass = appView.definitionFor(libraryClass.superType);
+ String message;
+ if (dexClass == null) {
+ message = "missing";
+ } else if (dexClass.isClasspathClass()) {
+ message = "a classpath class";
+ } else {
+ message = "INVALID";
+ assert false;
+ }
+ appView
+ .options()
+ .warningInvalidLibrarySuperclassForDesugar(
+ dexClass == null ? libraryClass.getOrigin() : dexClass.getOrigin(),
+ libraryClass.type,
+ libraryClass.superType,
+ message,
+ retarget,
+ appView);
+ }
+
private DexType dispatchInterfaceTypeFor(DexMethod method) {
return dispatchTypeFor(method, "dispatchInterface");
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 30bc78e..e3c15ae 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -125,7 +125,7 @@
boolean canGenerateWrapper(DexType type) {
DexClass dexClass = appView.definitionFor(type);
- if (dexClass == null) {
+ if (dexClass == null || dexClass.accessFlags.isFinal()) {
return false;
}
return dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation();
@@ -169,18 +169,9 @@
private DexClass getValidClassToWrap(DexType type) {
DexClass dexClass = appView.definitionFor(type);
// The dexClass should be a library class, so it cannot be null.
- assert dexClass != null
- && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation());
- if (dexClass.accessFlags.isFinal()) {
- throw appView
- .options()
- .reporter
- .fatalError(
- new StringDiagnostic(
- "Cannot generate a wrapper for final class "
- + dexClass.type
- + ". Add a custom conversion in the desugared library."));
- }
+ assert dexClass != null;
+ assert dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation();
+ assert !dexClass.accessFlags.isFinal();
return dexClass;
}
@@ -223,7 +214,7 @@
DexTypeList interfaces =
isItf ? new DexTypeList(new DexType[] {wrappingType}) : DexTypeList.empty();
return new DexProgramClass(
- wrapperField.field.holder,
+ wrapperField.holder(),
null,
new SynthesizedOrigin("Desugared library API Converter", getClass()),
ClassAccessFlags.fromSharedAccessFlags(
@@ -260,23 +251,24 @@
// return v3;
Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder());
boolean isInterface;
if (holderClass == null) {
- assert appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface()
- .containsValue(dexEncodedMethod.method.holder);
+ assert appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsValue(dexEncodedMethod.holder());
isInterface = true;
} else {
isInterface = holderClass.isInterface();
}
DexMethod methodToInstall =
factory.createMethod(
- wrapperField.field.holder,
- dexEncodedMethod.method.proto,
- dexEncodedMethod.method.name);
+ wrapperField.holder(), dexEncodedMethod.method.proto, dexEncodedMethod.method.name);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
- invalidWrappers.add(wrapperField.field.holder);
+ invalidWrappers.add(wrapperField.holder());
finalMethods.add(dexEncodedMethod.method);
continue;
} else {
@@ -312,15 +304,15 @@
// return v3;
Set<DexMethod> finalMethods = Sets.newIdentityHashSet();
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder());
assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
boolean isInterface = holderClass == null || holderClass.isInterface();
DexMethod methodToInstall =
DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
- dexEncodedMethod.method, wrapperField.field.holder, appView);
+ dexEncodedMethod.method, wrapperField.holder(), appView);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
- invalidWrappers.add(wrapperField.field.holder);
+ invalidWrappers.add(wrapperField.holder());
finalMethods.add(dexEncodedMethod.method);
continue;
} else {
@@ -342,7 +334,7 @@
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Generate only abstract methods for library override detection.
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
- DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.holder());
assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
if (!dexEncodedMethod.isFinal()) {
DexMethod methodToInstall =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 1a4c416..e9b9b7e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -62,6 +62,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
//
@@ -143,6 +144,37 @@
initializeEmulatedInterfaceVariables();
}
+ public static void checkForAssumedLibraryTypes(AppInfo appInfo, InternalOptions options) {
+ DesugaredLibraryConfiguration config = options.desugaredLibraryConfiguration;
+ BiConsumer<DexType, DexType> registerEntry = registerMapEntry(appInfo);
+ config.getEmulateLibraryInterface().forEach(registerEntry);
+ config.getCustomConversions().forEach(registerEntry);
+ config.getRetargetCoreLibMember().forEach((method, types) -> types.forEach(registerEntry));
+ }
+
+ private static BiConsumer<DexType, DexType> registerMapEntry(AppInfo appInfo) {
+ return (key, value) -> {
+ registerType(appInfo, key);
+ registerType(appInfo, value);
+ };
+ }
+
+ private static void registerType(AppInfo appInfo, DexType type) {
+ appInfo.dexItemFactory().registerTypeNeededForDesugaring(type);
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz != null && clazz.isLibraryClass() && clazz.isInterface()) {
+ clazz.forEachMethod(
+ m -> {
+ if (m.isDefaultMethod()) {
+ appInfo.dexItemFactory().registerTypeNeededForDesugaring(m.method.proto.returnType);
+ for (DexType param : m.method.proto.parameters.values) {
+ appInfo.dexItemFactory().registerTypeNeededForDesugaring(param);
+ }
+ }
+ });
+ }
+ }
+
private void initializeEmulatedInterfaceVariables() {
Map<DexType, DexType> emulateLibraryInterface =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -238,7 +270,7 @@
invokeStatic.outValue(), invokeStatic.arguments()));
requiredDispatchClasses
.computeIfAbsent(clazz.asLibraryClass(), k -> Sets.newConcurrentHashSet())
- .add(appInfo.definitionFor(encodedMethod.method.holder).asProgramClass());
+ .add(appInfo.definitionFor(encodedMethod.holder()).asProgramClass());
}
} else {
instructions.replaceCurrentInstruction(
@@ -269,8 +301,7 @@
// WARNING: This may result in incorrect code on older platforms!
// Retarget call to an appropriate method of companion class.
DexMethod amendedMethod =
- amendDefaultMethod(
- appInfo.definitionFor(encodedMethod.method.holder), invokedMethod);
+ amendDefaultMethod(appInfo.definitionFor(encodedMethod.holder()), invokedMethod);
instructions.replaceCurrentInstruction(
new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
@@ -284,9 +315,9 @@
DexEncodedMethod dexEncodedMethod =
appView
.appInfo()
- .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.method.holder);
+ .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.holder());
if (dexEncodedMethod != null) {
- DexClass dexClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ DexClass dexClass = appView.definitionFor(dexEncodedMethod.holder());
if (dexClass != null && dexClass.isLibraryClass()) {
// Rewriting is required because the super invoke resolves into a missing
// method (method is on desugared library). Find out if it needs to be
@@ -418,8 +449,8 @@
// interfaces.
return null;
}
- if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.method.holder)) {
- return singleTarget.method.holder;
+ if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.holder())) {
+ return singleTarget.holder();
}
return null;
}
@@ -616,8 +647,8 @@
}
emulationMethods.add(
DexEncodedMethod.toEmulateDispatchLibraryMethod(
- method.method.holder,
- emulateInterfaceLibraryMethod(method.method, method.method.holder, factory),
+ method.holder(),
+ emulateInterfaceLibraryMethod(method.method, method.holder(), factory),
companionMethod,
libraryMethod,
extraDispatchCases,
@@ -719,6 +750,10 @@
return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
}
+ public static boolean isEmulatedLibraryClassType(DexType type) {
+ return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
+ }
+
// Gets the interface class for a companion class `type`.
private DexType getInterfaceClassType(DexType type) {
return getInterfaceClassType(type, factory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 4409c30..ab5a9cd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -320,7 +320,7 @@
} else {
assert code.isCfCode();
for (CfInstruction insn : code.asCfCode().getInstructions()) {
- if (insn instanceof CfInvoke && ((CfInvoke) insn).isInvokeSuper(method.method.holder)) {
+ if (insn instanceof CfInvoke && ((CfInvoke) insn).isInvokeSuper(method.holder())) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index d440b01..2ac43f0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -96,7 +96,7 @@
invocationContext == null ? null : lookupTargetMethod(appInfo, invocationContext);
if (targetMethod != null) {
targetAccessFlags = targetMethod.accessFlags.copy();
- targetHolder = targetMethod.method.holder;
+ targetHolder = targetMethod.holder();
} else {
targetAccessFlags = null;
targetHolder = null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 4ee35d6..d6d698e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -135,7 +135,7 @@
*/
public void desugarLambdas(DexEncodedMethod encodedMethod, IRCode code) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
- DexType currentType = encodedMethod.method.holder;
+ DexType currentType = encodedMethod.holder();
ListIterator<BasicBlock> blocks = code.listIterator();
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
@@ -145,7 +145,7 @@
if (instruction.isInvokeCustom()) {
InvokeCustom invoke = instruction.asInvokeCustom();
LambdaDescriptor descriptor =
- inferLambdaDescriptor(invoke.getCallSite(), encodedMethod.method.holder);
+ inferLambdaDescriptor(invoke.getCallSite(), encodedMethod.holder());
if (descriptor == LambdaDescriptor.MATCH_FAILED) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index b78757d..f4e1f4a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -51,7 +51,6 @@
private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
NEST_ACCESS_NAME_PREFIX + "sfput";
public static final String NEST_CONSTRUCTOR_NAME = NEST_ACCESS_NAME_PREFIX + "Constructor";
- private static final String FULL_NEST_CONTRUCTOR_NAME = "L" + NEST_CONSTRUCTOR_NAME + ";";
protected final AppView<?> appView;
// Following maps are there to avoid creating the bridges multiple times
@@ -161,7 +160,7 @@
private DexProgramClass createNestAccessConstructor() {
return new DexProgramClass(
- appView.dexItemFactory().createType(FULL_NEST_CONTRUCTOR_NAME),
+ appView.dexItemFactory().nestConstructorType,
null,
new SynthesizedOrigin("Nest based access desugaring", getClass()),
// Make the synthesized class public since shared in the whole program.
@@ -238,7 +237,7 @@
}
private DexMethod computeFieldBridge(DexEncodedField field, boolean isGet) {
- DexType holderType = field.field.holder;
+ DexType holderType = field.holder();
DexType fieldType = field.field.type;
int bridgeParameterCount =
BooleanUtils.intValue(!field.isStatic()) + BooleanUtils.intValue(!isGet);
@@ -259,10 +258,10 @@
boolean invokeRequiresRewriting(DexEncodedMethod method, DexClass contextClass) {
assert method != null;
// Rewrite only when targeting other nest members private fields.
- if (!method.accessFlags.isPrivate() || method.method.holder == contextClass.type) {
+ if (!method.accessFlags.isPrivate() || method.holder() == contextClass.type) {
return false;
}
- DexClass methodHolder = definitionFor(method.method.holder);
+ DexClass methodHolder = definitionFor(method.holder());
assert methodHolder != null; // from encodedMethod
return methodHolder.getNestHost() == contextClass.getNestHost();
}
@@ -270,10 +269,10 @@
boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClass contextClass) {
assert field != null;
// Rewrite only when targeting other nest members private fields.
- if (!field.accessFlags.isPrivate() || field.field.holder == contextClass.type) {
+ if (!field.accessFlags.isPrivate() || field.holder() == contextClass.type) {
return false;
}
- DexClass fieldHolder = definitionFor(field.field.holder);
+ DexClass fieldHolder = definitionFor(field.holder());
assert fieldHolder != null; // from encodedField
return fieldHolder.getNestHost() == contextClass.getNestHost();
}
@@ -295,7 +294,7 @@
}
DexMethod ensureFieldAccessBridge(DexEncodedField field, boolean isGet) {
- DexClass holder = definitionFor(field.field.holder);
+ DexClass holder = definitionFor(field.holder());
assert holder != null;
DexMethod bridgeMethod = computeFieldBridge(field, isGet);
if (holderRequiresBridge(holder)) {
@@ -314,7 +313,7 @@
DexMethod ensureInvokeBridge(DexEncodedMethod method) {
// We add bridges only when targeting other nest members.
- DexClass holder = definitionFor(method.method.holder);
+ DexClass holder = definitionFor(method.holder());
assert holder != null;
DexMethod bridgeMethod;
if (method.isInstanceInitializer()) {
@@ -508,7 +507,7 @@
}
public DexType getHolder() {
- return field.field.holder;
+ return field.holder();
}
public DexField getField() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index 1c3b6a2..6f5c3f2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -18,6 +18,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
public abstract class PrefixRewritingMapper {
@@ -47,6 +48,8 @@
public abstract boolean isRewriting();
+ public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
+
public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper {
private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
@@ -70,6 +73,11 @@
return factory.createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
}
+ @Override
+ public void forAllRewrittenTypes(Consumer<DexType> consumer) {
+ rewritten.keySet().forEach(consumer);
+ }
+
private void validatePrefixes(Map<String, String> initialPrefixes) {
String[] prefixes = initialPrefixes.keySet().toArray(new String[0]);
for (int i = 0; i < prefixes.length; i++) {
@@ -192,5 +200,8 @@
public boolean isRewriting() {
return false;
}
+
+ @Override
+ public void forAllRewrittenTypes(Consumer<DexType> consumer) {}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
index c4c9bd7..ca10fe6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -56,7 +56,7 @@
private <E> void addDeferredBridgesAndMapMethods(
Map<E, DexEncodedMethod> bridges, BiConsumer<E, DexMethod> lensInserter) {
for (Map.Entry<E, DexEncodedMethod> entry : bridges.entrySet()) {
- DexClass holder = definitionFor(entry.getValue().method.holder);
+ DexClass holder = definitionFor(entry.getValue().holder());
assert holder != null && holder.isProgramClass();
holder.asProgramClass().addMethod(entry.getValue());
lensInserter.accept(entry.getKey(), entry.getValue().method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 17250cf..e477fe3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -99,7 +99,7 @@
new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues()));
// Mark as a class referencing utility class.
- referencingClasses.add(appInfo.definitionFor(code.method.method.holder).asProgramClass());
+ referencingClasses.add(appInfo.definitionFor(code.method.holder()).asProgramClass());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 023ef1c..bc3c320 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.Cmp;
import com.android.tools.r8.ir.code.If;
@@ -47,6 +48,59 @@
public final class BackportedMethods {
+ public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
+ factory.createSynthesizedType("Ljava/lang/ArithmeticException;");
+ factory.createSynthesizedType("Ljava/lang/AssertionError;");
+ factory.createSynthesizedType("Ljava/lang/Double;");
+ factory.createSynthesizedType("Ljava/lang/ExceptionInInitializerError;");
+ factory.createSynthesizedType("Ljava/lang/Float;");
+ factory.createSynthesizedType("Ljava/lang/IllegalAccessException;");
+ factory.createSynthesizedType("Ljava/lang/IllegalArgumentException;");
+ factory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;");
+ factory.createSynthesizedType("Ljava/lang/Integer;");
+ factory.createSynthesizedType("Ljava/lang/Iterable;");
+ factory.createSynthesizedType("Ljava/lang/Long;");
+ factory.createSynthesizedType("Ljava/lang/Math;");
+ factory.createSynthesizedType("Ljava/lang/NoSuchMethodException;");
+ factory.createSynthesizedType("Ljava/lang/NullPointerException;");
+ factory.createSynthesizedType("Ljava/lang/NumberFormatException;");
+ factory.createSynthesizedType("Ljava/lang/Runnable;");
+ factory.createSynthesizedType("Ljava/lang/SecurityException;");
+ factory.createSynthesizedType("Ljava/lang/reflect/InvocationTargetException;");
+ factory.createSynthesizedType("Ljava/lang/reflect/Method;");
+ factory.createSynthesizedType("Ljava/util/AbstractMap$SimpleImmutableEntry;");
+ factory.createSynthesizedType("Ljava/util/ArrayList;");
+ factory.createSynthesizedType("Ljava/util/Arrays;");
+ factory.createSynthesizedType("Ljava/util/Collection;");
+ factory.createSynthesizedType("Ljava/util/Collections;");
+ factory.createSynthesizedType("Ljava/util/Comparator;");
+ factory.createSynthesizedType("Ljava/util/Enumeration;");
+ factory.createSynthesizedType("Ljava/util/HashMap;");
+ factory.createSynthesizedType("Ljava/util/HashSet;");
+ factory.createSynthesizedType("Ljava/util/Iterator;");
+ factory.createSynthesizedType("Ljava/util/List;");
+ factory.createSynthesizedType("Ljava/util/ListIterator;");
+ factory.createSynthesizedType("Ljava/util/Map$Entry;");
+ factory.createSynthesizedType("Ljava/util/Map;");
+ factory.createSynthesizedType("Ljava/util/Objects;");
+ factory.createSynthesizedType("Ljava/util/Optional;");
+ factory.createSynthesizedType("Ljava/util/OptionalDouble;");
+ factory.createSynthesizedType("Ljava/util/OptionalInt;");
+ factory.createSynthesizedType("Ljava/util/OptionalLong;");
+ factory.createSynthesizedType("Ljava/util/Set;");
+ factory.createSynthesizedType("Ljava/util/function/Consumer;");
+ factory.createSynthesizedType("Ljava/util/function/DoubleConsumer;");
+ factory.createSynthesizedType("Ljava/util/function/IntConsumer;");
+ factory.createSynthesizedType("Ljava/util/function/LongConsumer;");
+ factory.createSynthesizedType("Ljava/util/function/Supplier;");
+ factory.createSynthesizedType("Ljava/util/stream/DoubleStream;");
+ factory.createSynthesizedType("Ljava/util/stream/IntStream;");
+ factory.createSynthesizedType("Ljava/util/stream/LongStream;");
+ factory.createSynthesizedType("Ljava/util/stream/Stream;");
+ factory.createSynthesizedType("[Ljava/lang/Class;");
+ factory.createSynthesizedType("[Ljava/lang/Object;");
+ }
+
public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
@@ -235,7 +289,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Math;"),
+ options.itemFactory.createType("Ljava/lang/Math;"),
options.itemFactory.createProto(
options.itemFactory.intType,
options.itemFactory.intType,
@@ -397,15 +451,15 @@
false),
new CfConstString(options.itemFactory.createString("close")),
new CfConstNumber(0, ValueType.INT),
- new CfNewArray(options.itemFactory.createSynthesizedType("[Ljava/lang/Class;")),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
new CfInvoke(
182,
options.itemFactory.createMethod(
options.itemFactory.classType,
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
options.itemFactory.stringType,
- options.itemFactory.createSynthesizedType("[Ljava/lang/Class;")),
+ options.itemFactory.createType("[Ljava/lang/Class;")),
options.itemFactory.createString("getMethod")),
false),
new CfStore(ValueType.OBJECT, 2),
@@ -413,15 +467,15 @@
new CfLoad(ValueType.OBJECT, 2),
new CfLoad(ValueType.OBJECT, 1),
new CfConstNumber(0, ValueType.INT),
- new CfNewArray(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
options.itemFactory.createProto(
options.itemFactory.objectType,
options.itemFactory.objectType,
- options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ options.itemFactory.createType("[Ljava/lang/Object;")),
options.itemFactory.createString("invoke")),
false),
new CfStackInstruction(CfStackInstruction.Opcode.Pop),
@@ -430,7 +484,7 @@
label5,
new CfStore(ValueType.OBJECT, 2),
label6,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -477,7 +531,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;"),
+ options.itemFactory.createType("Ljava/lang/AssertionError;"),
options.itemFactory.createProto(
options.itemFactory.voidType,
options.itemFactory.stringType,
@@ -488,7 +542,7 @@
label7,
new CfStore(ValueType.OBJECT, 2),
label8,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -535,7 +589,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/AssertionError;"),
+ options.itemFactory.createType("Ljava/lang/AssertionError;"),
options.itemFactory.createProto(
options.itemFactory.voidType,
options.itemFactory.stringType,
@@ -550,8 +604,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/reflect/InvocationTargetException;"),
+ options.itemFactory.createType("Ljava/lang/reflect/InvocationTargetException;"),
options.itemFactory.createProto(options.itemFactory.throwableType),
options.itemFactory.createString("getCause")),
false),
@@ -577,40 +630,36 @@
label2,
label4,
ImmutableList.of(
- options.itemFactory.createSynthesizedType("Ljava/lang/NoSuchMethodException;")),
+ options.itemFactory.createType("Ljava/lang/NoSuchMethodException;")),
+ ImmutableList.of(label5)),
+ new CfTryCatch(
+ label2,
+ label4,
+ ImmutableList.of(options.itemFactory.createType("Ljava/lang/SecurityException;")),
ImmutableList.of(label5)),
new CfTryCatch(
label2,
label4,
ImmutableList.of(
- options.itemFactory.createSynthesizedType("Ljava/lang/SecurityException;")),
- ImmutableList.of(label5)),
- new CfTryCatch(
- label2,
- label4,
- ImmutableList.of(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IllegalAccessException;")),
+ options.itemFactory.createType("Ljava/lang/IllegalAccessException;")),
ImmutableList.of(label7)),
new CfTryCatch(
label2,
label4,
ImmutableList.of(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IllegalArgumentException;")),
+ options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
ImmutableList.of(label7)),
new CfTryCatch(
label2,
label4,
ImmutableList.of(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/ExceptionInInitializerError;")),
+ options.itemFactory.createType("Ljava/lang/ExceptionInInitializerError;")),
ImmutableList.of(label7)),
new CfTryCatch(
label2,
label4,
ImmutableList.of(
- options.itemFactory.createSynthesizedType(
+ options.itemFactory.createType(
"Ljava/lang/reflect/InvocationTargetException;")),
ImmutableList.of(label9)),
new CfTryCatch(
@@ -635,14 +684,14 @@
6,
ImmutableList.of(
label0,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;")),
+ new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfArrayLength(),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"),
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("<init>")),
@@ -670,7 +719,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -678,7 +727,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"),
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.objectType),
options.itemFactory.createString("add")),
@@ -692,10 +741,10 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/List;"),
- options.itemFactory.createSynthesizedType("Ljava/util/List;")),
+ options.itemFactory.createType("Ljava/util/List;"),
+ options.itemFactory.createType("Ljava/util/List;")),
options.itemFactory.createString("unmodifiableList")),
false),
new CfReturn(ValueType.OBJECT),
@@ -717,15 +766,14 @@
ImmutableList.of(
label0,
new CfNew(
- options.itemFactory.createSynthesizedType(
- "Ljava/util/AbstractMap$SimpleImmutableEntry;")),
+ options.itemFactory.createType("Ljava/util/AbstractMap$SimpleImmutableEntry;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
label1,
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -735,7 +783,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -743,8 +791,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/util/AbstractMap$SimpleImmutableEntry;"),
+ options.itemFactory.createType("Ljava/util/AbstractMap$SimpleImmutableEntry;"),
options.itemFactory.createProto(
options.itemFactory.voidType,
options.itemFactory.objectType,
@@ -775,14 +822,14 @@
8,
ImmutableList.of(
label0,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashMap;")),
+ new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfArrayLength(),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"),
+ options.itemFactory.createType("Ljava/util/HashMap;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("<init>")),
@@ -809,14 +856,14 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("getKey")),
true),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -827,14 +874,14 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("getValue")),
true),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -847,7 +894,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"),
+ options.itemFactory.createType("Ljava/util/HashMap;"),
options.itemFactory.createProto(
options.itemFactory.objectType,
options.itemFactory.objectType,
@@ -856,8 +903,7 @@
false),
new CfIf(If.Type.EQ, ValueType.OBJECT, label7),
label6,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/IllegalArgumentException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -896,8 +942,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IllegalArgumentException;"),
+ options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -911,10 +956,10 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Map;"),
- options.itemFactory.createSynthesizedType("Ljava/util/Map;")),
+ options.itemFactory.createType("Ljava/util/Map;"),
+ options.itemFactory.createType("Ljava/util/Map;")),
options.itemFactory.createString("unmodifiableMap")),
false),
new CfReturn(ValueType.OBJECT),
@@ -938,14 +983,14 @@
6,
ImmutableList.of(
label0,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashSet;")),
+ new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfArrayLength(),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"),
+ options.itemFactory.createType("Ljava/util/HashSet;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("<init>")),
@@ -973,7 +1018,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -981,15 +1026,14 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"),
+ options.itemFactory.createType("Ljava/util/HashSet;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.objectType),
options.itemFactory.createString("add")),
false),
new CfIf(If.Type.NE, ValueType.INT, label5),
label4,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/IllegalArgumentException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -1028,8 +1072,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IllegalArgumentException;"),
+ options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -1043,10 +1086,10 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Set;"),
- options.itemFactory.createSynthesizedType("Ljava/util/Set;")),
+ options.itemFactory.createType("Ljava/util/Set;"),
+ options.itemFactory.createType("Ljava/util/Set;")),
options.itemFactory.createString("unmodifiableSet")),
false),
new CfReturn(ValueType.OBJECT),
@@ -1069,20 +1112,20 @@
4,
ImmutableList.of(
label0,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;")),
+ new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collection;"),
+ options.itemFactory.createType("Ljava/util/Collection;"),
options.itemFactory.createProto(options.itemFactory.intType),
options.itemFactory.createString("size")),
true),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"),
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("<init>")),
@@ -1093,9 +1136,9 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collection;"),
+ options.itemFactory.createType("Ljava/util/Collection;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")),
+ options.itemFactory.createType("Ljava/util/Iterator;")),
options.itemFactory.createString("iterator")),
true),
new CfStore(ValueType.OBJECT, 2),
@@ -1104,7 +1147,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("hasNext")),
true),
@@ -1113,7 +1156,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("next")),
true),
@@ -1124,7 +1167,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -1132,7 +1175,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/ArrayList;"),
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.objectType),
options.itemFactory.createString("add")),
@@ -1145,10 +1188,10 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/List;"),
- options.itemFactory.createSynthesizedType("Ljava/util/List;")),
+ options.itemFactory.createType("Ljava/util/List;"),
+ options.itemFactory.createType("Ljava/util/List;")),
options.itemFactory.createString("unmodifiableList")),
false),
new CfReturn(ValueType.OBJECT),
@@ -1174,20 +1217,20 @@
4,
ImmutableList.of(
label0,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashMap;")),
+ new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Map;"),
+ options.itemFactory.createType("Ljava/util/Map;"),
options.itemFactory.createProto(options.itemFactory.intType),
options.itemFactory.createString("size")),
true),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"),
+ options.itemFactory.createType("Ljava/util/HashMap;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("<init>")),
@@ -1198,17 +1241,17 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Map;"),
+ options.itemFactory.createType("Ljava/util/Map;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Set;")),
+ options.itemFactory.createType("Ljava/util/Set;")),
options.itemFactory.createString("entrySet")),
true),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Set;"),
+ options.itemFactory.createType("Ljava/util/Set;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")),
+ options.itemFactory.createType("Ljava/util/Iterator;")),
options.itemFactory.createString("iterator")),
true),
new CfStore(ValueType.OBJECT, 2),
@@ -1217,7 +1260,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("hasNext")),
true),
@@ -1226,11 +1269,11 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("next")),
true),
- new CfCheckCast(options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;")),
+ new CfCheckCast(options.itemFactory.createType("Ljava/util/Map$Entry;")),
new CfStore(ValueType.OBJECT, 3),
label3,
new CfLoad(ValueType.OBJECT, 1),
@@ -1239,14 +1282,14 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("getKey")),
true),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -1256,14 +1299,14 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("getValue")),
true),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -1272,7 +1315,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashMap;"),
+ options.itemFactory.createType("Ljava/util/HashMap;"),
options.itemFactory.createProto(
options.itemFactory.objectType,
options.itemFactory.objectType,
@@ -1287,10 +1330,10 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Map;"),
- options.itemFactory.createSynthesizedType("Ljava/util/Map;")),
+ options.itemFactory.createType("Ljava/util/Map;"),
+ options.itemFactory.createType("Ljava/util/Map;")),
options.itemFactory.createString("unmodifiableMap")),
false),
new CfReturn(ValueType.OBJECT),
@@ -1313,20 +1356,20 @@
4,
ImmutableList.of(
label0,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/util/HashSet;")),
+ new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collection;"),
+ options.itemFactory.createType("Ljava/util/Collection;"),
options.itemFactory.createProto(options.itemFactory.intType),
options.itemFactory.createString("size")),
true),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"),
+ options.itemFactory.createType("Ljava/util/HashSet;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("<init>")),
@@ -1337,9 +1380,9 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collection;"),
+ options.itemFactory.createType("Ljava/util/Collection;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")),
+ options.itemFactory.createType("Ljava/util/Iterator;")),
options.itemFactory.createString("iterator")),
true),
new CfStore(ValueType.OBJECT, 2),
@@ -1348,7 +1391,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("hasNext")),
true),
@@ -1357,7 +1400,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("next")),
true),
@@ -1368,7 +1411,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -1376,7 +1419,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/HashSet;"),
+ options.itemFactory.createType("Ljava/util/HashSet;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.objectType),
options.itemFactory.createString("add")),
@@ -1389,10 +1432,10 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Set;"),
- options.itemFactory.createSynthesizedType("Ljava/util/Set;")),
+ options.itemFactory.createType("Ljava/util/Set;"),
+ options.itemFactory.createType("Ljava/util/Set;")),
options.itemFactory.createString("unmodifiableSet")),
false),
new CfReturn(ValueType.OBJECT),
@@ -1413,18 +1456,18 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/List;")),
+ options.itemFactory.createType("Ljava/util/List;")),
options.itemFactory.createString("emptyList")),
false),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Enumeration;"),
- options.itemFactory.createSynthesizedType("Ljava/util/Collection;")),
+ options.itemFactory.createType("Ljava/util/Enumeration;"),
+ options.itemFactory.createType("Ljava/util/Collection;")),
options.itemFactory.createString("enumeration")),
false),
new CfReturn(ValueType.OBJECT)),
@@ -1443,17 +1486,17 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/List;")),
+ options.itemFactory.createType("Ljava/util/List;")),
options.itemFactory.createString("emptyList")),
false),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/List;"),
+ options.itemFactory.createType("Ljava/util/List;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")),
+ options.itemFactory.createType("Ljava/util/Iterator;")),
options.itemFactory.createString("iterator")),
true),
new CfReturn(ValueType.OBJECT)),
@@ -1473,17 +1516,17 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Collections;"),
+ options.itemFactory.createType("Ljava/util/Collections;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/List;")),
+ options.itemFactory.createType("Ljava/util/List;")),
options.itemFactory.createString("emptyList")),
false),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/List;"),
+ options.itemFactory.createType("Ljava/util/List;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/ListIterator;")),
+ options.itemFactory.createType("Ljava/util/ListIterator;")),
options.itemFactory.createString("listIterator")),
true),
new CfReturn(ValueType.OBJECT)),
@@ -1505,7 +1548,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Double;"),
+ options.itemFactory.createType("Ljava/lang/Double;"),
options.itemFactory.createProto(
options.itemFactory.longType, options.itemFactory.doubleType),
options.itemFactory.createString("doubleToLongBits")),
@@ -1539,7 +1582,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Double;"),
+ options.itemFactory.createType("Ljava/lang/Double;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.doubleType),
options.itemFactory.createString("isInfinite")),
@@ -1549,7 +1592,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Double;"),
+ options.itemFactory.createType("Ljava/lang/Double;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.doubleType),
options.itemFactory.createString("isNaN")),
@@ -1581,7 +1624,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Float;"),
+ options.itemFactory.createType("Ljava/lang/Float;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.floatType),
options.itemFactory.createString("isInfinite")),
@@ -1591,7 +1634,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Float;"),
+ options.itemFactory.createType("Ljava/lang/Float;"),
options.itemFactory.createProto(
options.itemFactory.booleanType, options.itemFactory.floatType),
options.itemFactory.createString("isNaN")),
@@ -1666,7 +1709,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"),
+ options.itemFactory.createType("Ljava/lang/Integer;"),
options.itemFactory.createProto(
options.itemFactory.intType,
options.itemFactory.intType,
@@ -1726,7 +1769,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"),
+ options.itemFactory.createType("Ljava/lang/Integer;"),
options.itemFactory.createProto(
options.itemFactory.intType,
options.itemFactory.stringType,
@@ -1794,7 +1837,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.stringType,
@@ -1810,8 +1853,7 @@
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.EQ, ValueType.INT, label5),
label4,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -1878,7 +1920,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"),
+ options.itemFactory.createType("Ljava/lang/NumberFormatException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -1959,7 +2001,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"),
+ options.itemFactory.createType("Ljava/lang/Integer;"),
options.itemFactory.createProto(
options.itemFactory.stringType,
options.itemFactory.intType,
@@ -1994,7 +2036,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.stringType,
options.itemFactory.longType,
@@ -2033,7 +2075,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.intType,
options.itemFactory.longType,
@@ -2184,7 +2226,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.stringType,
@@ -2240,14 +2282,13 @@
new CfLoad(ValueType.INT, 2),
new CfIf(If.Type.NE, ValueType.INT, label3),
label2,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("empty string")),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"),
+ options.itemFactory.createType("Ljava/lang/NumberFormatException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -2261,8 +2302,7 @@
new CfConstNumber(36, ValueType.INT),
new CfIfCmp(If.Type.LE, ValueType.INT, label5),
label4,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("illegal radix: ")),
new CfLoad(ValueType.INT, 1),
@@ -2285,7 +2325,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"),
+ options.itemFactory.createType("Ljava/lang/NumberFormatException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -2298,7 +2338,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.longType,
@@ -2366,14 +2406,13 @@
new CfConstNumber(-1, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label15),
label14,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"),
+ options.itemFactory.createType("Ljava/lang/NumberFormatException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -2400,7 +2439,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.longType,
@@ -2410,8 +2449,7 @@
new CfNumberConversion(NumericType.LONG, NumericType.INT),
new CfIfCmp(If.Type.LE, ValueType.INT, label18),
label17,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NumberFormatException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("Too large for unsigned long: ")),
new CfLoad(ValueType.OBJECT, 0),
@@ -2426,7 +2464,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NumberFormatException;"),
+ options.itemFactory.createType("Ljava/lang/NumberFormatException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -2570,7 +2608,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.stringType,
options.itemFactory.longType,
@@ -2636,7 +2674,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.stringType,
options.itemFactory.longType,
@@ -2674,7 +2712,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Integer;"),
+ options.itemFactory.createType("Ljava/lang/Integer;"),
options.itemFactory.createProto(
options.itemFactory.intType, options.itemFactory.intType),
options.itemFactory.createString("numberOfTrailingZeros")),
@@ -2741,7 +2779,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.longType,
@@ -2870,12 +2908,12 @@
new CfLoad(ValueType.INT, 4),
new CfReturn(ValueType.INT),
label4,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -2934,12 +2972,12 @@
new CfLoad(ValueType.LONG, 4),
new CfReturn(ValueType.LONG),
label7,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -2964,12 +3002,12 @@
new CfConstNumber(-2147483648, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3000,12 +3038,12 @@
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3155,7 +3193,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Math;"),
+ options.itemFactory.createType("Ljava/lang/Math;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.longType,
@@ -3287,7 +3325,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Math;"),
+ options.itemFactory.createType("Ljava/lang/Math;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.longType,
@@ -3316,12 +3354,12 @@
new CfConstNumber(2147483647, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3352,12 +3390,12 @@
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3405,12 +3443,12 @@
new CfLoad(ValueType.INT, 4),
new CfReturn(ValueType.INT),
label4,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3449,7 +3487,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.intType, options.itemFactory.longType),
options.itemFactory.createString("numberOfLeadingZeros")),
@@ -3461,7 +3499,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.intType, options.itemFactory.longType),
options.itemFactory.createString("numberOfLeadingZeros")),
@@ -3472,7 +3510,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.intType, options.itemFactory.longType),
options.itemFactory.createString("numberOfLeadingZeros")),
@@ -3485,7 +3523,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Long;"),
+ options.itemFactory.createType("Ljava/lang/Long;"),
options.itemFactory.createProto(
options.itemFactory.intType, options.itemFactory.longType),
options.itemFactory.createString("numberOfLeadingZeros")),
@@ -3545,12 +3583,12 @@
new CfLoad(ValueType.LONG, 5),
new CfReturn(ValueType.LONG),
label15,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3575,7 +3613,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Math;"),
+ options.itemFactory.createType("Ljava/lang/Math;"),
options.itemFactory.createProto(
options.itemFactory.longType,
options.itemFactory.longType,
@@ -3727,12 +3765,12 @@
new CfConstNumber(-2147483648, ValueType.INT),
new CfIfCmp(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3762,12 +3800,12 @@
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3795,7 +3833,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Math;"),
+ options.itemFactory.createType("Ljava/lang/Math;"),
options.itemFactory.createProto(
options.itemFactory.doubleType, options.itemFactory.doubleType),
options.itemFactory.createString("nextUp")),
@@ -3821,7 +3859,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Math;"),
+ options.itemFactory.createType("Ljava/lang/Math;"),
options.itemFactory.createProto(
options.itemFactory.floatType, options.itemFactory.floatType),
options.itemFactory.createString("nextUp")),
@@ -3866,12 +3904,12 @@
new CfLoad(ValueType.INT, 4),
new CfReturn(ValueType.INT),
label4,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3930,12 +3968,12 @@
new CfLoad(ValueType.LONG, 4),
new CfReturn(ValueType.LONG),
label7,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -3967,12 +4005,12 @@
new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
new CfIf(If.Type.EQ, ValueType.INT, label3),
label2,
- new CfNew(options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/ArithmeticException;"),
+ options.itemFactory.createType("Ljava/lang/ArithmeticException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -4009,8 +4047,7 @@
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
new CfIfCmp(If.Type.LE, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -4103,8 +4140,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IndexOutOfBoundsException;"),
+ options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -4138,8 +4174,7 @@
new CfLoad(ValueType.INT, 2),
new CfIfCmp(If.Type.LE, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -4214,8 +4249,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IndexOutOfBoundsException;"),
+ options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -4246,8 +4280,7 @@
new CfLoad(ValueType.INT, 1),
new CfIfCmp(If.Type.LT, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/IndexOutOfBoundsException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -4304,8 +4337,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IndexOutOfBoundsException;"),
+ options.itemFactory.createType("Ljava/lang/IndexOutOfBoundsException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -4342,7 +4374,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Comparator;"),
+ options.itemFactory.createType("Ljava/util/Comparator;"),
options.itemFactory.createProto(
options.itemFactory.intType,
options.itemFactory.objectType,
@@ -4428,7 +4460,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.booleanArrayType,
@@ -4457,7 +4489,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.byteArrayType,
@@ -4486,7 +4518,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.charArrayType,
@@ -4515,7 +4547,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.doubleArrayType,
@@ -4544,7 +4576,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.floatArrayType,
@@ -4573,7 +4605,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.intArrayType,
@@ -4602,7 +4634,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.longArrayType,
@@ -4631,7 +4663,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
options.itemFactory.shortArrayType,
@@ -4647,24 +4679,24 @@
new CfReturn(ValueType.INT),
label34,
new CfLoad(ValueType.OBJECT, 0),
- new CfInstanceOf(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")),
new CfIf(If.Type.EQ, ValueType.INT, label38),
label35,
new CfLoad(ValueType.OBJECT, 1),
- new CfInstanceOf(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")),
new CfIf(If.Type.EQ, ValueType.INT, label36),
new CfLoad(ValueType.OBJECT, 0),
- new CfCheckCast(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ new CfCheckCast(options.itemFactory.createType("[Ljava/lang/Object;")),
new CfLoad(ValueType.OBJECT, 1),
- new CfCheckCast(options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ new CfCheckCast(options.itemFactory.createType("[Ljava/lang/Object;")),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Arrays;"),
+ options.itemFactory.createType("Ljava/util/Arrays;"),
options.itemFactory.createProto(
options.itemFactory.booleanType,
- options.itemFactory.createSynthesizedType("[Ljava/lang/Object;"),
- options.itemFactory.createSynthesizedType("[Ljava/lang/Object;")),
+ options.itemFactory.createType("[Ljava/lang/Object;"),
+ options.itemFactory.createType("[Ljava/lang/Object;")),
options.itemFactory.createString("deepEquals")),
false),
new CfIf(If.Type.EQ, ValueType.INT, label36),
@@ -4831,7 +4863,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType,
options.itemFactory.objectType,
@@ -4866,19 +4898,18 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType,
options.itemFactory.objectType,
options.itemFactory.stringType),
options.itemFactory.createString("requireNonNull")),
false),
- new CfCheckCast(
- options.itemFactory.createSynthesizedType("Ljava/util/function/Supplier;")),
+ new CfCheckCast(options.itemFactory.createType("Ljava/util/function/Supplier;")),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/function/Supplier;"),
+ options.itemFactory.createType("Ljava/util/function/Supplier;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("get")),
true),
@@ -4889,7 +4920,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType,
options.itemFactory.objectType,
@@ -4917,14 +4948,13 @@
new CfLoad(ValueType.OBJECT, 0),
new CfIf(If.Type.NE, ValueType.OBJECT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"),
+ options.itemFactory.createType("Ljava/lang/NullPointerException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -4952,7 +4982,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.stringType,
options.itemFactory.objectType,
@@ -5012,7 +5042,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Optional;"),
+ options.itemFactory.createType("Ljava/util/Optional;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5023,14 +5053,14 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Optional;"),
+ options.itemFactory.createType("Ljava/util/Optional;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("get")),
false),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/function/Consumer;"),
+ options.itemFactory.createType("Ljava/util/function/Consumer;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.objectType),
options.itemFactory.createString("accept")),
@@ -5041,7 +5071,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"),
+ options.itemFactory.createType("Ljava/lang/Runnable;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("run")),
true),
@@ -5069,7 +5099,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"),
+ options.itemFactory.createType("Ljava/util/OptionalDouble;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5080,15 +5110,14 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"),
+ options.itemFactory.createType("Ljava/util/OptionalDouble;"),
options.itemFactory.createProto(options.itemFactory.doubleType),
options.itemFactory.createString("getAsDouble")),
false),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/util/function/DoubleConsumer;"),
+ options.itemFactory.createType("Ljava/util/function/DoubleConsumer;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.doubleType),
options.itemFactory.createString("accept")),
@@ -5099,7 +5128,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"),
+ options.itemFactory.createType("Ljava/lang/Runnable;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("run")),
true),
@@ -5127,7 +5156,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"),
+ options.itemFactory.createType("Ljava/util/OptionalInt;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5138,14 +5167,14 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"),
+ options.itemFactory.createType("Ljava/util/OptionalInt;"),
options.itemFactory.createProto(options.itemFactory.intType),
options.itemFactory.createString("getAsInt")),
false),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/function/IntConsumer;"),
+ options.itemFactory.createType("Ljava/util/function/IntConsumer;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.intType),
options.itemFactory.createString("accept")),
@@ -5156,7 +5185,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"),
+ options.itemFactory.createType("Ljava/lang/Runnable;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("run")),
true),
@@ -5184,7 +5213,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"),
+ options.itemFactory.createType("Ljava/util/OptionalLong;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5195,14 +5224,14 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"),
+ options.itemFactory.createType("Ljava/util/OptionalLong;"),
options.itemFactory.createProto(options.itemFactory.longType),
options.itemFactory.createString("getAsLong")),
false),
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/function/LongConsumer;"),
+ options.itemFactory.createType("Ljava/util/function/LongConsumer;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.longType),
options.itemFactory.createString("accept")),
@@ -5213,7 +5242,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Runnable;"),
+ options.itemFactory.createType("Ljava/lang/Runnable;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("run")),
true),
@@ -5239,7 +5268,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Optional;"),
+ options.itemFactory.createType("Ljava/util/Optional;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5270,7 +5299,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"),
+ options.itemFactory.createType("Ljava/util/OptionalDouble;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5301,7 +5330,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"),
+ options.itemFactory.createType("Ljava/util/OptionalInt;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5332,7 +5361,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"),
+ options.itemFactory.createType("Ljava/util/OptionalLong;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5365,7 +5394,7 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
@@ -5376,7 +5405,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Optional;"),
+ options.itemFactory.createType("Ljava/util/Optional;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5389,23 +5418,23 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/function/Supplier;"),
+ options.itemFactory.createType("Ljava/util/function/Supplier;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("get")),
true),
- new CfCheckCast(options.itemFactory.createSynthesizedType("Ljava/util/Optional;")),
+ new CfCheckCast(options.itemFactory.createType("Ljava/util/Optional;")),
new CfStore(ValueType.OBJECT, 2),
label4,
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Objects;"),
+ options.itemFactory.createType("Ljava/util/Objects;"),
options.itemFactory.createProto(
options.itemFactory.objectType, options.itemFactory.objectType),
options.itemFactory.createString("requireNonNull")),
false),
- new CfCheckCast(options.itemFactory.createSynthesizedType("Ljava/util/Optional;")),
+ new CfCheckCast(options.itemFactory.createType("Ljava/util/Optional;")),
new CfReturn(ValueType.OBJECT),
label5),
ImmutableList.of(),
@@ -5427,7 +5456,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Optional;"),
+ options.itemFactory.createType("Ljava/util/Optional;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5437,16 +5466,16 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Optional;"),
+ options.itemFactory.createType("Ljava/util/Optional;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("get")),
false),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"),
+ options.itemFactory.createType("Ljava/util/stream/Stream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"),
+ options.itemFactory.createType("Ljava/util/stream/Stream;"),
options.itemFactory.objectType),
options.itemFactory.createString("of")),
true),
@@ -5455,9 +5484,9 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"),
+ options.itemFactory.createType("Ljava/util/stream/Stream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;")),
+ options.itemFactory.createType("Ljava/util/stream/Stream;")),
options.itemFactory.createString("empty")),
true),
new CfReturn(ValueType.OBJECT),
@@ -5481,7 +5510,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"),
+ options.itemFactory.createType("Ljava/util/OptionalDouble;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5491,17 +5520,16 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalDouble;"),
+ options.itemFactory.createType("Ljava/util/OptionalDouble;"),
options.itemFactory.createProto(options.itemFactory.doubleType),
options.itemFactory.createString("getAsDouble")),
false),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/DoubleStream;"),
+ options.itemFactory.createType("Ljava/util/stream/DoubleStream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType(
- "Ljava/util/stream/DoubleStream;"),
+ options.itemFactory.createType("Ljava/util/stream/DoubleStream;"),
options.itemFactory.doubleType),
options.itemFactory.createString("of")),
true),
@@ -5510,10 +5538,9 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/DoubleStream;"),
+ options.itemFactory.createType("Ljava/util/stream/DoubleStream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType(
- "Ljava/util/stream/DoubleStream;")),
+ options.itemFactory.createType("Ljava/util/stream/DoubleStream;")),
options.itemFactory.createString("empty")),
true),
new CfReturn(ValueType.OBJECT),
@@ -5537,7 +5564,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"),
+ options.itemFactory.createType("Ljava/util/OptionalInt;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5547,16 +5574,16 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalInt;"),
+ options.itemFactory.createType("Ljava/util/OptionalInt;"),
options.itemFactory.createProto(options.itemFactory.intType),
options.itemFactory.createString("getAsInt")),
false),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;"),
+ options.itemFactory.createType("Ljava/util/stream/IntStream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;"),
+ options.itemFactory.createType("Ljava/util/stream/IntStream;"),
options.itemFactory.intType),
options.itemFactory.createString("of")),
true),
@@ -5565,9 +5592,9 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;"),
+ options.itemFactory.createType("Ljava/util/stream/IntStream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/IntStream;")),
+ options.itemFactory.createType("Ljava/util/stream/IntStream;")),
options.itemFactory.createString("empty")),
true),
new CfReturn(ValueType.OBJECT),
@@ -5591,7 +5618,7 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"),
+ options.itemFactory.createType("Ljava/util/OptionalLong;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("isPresent")),
false),
@@ -5601,16 +5628,16 @@
new CfInvoke(
182,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/OptionalLong;"),
+ options.itemFactory.createType("Ljava/util/OptionalLong;"),
options.itemFactory.createProto(options.itemFactory.longType),
options.itemFactory.createString("getAsLong")),
false),
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;"),
+ options.itemFactory.createType("Ljava/util/stream/LongStream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;"),
+ options.itemFactory.createType("Ljava/util/stream/LongStream;"),
options.itemFactory.longType),
options.itemFactory.createString("of")),
true),
@@ -5619,9 +5646,9 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;"),
+ options.itemFactory.createType("Ljava/util/stream/LongStream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/LongStream;")),
+ options.itemFactory.createType("Ljava/util/stream/LongStream;")),
options.itemFactory.createString("empty")),
true),
new CfReturn(ValueType.OBJECT),
@@ -5723,9 +5750,9 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"),
+ options.itemFactory.createType("Ljava/util/stream/Stream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;")),
+ options.itemFactory.createType("Ljava/util/stream/Stream;")),
options.itemFactory.createString("empty")),
true),
new CfGoto(label2),
@@ -5734,9 +5761,9 @@
new CfInvoke(
184,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"),
+ options.itemFactory.createType("Ljava/util/stream/Stream;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/stream/Stream;"),
+ options.itemFactory.createType("Ljava/util/stream/Stream;"),
options.itemFactory.objectType),
options.itemFactory.createString("of")),
true),
@@ -5849,14 +5876,13 @@
label0,
new CfLoad(ValueType.OBJECT, 0),
new CfIf(If.Type.NE, ValueType.OBJECT, label1),
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("delimiter")),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"),
+ options.itemFactory.createType("Ljava/lang/NullPointerException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -5965,14 +5991,13 @@
label0,
new CfLoad(ValueType.OBJECT, 0),
new CfIf(If.Type.NE, ValueType.OBJECT, label1),
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfConstString(options.itemFactory.createString("delimiter")),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"),
+ options.itemFactory.createType("Ljava/lang/NullPointerException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
@@ -5994,9 +6019,9 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/Iterable;"),
+ options.itemFactory.createType("Ljava/lang/Iterable;"),
options.itemFactory.createProto(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;")),
+ options.itemFactory.createType("Ljava/util/Iterator;")),
options.itemFactory.createString("iterator")),
true),
new CfStore(ValueType.OBJECT, 3),
@@ -6005,7 +6030,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("hasNext")),
true),
@@ -6016,7 +6041,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("next")),
true),
@@ -6036,7 +6061,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.booleanType),
options.itemFactory.createString("hasNext")),
true),
@@ -6060,7 +6085,7 @@
new CfInvoke(
185,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/util/Iterator;"),
+ options.itemFactory.createType("Ljava/util/Iterator;"),
options.itemFactory.createProto(options.itemFactory.objectType),
options.itemFactory.createString("next")),
true),
@@ -6115,8 +6140,7 @@
new CfLoad(ValueType.INT, 1),
new CfIf(If.Type.GE, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/IllegalArgumentException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/IllegalArgumentException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -6155,8 +6179,7 @@
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType(
- "Ljava/lang/IllegalArgumentException;"),
+ options.itemFactory.createType("Ljava/lang/IllegalArgumentException;"),
options.itemFactory.createProto(
options.itemFactory.voidType, options.itemFactory.stringType),
options.itemFactory.createString("<init>")),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index da20118..a356e8b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -112,18 +112,18 @@
break;
case PACKAGE:
if (entry.value.size == 0) {
- if (!method.method.holder.descriptor.contains(dexItemFactory.descriptorSeparator)) {
+ if (!method.holder().descriptor.contains(dexItemFactory.descriptorSeparator)) {
transformation = entry.entry.getTransformation();
}
- } else if (method.method.holder.descriptor.startsWith(entry.value)) {
+ } else if (method.holder().descriptor.startsWith(entry.value)) {
transformation = entry.entry.getTransformation();
}
break;
case CLASS:
- if (method.method.holder.descriptor.equals(entry.value)) {
+ if (method.holder().descriptor.equals(entry.value)) {
transformation = entry.entry.getTransformation();
}
- if (isDescriptorForClassOrInnerClass(entry.value, method.method.holder.descriptor)) {
+ if (isDescriptorForClassOrInnerClass(entry.value, method.holder().descriptor)) {
transformation = entry.entry.getTransformation();
}
break;
@@ -311,7 +311,7 @@
if (method.isClassInitializer()) {
clinit = method;
} else {
- DexClass clazz = appView.definitionFor(method.method.holder);
+ DexClass clazz = appView.definitionFor(method.holder());
if (clazz == null) {
return;
}
@@ -328,7 +328,7 @@
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
- if (method.method.holder == dexItemFactory.kotlin.kotlinAssertions) {
+ if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) {
rewriteKotlinAssertionEnable(code, transformation, iterator, invoke);
} else {
iterator.replaceCurrentInstruction(code.createIntConstant(0));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 368b58a..35895b7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -98,7 +98,7 @@
continue;
}
}
- Collection<DexEncodedMethod> targets = invoke.lookupTargets(appView, context.method.holder);
+ Collection<DexEncodedMethod> targets = invoke.lookupTargets(appView, context.holder());
assert invoke.isInvokeMethodWithDynamicDispatch()
// For other invocation types, the size of targets should be at most one.
|| targets == null || targets.size() <= 1;
@@ -231,7 +231,7 @@
if (abstractValue.isSingleValue()) {
assert appView.options().enablePropagationOfConstantsAtCallSites;
SingleValue singleValue = abstractValue.asSingleValue();
- if (singleValue.isMaterializableInContext(appView, code.method.method.holder)) {
+ if (singleValue.isMaterializableInContext(appView, code.method.holder())) {
Instruction replacement =
singleValue.createMaterializingInstruction(appView, code, instr);
replacement.setPosition(instr.getPosition());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index 025c178..ce853a2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -133,7 +133,7 @@
return ClassInitializerDefaultsResult.empty();
}
- DexClass clazz = appView.definitionFor(method.method.holder);
+ DexClass clazz = appView.definitionFor(method.holder());
if (clazz == null) {
return ClassInitializerDefaultsResult.empty();
}
@@ -160,7 +160,7 @@
Value value = put.value();
if (unnecessaryStaticPuts.contains(put)) {
if (fieldType == dexItemFactory.stringType) {
- fieldsWithStaticValues.put(field, getDexStringValue(value, method.method.holder));
+ fieldsWithStaticValues.put(field, getDexStringValue(value, method.holder()));
} else if (fieldType.isClassType() || fieldType.isArrayType()) {
if (value.isZero()) {
fieldsWithStaticValues.put(field, DexValueNull.NULL);
@@ -380,7 +380,7 @@
} else if (instruction.isStaticGet()) {
StaticGet get = instruction.asStaticGet();
DexEncodedField field = appView.appInfo().resolveField(get.getField());
- if (field != null && field.field.holder == clazz.type) {
+ if (field != null && field.holder() == clazz.type) {
isReadBefore.add(field.field);
} else if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) {
// Reading another field is only OK if the read does not have side-effects.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5b78d8a..fee1f02 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1115,7 +1115,7 @@
BasicBlock defaultTarget = theSwitch.fallthroughBlock();
SwitchCaseEliminator eliminator = null;
BasicBlockBehavioralSubsumption behavioralSubsumption =
- new BasicBlockBehavioralSubsumption(appView, code.method.method.holder);
+ new BasicBlockBehavioralSubsumption(appView, code.method.holder());
// Compute the set of switch cases that can be removed.
for (int i = 0; i < theSwitch.numberOfKeys(); i++) {
@@ -1231,7 +1231,7 @@
}
// Check if the invoked method is known to return one of its arguments.
- DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.method.holder);
+ DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.holder());
if (target != null && target.getOptimizationInfo().returnsArgument()) {
int argumentIndex = target.getOptimizationInfo().getReturnedArgument();
// Replace the out value of the invoke with the argument and ignore the out value.
@@ -1353,7 +1353,7 @@
// If the cast type is not accessible in the current context, we should not remove the cast
// in order to preserve IllegalAccessError. Note that JVM and ART behave differently: see
// {@link com.android.tools.r8.ir.optimize.checkcast.IllegalAccessErrorTest}.
- if (!isTypeVisibleFromContext(appView, code.method.method.holder, castType)) {
+ if (!isTypeVisibleFromContext(appView, code.method.holder(), castType)) {
return RemoveCheckCastInstructionIfTrivialResult.NO_REMOVALS;
}
@@ -1410,7 +1410,7 @@
InstanceOf instanceOf, InstructionListIterator it, IRCode code) {
// If the instance-of type is not accessible in the current context, we should not remove the
// instance-of instruction in order to preserve IllegalAccessError.
- if (!isTypeVisibleFromContext(appView, code.method.method.holder, instanceOf.type())) {
+ if (!isTypeVisibleFromContext(appView, code.method.holder(), instanceOf.type())) {
return false;
}
@@ -2050,8 +2050,7 @@
for (ConstInstruction value : values) {
stringValues.add(value.outValue());
}
- Value invokeValue =
- code.createValue(newArray.outValue().getType(), newArray.getLocalInfo());
+ Value invokeValue = code.createValue(newArray.getOutType(), newArray.getLocalInfo());
InvokeNewArray invoke =
new InvokeNewArray(dexItemFactory.stringArrayType, invokeValue, stringValues);
for (Value value : newArray.inValues()) {
@@ -2469,7 +2468,7 @@
}
}
} else {
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
AbstractValue abstractValue = lhs.getAbstractValue(appView, context);
if (abstractValue.isSingleConstClassValue() || abstractValue.isSingleFieldValue()) {
AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
@@ -2761,7 +2760,7 @@
InvokeMethod invoke = insn.asInvokeMethod();
DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView.withLiveness(), code.method.method.holder);
+ invoke.lookupSingleTarget(appView.withLiveness(), code.method.holder());
if (singleTarget == null || !singleTarget.getOptimizationInfo().neverReturnsNormally()) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 2dff525..c98c262 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -88,7 +88,7 @@
public void canonicalize(AppView<?> appView, IRCode code) {
DexEncodedMethod method = code.method;
- DexType context = method.method.holder;
+ DexType context = method.holder();
Object2ObjectLinkedOpenCustomHashMap<Instruction, List<Value>> valuesDefinedByConstant =
new Object2ObjectLinkedOpenCustomHashMap<>(
new Strategy<Instruction>() {
@@ -149,8 +149,7 @@
continue;
}
SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
- if (method.isClassInitializer()
- && method.method.holder == singleFieldValue.getField().holder) {
+ if (method.isClassInitializer() && method.holder() == singleFieldValue.getField().holder) {
// Avoid that canonicalization inserts a read before the unique write in the class
// initializer, as that would change the program behavior.
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index d225220..d9773d9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -105,7 +105,7 @@
return true;
}
- DexClass clazz = appView.definitionFor(singleTarget.method.holder);
+ DexClass clazz = appView.definitionFor(singleTarget.holder());
if (!clazz.isProgramClass()) {
if (clazz.isClasspathClass()) {
whyAreYouNotInliningReporter.reportClasspathMethod();
@@ -211,12 +211,12 @@
// Don't inline code with references beyond root main dex classes into a root main dex class.
// If we do this it can increase the size of the main dex dependent classes.
if (reason != Reason.FORCE
- && inlineeRefersToClassesNotInMainDex(method.method.holder, singleTarget)) {
+ && inlineeRefersToClassesNotInMainDex(method.holder(), singleTarget)) {
whyAreYouNotInliningReporter.reportInlineeRefersToClassesNotInMainDex();
return false;
}
assert reason != Reason.FORCE
- || !inlineeRefersToClassesNotInMainDex(method.method.holder, singleTarget);
+ || !inlineeRefersToClassesNotInMainDex(method.holder(), singleTarget);
return true;
}
@@ -360,8 +360,8 @@
// - the current method has already triggered the holder for the target method to be
// initialized, or
// - there is no non-trivial class initializer.
- DexType targetHolder = target.method.holder;
- if (appView.appInfo().isSubtype(method.method.holder, targetHolder)) {
+ DexType targetHolder = target.holder();
+ if (appView.appInfo().isSubtype(method.holder(), targetHolder)) {
return true;
}
DexClass clazz = appView.definitionFor(targetHolder);
@@ -374,14 +374,14 @@
appView.withInitializedClassesInInstanceMethods(
analysis ->
analysis.isClassDefinitelyLoadedInInstanceMethodsOn(
- target.method.holder, method.method.holder),
+ target.holder(), method.holder()),
false);
if (targetIsGuaranteedToBeInitialized) {
return true;
}
}
if (classInitializationAnalysis.isClassDefinitelyLoadedBeforeInstruction(
- target.method.holder, invoke)) {
+ target.holder(), invoke)) {
return true;
}
// Check for class initializer side effects when loading this class, as inlining might remove
@@ -445,8 +445,8 @@
// Allow inlining a constructor into a constructor of the same class, as the constructor code
// is expected to adhere to the VM specification.
- DexType callerMethodHolder = method.method.holder;
- DexType calleeMethodHolder = inlinee.method.method.holder;
+ DexType callerMethodHolder = method.holder();
+ DexType calleeMethodHolder = inlinee.method.holder();
// Calling a constructor on the same class from a constructor can always be inlined.
if (method.isInstanceInitializer() && callerMethodHolder == calleeMethodHolder) {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 82ea8ab..897b85b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -136,7 +136,7 @@
if (target == null) {
continue;
}
- DexType holderType = target.method.holder;
+ DexType holderType = target.holder();
DexClass holderClass = appView.definitionFor(holderType);
// Make sure we are not landing on another interface, e.g., interface's default method.
if (holderClass == null || holderClass.isInterface()) {
@@ -284,7 +284,7 @@
// Most likely due to a missing class, or invoke is already as specific as it gets.
return target;
}
- DexClass newTargetClass = appView.definitionFor(newTarget.method.holder);
+ DexClass newTargetClass = appView.definitionFor(newTarget.holder());
if (newTargetClass == null
|| newTargetClass.isLibraryClass()
|| !canInvokeTargetWithInvokeVirtual(newTarget)
@@ -296,13 +296,12 @@
}
private boolean canInvokeTargetWithInvokeVirtual(DexEncodedMethod target) {
- return target.isNonPrivateVirtualMethod()
- && appView.isInterface(target.method.holder).isFalse();
+ return target.isNonPrivateVirtualMethod() && appView.isInterface(target.holder()).isFalse();
}
private boolean hasAccessToInvokeTargetFromContext(DexEncodedMethod target, DexType context) {
assert !target.accessFlags.isPrivate();
- DexType holder = target.method.holder;
+ DexType holder = target.holder();
if (holder == context) {
// It is always safe to invoke a method from the same enclosing class.
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index 8048e3a..d518ef6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -78,8 +78,7 @@
TypeElement.fromDexType(invokedMethod.holder, definitelyNotNull(), appView);
dynamicLowerBoundType = null;
} else {
- DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView, code.method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
if (singleTarget == null) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 5dba772..76ca615 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -110,7 +110,7 @@
}
});
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
// Collect invocations along with arguments.
for (BasicBlock block : code.blocks) {
for (Instruction current : block.getInstructions()) {
@@ -143,7 +143,7 @@
}
// Verify that the target method is accessible in the current context.
if (!isMemberVisibleFromOriginalContext(
- appView, context, target.method.holder, target.accessFlags)) {
+ appView, context, target.holder(), target.accessFlags)) {
continue;
}
// Check if the call could throw a NPE as a result of the receiver being null.
@@ -207,7 +207,7 @@
}
}
Value canonicalizedValue =
- code.createValue(invoke.outValue().getType(), invoke.outValue().getLocalInfo());
+ code.createValue(invoke.getOutType(), invoke.outValue().getLocalInfo());
Invoke canonicalizedInvoke =
Invoke.create(
invoke.getType(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index b56dece..33d982a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -164,7 +164,7 @@
new InliningConstraints(appView, GraphLense.getIdentityLense());
for (Instruction instruction : code.instructions()) {
ConstraintWithTarget state =
- instructionAllowedForInlining(instruction, inliningConstraints, method.method.holder);
+ instructionAllowedForInlining(instruction, inliningConstraints, method.holder());
if (state == ConstraintWithTarget.NEVER) {
result = state;
break;
@@ -192,12 +192,12 @@
}
boolean hasInliningAccess(DexEncodedMethod method, DexEncodedMethod target) {
- if (!isVisibleWithFlags(target.method.holder, method.method.holder, target.accessFlags)) {
+ if (!isVisibleWithFlags(target.holder(), method.holder(), target.accessFlags)) {
return false;
}
// The class needs also to be visible for us to have access.
- DexClass targetClass = appView.definitionFor(target.method.holder);
- return isVisibleWithFlags(target.method.holder, method.method.holder, targetClass.accessFlags);
+ DexClass targetClass = appView.definitionFor(target.holder());
+ return isVisibleWithFlags(target.holder(), method.holder(), targetClass.accessFlags);
}
private boolean isVisibleWithFlags(DexType target, DexType context, AccessFlags flags) {
@@ -710,7 +710,7 @@
lockValue =
code.createValue(
TypeElement.fromDexType(dexItemFactory.objectType, definitelyNotNull(), appView));
- monitorEnterBlockIterator.add(new ConstClass(lockValue, target.method.holder));
+ monitorEnterBlockIterator.add(new ConstClass(lockValue, target.holder()));
} else {
lockValue = entryBlock.getInstructions().getFirst().asArgument().outValue();
}
@@ -738,10 +738,10 @@
if (inliningIRProvider.shouldApplyCodeRewritings(code.method)) {
assert lensCodeRewriter != null;
- lensCodeRewriter.rewrite(code, target);
if (enumUnboxer != null) {
enumUnboxer.rewriteCode(code);
}
+ lensCodeRewriter.rewrite(code, target);
}
if (lambdaMerger != null) {
lambdaMerger.rewriteCodeForInlining(target, code, context, inliningIRProvider);
@@ -964,7 +964,7 @@
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
// TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget()!
- DexEncodedMethod singleTarget = oracle.lookupSingleTarget(invoke, context.method.holder);
+ DexEncodedMethod singleTarget = oracle.lookupSingleTarget(invoke, context.holder());
if (singleTarget == null) {
WhyAreYouNotInliningReporter.handleInvokeWithUnknownTarget(invoke, appView, context);
continue;
@@ -1115,8 +1115,8 @@
// method holder as a fallback.
receiverType = invoke.getInvokedMethod().holder;
}
- if (!appView.appInfo().isSubtype(receiverType, target.method.holder)) {
- return target.method.holder;
+ if (!appView.appInfo().isSubtype(receiverType, target.holder())) {
+ return target.holder();
}
}
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index a1307a0..ea77caf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -197,7 +197,7 @@
DexEncodedMethod alternativeDexEncodedMethod =
lookupFunction.apply(singleResolutionResult, superContext);
if (alternativeDexEncodedMethod != null
- && alternativeDexEncodedMethod.method.holder == superContext.type) {
+ && alternativeDexEncodedMethod.holder() == superContext.type) {
return alternativeDexEncodedMethod;
}
return null;
@@ -354,8 +354,8 @@
// `invocationContext` has access to the definition of the field.
//
// See, for example, InlineNonReboundFieldTest (b/128604123).
- if (field.holder != target.field.holder) {
- DexType actualFieldHolder = graphLense.lookupType(target.field.holder);
+ if (field.holder != target.holder()) {
+ DexType actualFieldHolder = graphLense.lookupType(target.holder());
fieldConstraintWithTarget =
ConstraintWithTarget.meet(
fieldConstraintWithTarget,
@@ -379,7 +379,7 @@
return ConstraintWithTarget.ALWAYS;
}
if (target != null) {
- DexType methodHolder = graphLense.lookupType(target.method.holder);
+ DexType methodHolder = graphLense.lookupType(target.holder());
DexClass methodClass = appView.definitionFor(methodHolder);
if (methodClass != null) {
if (!allowStaticInterfaceMethodCalls && methodClass.isInterface() && target.hasCode()) {
@@ -423,7 +423,7 @@
return ConstraintWithTarget.NEVER;
}
- DexType methodHolder = graphLense.lookupType(resolutionTarget.method.holder);
+ DexType methodHolder = graphLense.lookupType(resolutionTarget.holder());
DexClass methodClass = appView.definitionFor(methodHolder);
assert methodClass != null;
ConstraintWithTarget methodConstraintWithTarget =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 4bf7a6d..bf7aa9b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinition;
@@ -14,7 +15,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -139,10 +139,9 @@
.createMaterializingInstruction(appView, code, instruction);
}
- TypeElement typeLattice = instruction.outValue().getType();
if (returnValueRule.isField()) {
DexField field = returnValueRule.getField();
- assert typeLattice == TypeElement.fromDexType(field.type, Nullability.maybeNull(), appView);
+ assert instruction.getOutType() == TypeElement.fromDexType(field.type, maybeNull(), appView);
DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field.holder, field);
if (staticField == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
index c6694f4..508e394 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
@@ -46,7 +46,7 @@
InstructionListIterator iterator = code.instructionListIterator();
DexClass callerHolderClass = appView.definitionFor(callerHolder);
assert callerHolderClass != null;
- assert code.method.method.holder != callerHolder;
+ assert code.method.holder() != callerHolder;
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
if (instruction.isInvokeDirect()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 15904c5..2e41703 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -98,8 +98,7 @@
}
}
- DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView, code.method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
if (singleTarget != null) {
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index ab9fa71..d9ebcf6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -862,7 +862,7 @@
// See whether we could move this invoke somewhere else. We reuse the logic from inlining
// here, as the constraints are the same.
ConstraintWithTarget constraint =
- invoke.inliningConstraint(inliningConstraints, method.method.holder);
+ invoke.inliningConstraint(inliningConstraints, method.holder());
if (constraint != ConstraintWithTarget.ALWAYS) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index de9d4cc..81b32e8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -4,12 +4,15 @@
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -29,12 +32,11 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
-import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.HashMap;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -56,18 +58,11 @@
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
// Maps keeping track of fields that have an already loaded value at basic block entry.
- private final Map<BasicBlock, Set<DexType>> activeInitializedClassesAtEntry =
- new IdentityHashMap<>();
- private final Map<BasicBlock, Map<FieldAndObject, FieldValue>> activeInstanceFieldsAtEntry =
- new IdentityHashMap<>();
- private final Map<BasicBlock, Map<DexField, FieldValue>> activeStaticFieldsAtEntry =
- new IdentityHashMap<>();
+ private final Map<BasicBlock, State> activeStateAtExit = new IdentityHashMap<>();
// Maps keeping track of fields with already loaded values for the current block during
// elimination.
- private Set<DexType> activeInitializedClasses;
- private Map<FieldAndObject, FieldValue> activeInstanceFieldValues;
- private Map<DexField, FieldValue> activeStaticFieldValues;
+ private State activeState;
public RedundantFieldLoadElimination(AppView<?> appView, IRCode code) {
this.appView = appView;
@@ -78,7 +73,7 @@
public static boolean shouldRun(AppView<?> appView, IRCode code) {
return appView.options().enableRedundantFieldLoadElimination
- && code.metadata().mayHaveFieldGet();
+ && (code.metadata().mayHaveFieldGet() || code.metadata().mayHaveInitClass());
}
private interface FieldValue {
@@ -145,41 +140,28 @@
}
}
- private boolean couldBeVolatile(DexField field) {
- DexEncodedField definition;
+ private DexEncodedField resolveField(DexField field) {
if (appView.enableWholeProgramOptimizations()) {
- definition = appView.appInfo().resolveField(field);
- } else {
- if (field.holder != method.method.holder) {
- return true;
- }
- definition = appView.definitionFor(field);
+ return appView.appInfo().resolveField(field);
}
- return definition == null || definition.accessFlags.isVolatile();
+ if (field.holder == method.holder()) {
+ return appView.definitionFor(field);
+ }
+ return null;
}
public void run() {
- DexType context = method.method.holder;
+ DexType context = method.holder();
for (BasicBlock block : dominatorTree.getSortedBlocks()) {
- activeInitializedClasses =
- activeInitializedClassesAtEntry.containsKey(block)
- ? activeInitializedClassesAtEntry.get(block)
- : Sets.newIdentityHashSet();
- activeInstanceFieldValues =
- activeInstanceFieldsAtEntry.containsKey(block)
- ? activeInstanceFieldsAtEntry.get(block)
- : new HashMap<>();
- activeStaticFieldValues =
- activeStaticFieldsAtEntry.containsKey(block)
- ? activeStaticFieldsAtEntry.get(block)
- : new IdentityHashMap<>();
+ computeActiveStateOnBlockEntry(block);
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
Instruction instruction = it.next();
if (instruction.isFieldInstruction()) {
DexField field = instruction.asFieldInstruction().getField();
- if (couldBeVolatile(field)) {
- killAllActiveFields();
+ DexEncodedField definition = resolveField(field);
+ if (definition == null || definition.isVolatile()) {
+ killAllNonFinalActiveFields();
continue;
}
@@ -190,56 +172,71 @@
}
Value object = instanceGet.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- if (activeInstanceFieldValues.containsKey(fieldAndObject)) {
- FieldValue replacement = activeInstanceFieldValues.get(fieldAndObject);
+ FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
+ if (replacement != null) {
replacement.eliminateRedundantRead(it, instanceGet);
} else {
- activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(instanceGet.value()));
+ activeState.putNonFinalInstanceField(
+ fieldAndObject, new ExistingValue(instanceGet.value()));
}
} else if (instruction.isInstancePut()) {
InstancePut instancePut = instruction.asInstancePut();
// An instance-put instruction can potentially write the given field on all objects
// because of aliases.
- killActiveFields(instancePut);
+ killNonFinalActiveFields(instancePut);
// ... but at least we know the field value for this particular object.
Value object = instancePut.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(instancePut.value()));
+ ExistingValue value = new ExistingValue(instancePut.value());
+ if (definition.isFinal()) {
+ assert method.isInstanceInitializer() || verifyWasInstanceInitializer();
+ activeState.putFinalInstanceField(fieldAndObject, value);
+ } else {
+ activeState.putNonFinalInstanceField(fieldAndObject, value);
+ }
} else if (instruction.isStaticGet()) {
StaticGet staticGet = instruction.asStaticGet();
if (staticGet.outValue().hasLocalInfo()) {
continue;
}
- if (activeStaticFieldValues.containsKey(field)) {
- FieldValue replacement = activeStaticFieldValues.get(field);
+ FieldValue replacement = activeState.getStaticFieldValue(field);
+ if (replacement != null) {
replacement.eliminateRedundantRead(it, staticGet);
} else {
// A field get on a different class can cause <clinit> to run and change static
// field values.
- killActiveFields(staticGet);
- activeStaticFieldValues.put(field, new ExistingValue(staticGet.value()));
+ killNonFinalActiveFields(staticGet);
+ activeState.putNonFinalStaticField(field, new ExistingValue(staticGet.value()));
}
} else if (instruction.isStaticPut()) {
StaticPut staticPut = instruction.asStaticPut();
// A field put on a different class can cause <clinit> to run and change static
// field values.
- killActiveFields(staticPut);
- activeStaticFieldValues.put(field, new ExistingValue(staticPut.value()));
+ killNonFinalActiveFields(staticPut);
+ ExistingValue value = new ExistingValue(staticPut.value());
+ if (definition.isFinal()) {
+ assert method.isClassInitializer();
+ activeState.putFinalStaticField(field, value);
+ } else {
+ activeState.putNonFinalStaticField(field, value);
+ }
}
} else if (instruction.isInitClass()) {
InitClass initClass = instruction.asInitClass();
assert !initClass.outValue().hasAnyUsers();
- if (activeInitializedClasses.contains(initClass.getClassValue())) {
+ DexType clazz = initClass.getClassValue();
+ if (activeState.isClassInitialized(clazz)) {
it.removeOrReplaceByDebugLocalRead();
}
+ activeState.markClassAsInitialized(clazz);
} else if (instruction.isMonitor()) {
if (instruction.asMonitor().isEnter()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
}
} else if (instruction.isInvokeDirect()) {
handleInvokeDirect(instruction.asInvokeDirect());
} else if (instruction.isInvokeMethod() || instruction.isInvokeCustom()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
} else if (instruction.isNewInstance()) {
NewInstance newInstance = instruction.asNewInstance();
if (newInstance.clazz.classInitializationMayHaveSideEffects(
@@ -247,7 +244,7 @@
// Types that are a super type of `context` are guaranteed to be initialized already.
type -> appView.isSubtype(context, type).isTrue(),
Sets.newIdentityHashSet())) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
}
} else {
// If the current instruction could trigger a method invocation, it could also cause field
@@ -286,7 +283,7 @@
: "Unexpected instruction of type " + instruction.getClass().getTypeName();
}
}
- propagateActiveStateFrom(block);
+ recordActiveStateOnBlockExit(block);
}
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
@@ -294,22 +291,33 @@
assert code.isConsistentSSA();
}
+ private boolean verifyWasInstanceInitializer() {
+ VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
+ assert verticallyMergedClasses != null;
+ assert verticallyMergedClasses.isTarget(method.holder());
+ assert appView
+ .dexItemFactory()
+ .isConstructor(appView.graphLense().getOriginalMethodSignature(method.method));
+ assert method.getOptimizationInfo().forceInline();
+ return true;
+ }
+
private void handleInvokeDirect(InvokeDirect invoke) {
if (!appView.enableWholeProgramOptimizations()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
return;
}
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder());
if (singleTarget == null || !singleTarget.isInstanceInitializer()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
return;
}
InstanceInitializerInfo instanceInitializerInfo =
singleTarget.getOptimizationInfo().getInstanceInitializerInfo();
if (instanceInitializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
}
InstanceFieldInitializationInfoCollection fieldInitializationInfos =
@@ -325,13 +333,13 @@
invoke.getArgument(info.asArgumentInitializationInfo().getArgumentIndex());
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
- activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(value));
+ activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value));
} else if (info.isSingleValue()) {
SingleValue value = info.asSingleValue();
if (value.isMaterializableInContext(appView, method.holder())) {
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
- activeInstanceFieldValues.put(fieldAndObject, new MaterializableValue(value));
+ activeState.putNonFinalInstanceField(fieldAndObject, new MaterializableValue(value));
}
} else {
assert info.isTypeInitializationInfo();
@@ -339,84 +347,206 @@
});
}
- private void propagateActiveStateFrom(BasicBlock block) {
- for (BasicBlock successor : block.getSuccessors()) {
+ private void computeActiveStateOnBlockEntry(BasicBlock block) {
+ if (block.isEntry()) {
+ activeState = new State();
+ return;
+ }
+ Deque<State> predecessorExitStates = new ArrayDeque<>(block.getPredecessors().size());
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ State predecessorExitState = activeStateAtExit.get(predecessor);
+ if (predecessorExitState == null) {
+ // Not processed yet.
+ activeState = new State();
+ return;
+ }
// Allow propagation across exceptional edges, just be careful not to propagate if the
// throwing instruction is a field instruction.
- if (successor.getPredecessors().size() == 1) {
- if (block.hasCatchSuccessor(successor)) {
- Instruction exceptionalExit = block.exceptionalExit();
- if (exceptionalExit != null) {
- if (exceptionalExit.isFieldInstruction()) {
- killActiveFieldsForExceptionalExit(exceptionalExit.asFieldInstruction());
- } else if (exceptionalExit.isInitClass()) {
- killActiveInitializedClassesForExceptionalExit(exceptionalExit.asInitClass());
- }
+ if (predecessor.hasCatchSuccessor(block)) {
+ Instruction exceptionalExit = predecessor.exceptionalExit();
+ if (exceptionalExit != null) {
+ predecessorExitState = new State(predecessorExitState);
+ if (exceptionalExit.isFieldInstruction()) {
+ predecessorExitState.killActiveFieldsForExceptionalExit(
+ exceptionalExit.asFieldInstruction());
+ } else if (exceptionalExit.isInitClass()) {
+ predecessorExitState.killActiveInitializedClassesForExceptionalExit(
+ exceptionalExit.asInitClass());
}
}
- assert !activeInitializedClassesAtEntry.containsKey(successor);
- activeInitializedClassesAtEntry.put(
- successor, SetUtils.newIdentityHashSet(activeInitializedClasses));
- assert !activeInstanceFieldsAtEntry.containsKey(successor);
- activeInstanceFieldsAtEntry.put(successor, new HashMap<>(activeInstanceFieldValues));
- assert !activeStaticFieldsAtEntry.containsKey(successor);
- activeStaticFieldsAtEntry.put(successor, new IdentityHashMap<>(activeStaticFieldValues));
}
+ predecessorExitStates.addLast(predecessorExitState);
}
+ State state = new State(predecessorExitStates.removeFirst());
+ predecessorExitStates.forEach(state::intersect);
+ activeState = state;
}
- private void killAllActiveFields() {
- activeInstanceFieldValues.clear();
- activeStaticFieldValues.clear();
+ private void recordActiveStateOnBlockExit(BasicBlock block) {
+ assert !activeStateAtExit.containsKey(block);
+ activeStateAtExit.put(block, activeState);
}
- private void killActiveFields(FieldInstruction instruction) {
+ private void killAllNonFinalActiveFields() {
+ activeState.clearNonFinalInstanceFields();
+ activeState.clearNonFinalStaticFields();
+ }
+
+ private void killNonFinalActiveFields(FieldInstruction instruction) {
DexField field = instruction.getField();
if (instruction.isInstancePut()) {
// Remove all the field/object pairs that refer to this field to make sure
// that we are conservative.
- List<FieldAndObject> keysToRemove = new ArrayList<>();
- for (FieldAndObject key : activeInstanceFieldValues.keySet()) {
- if (key.field == field) {
- keysToRemove.add(key);
- }
- }
- keysToRemove.forEach(activeInstanceFieldValues::remove);
+ activeState.removeNonFinalInstanceFields(field);
} else if (instruction.isStaticPut()) {
- if (field.holder != code.method.method.holder) {
+ if (field.holder != code.method.holder()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
- activeStaticFieldValues.clear();
+ activeState.clearNonFinalStaticFields();
} else {
- activeStaticFieldValues.remove(field);
+ activeState.removeNonFinalStaticField(field);
}
} else if (instruction.isStaticGet()) {
- if (field.holder != code.method.method.holder) {
+ if (field.holder != code.method.holder()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
- activeStaticFieldValues.clear();
+ activeState.clearNonFinalStaticFields();
}
} else if (instruction.isInstanceGet()) {
throw new Unreachable();
}
}
- // If a field get instruction throws an exception it did not have an effect on the
- // value of the field. Therefore, when propagating across exceptional edges for a
- // field get instruction we have to exclude that field from the set of known
- // field values.
- private void killActiveFieldsForExceptionalExit(FieldInstruction instruction) {
- DexField field = instruction.getField();
- if (instruction.isInstanceGet()) {
- Value object = instruction.asInstanceGet().object().getAliasedValue();
- FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- activeInstanceFieldValues.remove(fieldAndObject);
- } else if (instruction.isStaticGet()) {
- activeStaticFieldValues.remove(field);
- }
- }
+ static class State {
- private void killActiveInitializedClassesForExceptionalExit(InitClass instruction) {
- activeInitializedClasses.remove(instruction.getClassValue());
+ private final Map<FieldAndObject, FieldValue> finalInstanceFieldValues = new HashMap<>();
+
+ private final Map<DexField, FieldValue> finalStaticFieldValues = new IdentityHashMap<>();
+
+ private final Set<DexType> initializedClasses = Sets.newIdentityHashSet();
+
+ private final Map<FieldAndObject, FieldValue> nonFinalInstanceFieldValues = new HashMap<>();
+
+ private final Map<DexField, FieldValue> nonFinalStaticFieldValues = new IdentityHashMap<>();
+
+ public State() {}
+
+ public State(State state) {
+ finalInstanceFieldValues.putAll(state.finalInstanceFieldValues);
+ finalStaticFieldValues.putAll(state.finalStaticFieldValues);
+ initializedClasses.addAll(state.initializedClasses);
+ nonFinalInstanceFieldValues.putAll(state.nonFinalInstanceFieldValues);
+ nonFinalStaticFieldValues.putAll(state.nonFinalStaticFieldValues);
+ }
+
+ public void clearNonFinalInstanceFields() {
+ nonFinalInstanceFieldValues.clear();
+ }
+
+ public void clearNonFinalStaticFields() {
+ nonFinalStaticFieldValues.clear();
+ }
+
+ public FieldValue getInstanceFieldValue(FieldAndObject field) {
+ FieldValue value = nonFinalInstanceFieldValues.get(field);
+ return value != null ? value : finalInstanceFieldValues.get(field);
+ }
+
+ public FieldValue getStaticFieldValue(DexField field) {
+ FieldValue value = nonFinalStaticFieldValues.get(field);
+ return value != null ? value : finalStaticFieldValues.get(field);
+ }
+
+ public void intersect(State state) {
+ intersectFieldValues(finalInstanceFieldValues, state.finalInstanceFieldValues);
+ intersectFieldValues(finalStaticFieldValues, state.finalStaticFieldValues);
+ intersectInitializedClasses(initializedClasses, state.initializedClasses);
+ intersectFieldValues(nonFinalInstanceFieldValues, state.nonFinalInstanceFieldValues);
+ intersectFieldValues(nonFinalStaticFieldValues, state.nonFinalStaticFieldValues);
+ }
+
+ private static <K> void intersectFieldValues(
+ Map<K, FieldValue> fieldValues, Map<K, FieldValue> other) {
+ fieldValues.entrySet().removeIf(entry -> other.get(entry.getKey()) != entry.getValue());
+ }
+
+ private static void intersectInitializedClasses(
+ Set<DexType> initializedClasses, Set<DexType> other) {
+ initializedClasses.removeIf(not(other::contains));
+ }
+
+ public boolean isClassInitialized(DexType clazz) {
+ return initializedClasses.contains(clazz);
+ }
+
+ // If a field get instruction throws an exception it did not have an effect on the value of the
+ // field. Therefore, when propagating across exceptional edges for a field get instruction we
+ // have to exclude that field from the set of known field values.
+ public void killActiveFieldsForExceptionalExit(FieldInstruction instruction) {
+ DexField field = instruction.getField();
+ if (instruction.isInstanceGet()) {
+ Value object = instruction.asInstanceGet().object().getAliasedValue();
+ FieldAndObject fieldAndObject = new FieldAndObject(field, object);
+ removeNonFinalInstanceField(fieldAndObject);
+ } else if (instruction.isStaticGet()) {
+ removeNonFinalStaticField(field);
+ }
+ }
+
+ private void killActiveInitializedClassesForExceptionalExit(InitClass instruction) {
+ initializedClasses.remove(instruction.getClassValue());
+ }
+
+ public void markClassAsInitialized(DexType clazz) {
+ initializedClasses.add(clazz);
+ }
+
+ public void removeInstanceField(FieldAndObject field) {
+ removeFinalInstanceField(field);
+ removeNonFinalInstanceField(field);
+ }
+
+ public void removeFinalInstanceField(FieldAndObject field) {
+ finalInstanceFieldValues.remove(field);
+ }
+
+ public void removeNonFinalInstanceField(FieldAndObject field) {
+ nonFinalInstanceFieldValues.remove(field);
+ }
+
+ public void removeNonFinalInstanceFields(DexField field) {
+ nonFinalInstanceFieldValues.keySet().removeIf(key -> key.field == field);
+ }
+
+ public void removeStaticField(DexField field) {
+ removeFinalStaticField(field);
+ removeNonFinalStaticField(field);
+ }
+
+ public void removeFinalStaticField(DexField field) {
+ finalStaticFieldValues.remove(field);
+ }
+
+ public void removeNonFinalStaticField(DexField field) {
+ nonFinalStaticFieldValues.remove(field);
+ }
+
+ public void putFinalInstanceField(FieldAndObject field, FieldValue value) {
+ finalInstanceFieldValues.put(field, value);
+ }
+
+ public void putFinalStaticField(DexField field, FieldValue value) {
+ finalStaticFieldValues.put(field, value);
+ }
+
+ public void putNonFinalInstanceField(FieldAndObject field, FieldValue value) {
+ assert !finalInstanceFieldValues.containsKey(field);
+ nonFinalInstanceFieldValues.put(field, value);
+ }
+
+ public void putNonFinalStaticField(DexField field, FieldValue value) {
+ assert !nonFinalStaticFieldValues.containsKey(field);
+ nonFinalStaticFieldValues.put(field, value);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 1b56e8c..1e19dea 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -36,7 +36,7 @@
public static void rewriteGetClassOrForNameToConstClass(
AppView<AppInfoWithLiveness> appView, IRCode code) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
ClassInitializationAnalysis classInitializationAnalysis =
new ClassInitializationAnalysis(appView, code);
for (BasicBlock block : code.blocks) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 8e0858a..6b854e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -351,8 +351,7 @@
Instruction instruction = instructionIterator.next();
if (instruction.throwsOnNullInput()) {
Value couldBeNullValue = instruction.getNonNullInput();
- if (isThrowNullCandidate(
- couldBeNullValue, instruction, appView, code.method.method.holder)) {
+ if (isThrowNullCandidate(couldBeNullValue, instruction, appView, code.method.holder())) {
if (instruction.isInstanceGet() || instruction.isInstancePut()) {
++numberOfInstanceGetOrInstancePutWithNullReceiver;
} else if (instruction.isInvokeMethodWithReceiver()) {
@@ -451,7 +450,7 @@
IRCode code,
AssumeDynamicTypeRemover assumeDynamicTypeRemover,
Set<Value> affectedValues) {
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
DexField field = instruction.getField();
DexType fieldType = field.type;
if (fieldType.isAlwaysNull(appView)) {
@@ -507,7 +506,7 @@
AssumeDynamicTypeRemover assumeDynamicTypeRemover,
Set<BasicBlock> blocksToBeRemoved,
Set<Value> affectedValues) {
- DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.method.holder);
+ DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.holder());
if (target == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index db393ed..7b60b90 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -157,8 +157,7 @@
// Constructors must be named `<init>`.
return null;
}
- newSignature =
- appView.dexItemFactory().createMethod(method.method.holder, newProto, newName);
+ newSignature = appView.dexItemFactory().createMethod(method.holder(), newProto, newName);
count++;
} while (!isMethodSignatureAvailable(newSignature));
return newSignature;
@@ -198,8 +197,7 @@
// Constructors must be named `<init>`.
return null;
}
- newSignature =
- appView.dexItemFactory().createMethod(method.method.holder, newProto, newName);
+ newSignature = appView.dexItemFactory().createMethod(method.holder(), newProto, newName);
count++;
} while (methodPool.hasSeen(equivalence.wrap(newSignature)));
return newSignature;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 81f68de..7f0ad04 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -160,7 +160,7 @@
if (eligibleClass.classInitializationMayHaveSideEffects(
appView,
// Types that are a super type of the current context are guaranteed to be initialized.
- type -> appView.isSubtype(method.method.holder, type).isTrue(),
+ type -> appView.isSubtype(method.holder(), type).isTrue(),
Sets.newIdentityHashSet())) {
return EligibilityStatus.HAS_CLINIT;
}
@@ -170,7 +170,7 @@
assert root.isStaticGet();
StaticGet staticGet = root.asStaticGet();
- if (staticGet.instructionMayHaveSideEffects(appView, method.method.holder)) {
+ if (staticGet.instructionMayHaveSideEffects(appView, method.holder())) {
return EligibilityStatus.RETRIEVAL_MAY_HAVE_SIDE_EFFECTS;
}
DexEncodedField field = appView.appInfo().resolveField(staticGet.getField());
@@ -265,8 +265,7 @@
if (user.isInvokeMethod()) {
InvokeMethod invokeMethod = user.asInvokeMethod();
- DexEncodedMethod singleTarget =
- invokeMethod.lookupSingleTarget(appView, method.method.holder);
+ DexEncodedMethod singleTarget = invokeMethod.lookupSingleTarget(appView, method.holder());
if (singleTarget == null) {
return user; // Not eligible.
}
@@ -507,8 +506,7 @@
continue;
}
- DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView, code.method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) {
throw new IllegalClassInlinerStateException();
}
@@ -569,7 +567,7 @@
continue;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder());
if (singleTarget != null) {
Predicate<InvokeMethod> noSideEffectsPredicate =
dexItemFactory.libraryMethodsWithoutSideEffects.getOrDefault(
@@ -1156,7 +1154,7 @@
}
// Check if the method is inline-able by standard inliner.
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder());
if (singleTarget == null) {
return false;
}
@@ -1190,7 +1188,7 @@
}
private boolean exemptFromInstructionLimit(DexEncodedMethod inlinee) {
- DexType inlineeHolder = inlinee.method.holder;
+ DexType inlineeHolder = inlinee.holder();
DexClass inlineeClass = appView.definitionFor(inlineeHolder);
assert inlineeClass != null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 84b952b..ea9141f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.enums;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
@@ -28,6 +30,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -86,13 +89,14 @@
debugLogEnabled = false;
debugLogs = null;
}
+ assert !appView.options().debug;
enumsUnboxingCandidates = new EnumUnboxingCandidateAnalysis(appView, this).findCandidates();
}
public void analyzeEnums(IRCode code) {
// Enum <clinit> and <init> are analyzed in between the two processing phases using optimization
// feedback. Methods valueOf and values are generated by javac and are analyzed differently.
- DexClass dexClass = appView.definitionFor(code.method.method.holder);
+ DexClass dexClass = appView.definitionFor(code.method.holder());
if (dexClass.isEnum()
&& (code.method.isInitializer()
|| appView.dexItemFactory().enumMethods.isValueOfMethod(code.method.method, dexClass)
@@ -140,11 +144,6 @@
if (enumClass != null) {
Reason reason = validateEnumUsages(code, outValue, enumClass);
if (reason == Reason.ELIGIBLE) {
- if (instruction.isCheckCast()) {
- // We are doing a type check, which typically means the in-value is of an upper
- // type and cannot be dealt with.
- markEnumAsUnboxable(Reason.DOWN_CAST, enumClass);
- }
eligibleEnums.add(enumClass.type);
}
}
@@ -152,15 +151,10 @@
addNullDependencies(outValue.uniqueUsers(), eligibleEnums);
}
}
- // If we have a ConstClass referencing directly an enum, it cannot be unboxed, except if
- // the constClass is in an enum valueOf method (in this case the valueOf method will be
- // removed or the enum will be marked as non unboxable).
if (instruction.isConstClass()) {
- ConstClass constClass = instruction.asConstClass();
- if (enumsUnboxingCandidates.containsKey(constClass.getValue())) {
- markEnumAsUnboxable(
- Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue()));
- }
+ analyzeConstClass(instruction.asConstClass());
+ } else if (instruction.isCheckCast()) {
+ analyzeCheckCast(instruction.asCheckCast());
} else if (instruction.isInvokeStatic()) {
// TODO(b/150370354): Since we temporary allow enum unboxing on enums with values and
// valueOf static methods only if such methods are unused, such methods cannot be
@@ -204,6 +198,48 @@
}
}
+ private void analyzeCheckCast(CheckCast checkCast) {
+ // We are doing a type check, which typically means the in-value is of an upper
+ // type and cannot be dealt with.
+ // If the cast is on a dynamically typed object, the checkCast can be simply removed.
+ // This allows enum array clone and valueOf to work correctly.
+ TypeElement objectType = checkCast.object().getDynamicUpperBoundType(appView);
+ if (objectType.equalUpToNullability(
+ TypeElement.fromDexType(checkCast.getType(), definitelyNotNull(), appView))) {
+ return;
+ }
+ DexProgramClass enumClass =
+ getEnumUnboxingCandidateOrNull(checkCast.getType().toBaseType(factory));
+ if (enumClass != null) {
+ markEnumAsUnboxable(Reason.DOWN_CAST, enumClass);
+ }
+ }
+
+ private void analyzeConstClass(ConstClass constClass) {
+ // We are using the ConstClass of an enum, which typically means the enum cannot be unboxed.
+ // We however allow unboxing if the ConstClass is only used as an argument to Enum#valueOf, to
+ // allow unboxing of: MyEnum a = Enum.valueOf(MyEnum.class, "A");.
+ if (!enumsUnboxingCandidates.containsKey(constClass.getValue())) {
+ return;
+ }
+ if (constClass.outValue() == null) {
+ return;
+ }
+ if (constClass.outValue().hasPhiUsers()) {
+ markEnumAsUnboxable(
+ Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue()));
+ return;
+ }
+ for (Instruction user : constClass.outValue().uniqueUsers()) {
+ if (!(user.isInvokeStatic()
+ && user.asInvokeStatic().getInvokedMethod() == factory.enumMethods.valueOf)) {
+ markEnumAsUnboxable(
+ Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue()));
+ return;
+ }
+ }
+ }
+
private void addNullDependencies(Set<Instruction> uses, Set<DexType> eligibleEnums) {
for (Instruction use : uses) {
if (use.isInvokeMethod()) {
@@ -349,7 +385,7 @@
return Reason.INVALID_INVOKE_ON_ARRAY;
}
DexEncodedMethod encodedSingleTarget =
- invokeMethod.lookupSingleTarget(appView, code.method.method.holder);
+ invokeMethod.lookupSingleTarget(appView, code.method.holder());
if (encodedSingleTarget == null) {
return Reason.INVALID_INVOKE;
}
@@ -359,14 +395,6 @@
return Reason.INVALID_INVOKE;
}
if (dexClass.isProgramClass()) {
- // All invokes in the program are generally valid, but specific care is required
- // for values() and valueOf().
- if (dexClass.isEnum() && factory.enumMethods.isValuesMethod(singleTarget, dexClass)) {
- return Reason.VALUES_INVOKE;
- }
- if (dexClass.isEnum() && factory.enumMethods.isValueOfMethod(singleTarget, dexClass)) {
- return Reason.VALUE_OF_INVOKE;
- }
int offset = BooleanUtils.intValue(!encodedSingleTarget.isStatic());
for (int i = 0; i < singleTarget.proto.parameters.size(); i++) {
if (invokeMethod.inValues().get(offset + i) == enumValue) {
@@ -384,8 +412,8 @@
if (dexClass.type != factory.enumType) {
return Reason.UNSUPPORTED_LIBRARY_CALL;
}
- // TODO(b/147860220): Methods toString(), name(), compareTo(), EnumSet and EnumMap may be
- // interesting to model. A the moment rewrite only Enum#ordinal().
+ // TODO(b/147860220): Methods toString(), name(), compareTo(), EnumSet and EnumMap may
+ // be interesting to model. A the moment rewrite only Enum#ordinal() and Enum#valueOf.
if (debugLogEnabled) {
if (singleTarget == factory.enumMethods.compareTo) {
return Reason.COMPARE_TO_INVOKE;
@@ -397,10 +425,10 @@
return Reason.TO_STRING_INVOKE;
}
}
- if (singleTarget != factory.enumMethods.ordinal) {
- return Reason.UNSUPPORTED_LIBRARY_CALL;
+ if (singleTarget == factory.enumMethods.ordinal) {
+ return Reason.ELIGIBLE;
}
- return Reason.ELIGIBLE;
+ return Reason.UNSUPPORTED_LIBRARY_CALL;
}
// A field put is valid only if the field is not on an enum, and the field type and the valuePut
@@ -411,7 +439,7 @@
if (field == null) {
return Reason.INVALID_FIELD_PUT;
}
- DexProgramClass dexClass = appView.definitionForProgramType(field.field.holder);
+ DexProgramClass dexClass = appView.definitionForProgramType(field.holder());
if (dexClass == null) {
return Reason.INVALID_FIELD_PUT;
}
@@ -603,7 +631,7 @@
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (enumsToUnbox.contains(clazz.type)) {
assert clazz.instanceFields().size() == 0;
- clearEnumtoUnboxMethods(clazz);
+ clearEnumToUnboxMethods(clazz);
} else {
clazz.getMethodCollection().replaceMethods(this::fixupMethod);
fixupFields(clazz.staticFields(), clazz::setStaticField);
@@ -616,7 +644,7 @@
return lensBuilder.build(factory, appView.graphLense());
}
- private void clearEnumtoUnboxMethods(DexProgramClass clazz) {
+ private void clearEnumToUnboxMethods(DexProgramClass clazz) {
// The compiler may have references to the enum methods, but such methods will be removed
// and they cannot be reprocessed since their rewriting through the lensCodeRewriter/
// enumUnboxerRewriter will generate invalid code.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
index 0bf0aed..a09ea37 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.cf.code.CfStore;
import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.MemberType;
@@ -36,6 +37,10 @@
public final class EnumUnboxingCfMethods {
+ public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
+ factory.createSynthesizedType("Ljava/lang/NullPointerException;");
+ }
+
public static CfCode EnumUnboxingMethods_compareTo(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
@@ -52,13 +57,12 @@
new CfLoad(ValueType.INT, 1),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"),
+ options.itemFactory.createType("Ljava/lang/NullPointerException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -89,13 +93,12 @@
new CfLoad(ValueType.INT, 0),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"),
+ options.itemFactory.createType("Ljava/lang/NullPointerException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
@@ -129,13 +132,12 @@
new CfLoad(ValueType.INT, 0),
new CfIf(If.Type.NE, ValueType.INT, label2),
label1,
- new CfNew(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;")),
+ new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
options.itemFactory.createMethod(
- options.itemFactory.createSynthesizedType("Ljava/lang/NullPointerException;"),
+ options.itemFactory.createType("Ljava/lang/NullPointerException;"),
options.itemFactory.createProto(options.itemFactory.voidType),
options.itemFactory.createString("<init>")),
false),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 0d065ff..73eff15 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -24,27 +24,28 @@
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
-import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ArrayAccess;
-import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -56,6 +57,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory factory;
private final EnumValueInfoMapCollection enumsToUnbox;
+ private final Map<DexMethod, DexType> extraMethods = new ConcurrentHashMap<>();
private final DexType utilityClassType;
private final DexMethod ordinalUtilityMethod;
@@ -71,7 +73,7 @@
}
this.enumsToUnbox = builder.build();
- this.utilityClassType = factory.createType("L" + ENUM_UNBOXING_UTILITY_CLASS_NAME + ";");
+ this.utilityClassType = factory.enumUnboxingUtilityType;
this.ordinalUtilityMethod =
factory.createMethod(
utilityClassType,
@@ -89,6 +91,7 @@
if (enumsToUnbox.isEmpty()) {
return;
}
+ assert code.isConsistentSSABeforeTypesAreCorrect();
Set<Phi> affectedPhis = Sets.newIdentityHashSet();
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
@@ -99,14 +102,38 @@
InvokeMethodWithReceiver invokeMethod = instruction.asInvokeMethodWithReceiver();
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
if (invokedMethod == factory.enumMethods.ordinal
- && invokeMethod.getReceiver().getType().isInt()) {
+ && isEnumToUnboxOrInt(invokeMethod.getReceiver().getType())) {
instruction =
new InvokeStatic(
ordinalUtilityMethod, invokeMethod.outValue(), invokeMethod.inValues());
iterator.replaceCurrentInstruction(instruction);
requiresOrdinalUtilityMethod = true;
+ continue;
}
// TODO(b/147860220): rewrite also other enum methods.
+ } else if (instruction.isInvokeStatic()) {
+ InvokeStatic invokeStatic = instruction.asInvokeStatic();
+ DexMethod invokedMethod = invokeStatic.getInvokedMethod();
+ if (invokedMethod == factory.enumMethods.valueOf
+ && invokeStatic.inValues().get(0).isConstClass()) {
+ DexType enumType =
+ invokeStatic.inValues().get(0).getConstInstruction().asConstClass().getValue();
+ if (enumsToUnbox.containsEnum(enumType)) {
+ DexMethod valueOfMethod = computeValueOfUtilityMethod(enumType);
+ Value outValue = invokeStatic.outValue();
+ Value rewrittenOutValue = null;
+ if (outValue != null) {
+ rewrittenOutValue = code.createValue(TypeElement.getInt());
+ affectedPhis.addAll(outValue.uniquePhiUsers());
+ }
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(
+ valueOfMethod,
+ rewrittenOutValue,
+ Collections.singletonList(invokeStatic.inValues().get(1))));
+ continue;
+ }
+ }
}
// Rewrites direct access to enum values into the corresponding int, $VALUES is not
// supported.
@@ -124,10 +151,8 @@
EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField());
assert enumValueInfo != null
: "Invalid read to " + staticGet.getField().name + ", error during enum analysis";
- instruction = new ConstNumber(staticGet.outValue(), enumValueInfo.convertToInt());
- staticGet.outValue().setType(PrimitiveTypeElement.fromNumericType(NumericType.INT));
- iterator.replaceCurrentInstruction(instruction);
affectedPhis.addAll(staticGet.outValue().uniquePhiUsers());
+ iterator.replaceCurrentInstruction(code.createIntConstant(enumValueInfo.convertToInt()));
}
}
// Rewrite array accesses from MyEnum[] (OBJECT) to int[] (INT).
@@ -137,8 +162,8 @@
instruction = arrayAccess.withMemberType(MemberType.INT);
iterator.replaceCurrentInstruction(instruction);
}
+ assert validateArrayAccess(instruction.asArrayAccess());
}
- assert validateEnumToUnboxRemoved(instruction);
}
if (!affectedPhis.isEmpty()) {
new DestructivePhiTypeUpdater(appView).recomputeAndPropagateTypes(code, affectedPhis);
@@ -146,33 +171,43 @@
assert code.isConsistentSSABeforeTypesAreCorrect();
}
- private boolean shouldRewriteArrayAccess(ArrayAccess arrayAccess) {
+ private boolean validateArrayAccess(ArrayAccess arrayAccess) {
ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
- return arrayAccess.getMemberType() == MemberType.OBJECT
- && arrayType.getNesting() == 1
- && arrayType.getBaseType().isInt();
+ assert arrayAccess.getMemberType() != MemberType.OBJECT
+ || arrayType.getNesting() > 1
+ || arrayType.getBaseType().isReferenceType();
+ return true;
}
- private boolean validateEnumToUnboxRemoved(Instruction instruction) {
- if (instruction.isArrayAccess()) {
- ArrayAccess arrayAccess = instruction.asArrayAccess();
- ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
- assert arrayAccess.getMemberType() != MemberType.OBJECT
- || arrayType.getNesting() > 1
- || arrayType.getBaseType().isReferenceType();
- }
- if (instruction.outValue() == null) {
+ private boolean isEnumToUnboxOrInt(TypeElement type) {
+ if (type.isInt()) {
return true;
}
- TypeElement typeLattice = instruction.outValue().getType();
- assert !typeLattice.isClassType()
- || !enumsToUnbox.containsEnum(typeLattice.asClassType().getClassType());
- if (typeLattice.isArrayType()) {
- TypeElement arrayBaseTypeLattice = typeLattice.asArrayType().getBaseType();
- assert !arrayBaseTypeLattice.isClassType()
- || !enumsToUnbox.containsEnum(arrayBaseTypeLattice.asClassType().getClassType());
+ if (!type.isClassType()) {
+ return false;
}
- return true;
+ return enumsToUnbox.containsEnum(type.asClassType().getClassType());
+ }
+
+ private DexMethod computeValueOfUtilityMethod(DexType type) {
+ assert enumsToUnbox.containsEnum(type);
+ DexMethod valueOf =
+ factory.createMethod(
+ utilityClassType,
+ factory.createProto(factory.intType, factory.stringType),
+ "valueOf" + type.toSourceString().replace('.', '$'));
+ extraMethods.putIfAbsent(valueOf, type);
+ return valueOf;
+ }
+
+ private boolean shouldRewriteArrayAccess(ArrayAccess arrayAccess) {
+ ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
+ if (arrayType.getNesting() != 1) {
+ return false;
+ }
+ TypeElement baseType = arrayType.getBaseType();
+ return baseType.isClassType()
+ && enumsToUnbox.containsEnum(baseType.asClassType().getClassType());
}
// TODO(b/150172351): Synthesize the utility class upfront in the enqueuer.
@@ -182,6 +217,11 @@
// Synthesize a class which holds various utility methods that may be called from the IR
// rewriting. If any of these methods are not used, they will be removed by the Enqueuer.
List<DexEncodedMethod> requiredMethods = new ArrayList<>();
+ extraMethods.forEach(
+ (method, enumType) -> {
+ requiredMethods.add(synthesizeValueOfUtilityMethod(method, enumType));
+ });
+ requiredMethods.sort((m1, m2) -> m1.method.name.slowCompareTo(m2.method.name));
if (requiresOrdinalUtilityMethod) {
requiredMethods.add(synthesizeOrdinalMethod());
}
@@ -215,6 +255,21 @@
converter.optimizeSynthesizedClass(utilityClass, executorService);
}
+ private DexEncodedMethod synthesizeValueOfUtilityMethod(DexMethod method, DexType enumType) {
+ CfCode cfCode =
+ new EnumUnboxingCfCodeProvider.EnumUnboxingValueOfCfCodeProvider(
+ appView, utilityClassType, enumType, enumsToUnbox.getEnumValueInfoMap(enumType))
+ .generateCfCode();
+ return new DexEncodedMethod(
+ method,
+ synthesizedMethodAccessFlags(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ cfCode,
+ REQUIRED_CLASS_FILE_VERSION,
+ true);
+ }
+
// TODO(b/150178516): Add a test for this case.
private boolean utilityClassInMainDexList() {
for (DexType toUnbox : enumsToUnbox.enumSet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index ba24e19..791c889 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -85,7 +85,7 @@
TypeElement[] staticTypes = new TypeElement[size];
if (!encodedMethod.isStatic()) {
staticTypes[0] =
- TypeElement.fromDexType(encodedMethod.method.holder, definitelyNotNull(), appView);
+ TypeElement.fromDexType(encodedMethod.holder(), definitelyNotNull(), appView);
}
for (int i = 0; i < encodedMethod.method.getArity(); i++) {
staticTypes[i + argOffset] =
@@ -168,7 +168,7 @@
Value aliasedValue = arg.getAliasedValue();
if (!aliasedValue.isPhi()) {
AbstractValue abstractValue =
- aliasedValue.definition.getAbstractValue(appView, method.method.holder);
+ aliasedValue.definition.getAbstractValue(appView, method.holder());
if (abstractValue.isNonTrivial()) {
newCallSiteInfo.constants.put(i, abstractValue);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 19c897f..4889b2e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -177,7 +177,7 @@
return;
}
- DexClass clazz = appView.definitionFor(method.method.holder);
+ DexClass clazz = appView.definitionFor(method.holder());
if (clazz == null) {
return;
}
@@ -241,8 +241,7 @@
case INVOKE_STATIC:
{
InvokeStatic invoke = insn.asInvokeStatic();
- DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView, method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder());
if (singleTarget == null) {
return; // Not allowed.
}
@@ -263,7 +262,7 @@
DexMethod invokedMethod = invoke.getInvokedMethod();
DexType returnType = invokedMethod.proto.returnType;
if (returnType.isClassType()
- && appView.appInfo().isRelatedBySubtyping(returnType, method.method.holder)) {
+ && appView.appInfo().isRelatedBySubtyping(returnType, method.holder())) {
return; // Not allowed, could introduce an alias of the receiver.
}
callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
@@ -373,7 +372,7 @@
if (definition.isArgument()) {
feedback.methodReturnsArgument(method, definition.asArgument().getIndex());
}
- DexType context = method.method.holder;
+ DexType context = method.holder();
AbstractValue abstractReturnValue = definition.getAbstractValue(appView, context);
if (abstractReturnValue.isNonTrivial()) {
feedback.methodReturnsAbstractValue(method, appView, abstractReturnValue);
@@ -417,7 +416,7 @@
return;
}
- DexClass clazz = appView.appInfo().definitionFor(method.method.holder);
+ DexClass clazz = appView.appInfo().definitionFor(method.holder());
if (clazz == null) {
assert false;
return;
@@ -687,7 +686,7 @@
if (method.isStatic()) {
// Identifies if the method preserves class initialization after inlining.
feedback.markTriggerClassInitBeforeAnySideEffect(
- method, triggersClassInitializationBeforeSideEffect(method.method.holder, code, appView));
+ method, triggersClassInitializationBeforeSideEffect(method.holder(), code, appView));
} else {
// Identifies if the method preserves null check of the receiver after inlining.
final Value receiver = code.getThis();
@@ -707,7 +706,7 @@
return alwaysTriggerExpectedEffectBeforeAnythingElse(
code,
(instruction, it) -> {
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
if (instruction.definitelyTriggersClassInitialization(
clazz, context, appView, DIRECTLY, AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
// In order to preserve class initialization semantic, the exception must not be caught
@@ -849,7 +848,7 @@
// We found a NPE check on the value.
return InstructionEffect.DESIRED_EFFECT;
}
- } else if (instr.instructionMayHaveSideEffects(appView, code.method.method.holder)) {
+ } else if (instr.instructionMayHaveSideEffects(appView, code.method.holder())) {
// If the current instruction is const-string, this could load the parameter name.
// Just make sure it is indeed not throwing.
if (instr.isConstString() && !instr.instructionInstanceCanThrow()) {
@@ -1014,7 +1013,7 @@
if (appView.appInfo().mayHaveSideEffects.containsKey(method.method)) {
return;
}
- DexType context = method.method.holder;
+ DexType context = method.holder();
if (method.isClassInitializer()) {
// For class initializers, we also wish to compute if the class initializer has observable
// side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
index 3a98637..608c41f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
@@ -38,7 +38,7 @@
return cached;
}
Position position = Position.getPositionForInlining(appView, invoke, context);
- Origin origin = appView.appInfo().originFor(method.method.holder);
+ Origin origin = appView.appInfo().originFor(method.holder());
return method.buildInliningIR(
context, appView, valueNumberGenerator, position, origin, methodProcessor);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 1ff91c0..091a5e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -32,8 +32,7 @@
return;
}
- Collection<DexEncodedMethod> possibleTargets =
- invoke.lookupTargets(appView, context.method.holder);
+ Collection<DexEncodedMethod> possibleTargets = invoke.lookupTargets(appView, context.holder());
if (possibleTargets == null) {
// In principle, this invoke might target any method in the program, but we do not want to
// report a message for each of the methods in `AppInfoWithLiveness#whyAreYouNotInlining`,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index 64026ca..b141817 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -220,7 +220,7 @@
"final field `"
+ instancePut.getField()
+ "` must be initialized in a constructor of `"
- + callee.method.holder.toSourceString()
+ + callee.holder().toSourceString()
+ "`.");
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
index 1648958..eb8b742 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
@@ -218,7 +218,7 @@
private boolean shouldRewrite(DexType type) {
// Rewrite references to lambda classes if we are outside the class.
- return type != (context != null ? context : method).method.holder;
+ return type != (context != null ? context : method).holder();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index ce2394c..0bdaa57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -133,7 +133,7 @@
Inliner inliner,
DexEncodedMethod context,
InliningIRProvider provider) {
- DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ DexProgramClass clazz = appView.definitionFor(method.holder()).asProgramClass();
assert clazz != null;
LambdaGroup lambdaGroup = lambdaGroups.get(clazz);
@@ -162,7 +162,7 @@
assert resolution.isSingleResolution();
DexEncodedMethod singleTarget = resolution.getSingleTarget();
assert singleTarget != null;
- invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.method.holder));
+ invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.holder()));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index 3e602b7..4d79186 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -54,7 +54,7 @@
return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
&& lambda == field.type
&& context.factory.isClassConstructor(context.method.method)
- && context.method.method.holder == lambda;
+ && context.method.holder() == lambda;
}
@Override
@@ -100,10 +100,10 @@
// Allow calls to a constructor from other classes if the lambda is singleton,
// otherwise allow such a call only from the same class static initializer.
boolean isSingletonLambda = group.isStateless() && group.isSingletonLambda(lambda);
- return (isSingletonLambda == (context.method.method.holder == lambda)) &&
- invoke.isInvokeDirect() &&
- context.factory.isConstructor(method) &&
- CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
+ return (isSingletonLambda == (context.method.holder() == lambda))
+ && invoke.isInvokeDirect()
+ && context.factory.isConstructor(method)
+ && CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
}
private boolean isValidVirtualCall(InvokeMethod invoke) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
index 31444c6..16d514c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -70,7 +70,7 @@
InvokeMethod invoke,
Set<Value> affectedValues) {
Value argument = invoke.arguments().get(0);
- AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.method.holder);
+ AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.holder());
if (abstractValue.isSingleNumberValue()) {
instructionIterator.replaceCurrentInstructionWithStaticGet(
appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
new file mode 100644
index 0000000..fd9d62b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Value;
+import java.util.Set;
+
+public class EnumMethodOptimizer implements LibraryMethodModelCollection {
+ private final AppView<?> appView;
+
+ EnumMethodOptimizer(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public DexType getType() {
+ return appView.dexItemFactory().enumType;
+ }
+
+ @Override
+ public void optimize(
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexEncodedMethod singleTarget,
+ Set<Value> affectedValues) {
+ if (singleTarget.method == appView.dexItemFactory().enumMethods.valueOf
+ && invoke.inValues().get(0).isConstClass()) {
+ insertAssumeDynamicType(code, instructionIterator, invoke);
+ }
+ }
+
+ private void insertAssumeDynamicType(
+ IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
+ // TODO(b/152516470): Support unboxing enums with Enum#valueOf in try-catch.
+ if (invoke.getBlock().hasCatchHandlers()) {
+ return;
+ }
+ DexType enumType = invoke.inValues().get(0).getConstInstruction().asConstClass().getValue();
+ DexProgramClass enumClass = appView.definitionForProgramType(enumType);
+ if (enumClass == null || enumClass.superType != appView.dexItemFactory().enumType) {
+ return;
+ }
+ TypeElement dynamicUpperBoundType =
+ TypeElement.fromDexType(enumType, definitelyNotNull(), appView);
+ Value outValue = invoke.outValue();
+ if (outValue == null) {
+ return;
+ }
+ // Replace usages of out-value by the out-value of the AssumeDynamicType instruction.
+ Value specializedOutValue = code.createValue(outValue.getType(), outValue.getLocalInfo());
+ outValue.replaceUsers(specializedOutValue);
+
+ // Insert AssumeDynamicType instruction.
+ Assume<DynamicTypeAssumption> assumeInstruction =
+ Assume.createAssumeDynamicTypeInstruction(
+ dynamicUpperBoundType, null, specializedOutValue, outValue, invoke, appView);
+ assumeInstruction.setPosition(appView.options().debug ? invoke.getPosition() : Position.none());
+ instructionIterator.add(assumeInstruction);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
index f3d7cc6..fc687a0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodOptimizer.java
@@ -37,6 +37,9 @@
register(new ObjectMethodOptimizer(appView));
register(new ObjectsMethodOptimizer(appView));
register(new StringMethodOptimizer(appView));
+ if (appView.appInfo().hasSubtyping() && appView.options().enableDynamicTypeOptimization) {
+ register(new EnumMethodOptimizer(appView));
+ }
if (LogMethodOptimizer.isEnabled(appView)) {
register(new LogMethodOptimizer(appView));
@@ -83,8 +86,7 @@
Instruction instruction = instructionIterator.next();
if (instruction.isInvokeMethod()) {
InvokeMethod invoke = instruction.asInvokeMethod();
- DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView, code.method.method.holder);
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
if (singleTarget != null) {
optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues);
}
@@ -103,7 +105,7 @@
Set<Value> affectedValues) {
LibraryMethodModelCollection optimizer =
libraryMethodModelCollections.getOrDefault(
- singleTarget.method.holder, NopLibraryMethodModelCollection.getInstance());
+ singleTarget.holder(), NopLibraryMethodModelCollection.getInstance());
optimizer.optimize(code, instructionIterator, invoke, singleTarget, affectedValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index 501a761..09c8f4e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -47,7 +47,7 @@
private void optimizeEquals(
IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
if (appView.appInfo().hasLiveness()) {
- DexType context = code.method.method.holder;
+ DexType context = code.method.holder();
Value first = invoke.arguments().get(0).getAliasedValue();
Value second = invoke.arguments().get(1).getAliasedValue();
if (isPrunedClassNameComparison(first, second, context)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
index 127b144..3b93c50 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
@@ -113,7 +113,7 @@
Instruction current = it.next();
if (position != current.getPosition()
|| !current.isConstNumber()
- || current.outValue().getType() != TypeElement.getInt()
+ || current.getOutType() != TypeElement.getInt()
|| current.asConstNumber().getIntValue() < -128
|| current.asConstNumber().getIntValue() > 127
|| !it.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 3c99a56..41df56a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -78,11 +79,11 @@
}
boolean isHostClassInitializer(DexEncodedMethod method) {
- return factory.isClassConstructor(method.method) && method.method.holder == hostType();
+ return factory.isClassConstructor(method.method) && method.holder() == hostType();
}
DexType hostType() {
- return singletonField.field.holder;
+ return singletonField.holder();
}
DexClass hostClass() {
@@ -212,7 +213,7 @@
public final void examineMethodCode(DexEncodedMethod method, IRCode code) {
Set<Instruction> alreadyProcessed = Sets.newIdentityHashSet();
- CandidateInfo receiverClassCandidateInfo = candidates.get(method.method.holder);
+ CandidateInfo receiverClassCandidateInfo = candidates.get(method.holder());
Value receiverValue = code.getThis(); // NOTE: is null for static methods.
if (receiverClassCandidateInfo != null) {
if (receiverValue != null) {
@@ -224,13 +225,13 @@
// If the candidate is still valid, ignore all instructions
// we treat as valid usages on receiver.
- if (candidates.get(method.method.holder) != null) {
+ if (candidates.get(method.holder()) != null) {
alreadyProcessed.addAll(receiverValue.uniqueUsers());
}
} else {
// We are inside a static method of candidate class.
// Check if this is a valid getter of the singleton field.
- if (method.method.proto.returnType == method.method.holder) {
+ if (method.method.proto.returnType == method.holder()) {
List<Instruction> examined = isValidGetter(receiverClassCandidateInfo, code);
if (examined != null) {
DexEncodedMethod getter = receiverClassCandidateInfo.getter.get();
@@ -267,12 +268,13 @@
NewInstance newInstance = instruction.asNewInstance();
CandidateInfo candidateInfo = processInstantiation(method, iterator, newInstance);
if (candidateInfo != null) {
+ alreadyProcessed.addAll(newInstance.outValue().aliasedUsers());
// For host class initializers having eligible instantiation we also want to
// ensure that the rest of the initializer consist of code w/o side effects.
// This must guarantee that removing field access will not result in missing side
// effects, otherwise we can still staticize, but cannot remove singleton reads.
while (iterator.hasNext()) {
- if (!isAllowedInHostClassInitializer(method.method.holder, iterator.next(), code)) {
+ if (!isAllowedInHostClassInitializer(method.holder(), iterator.next(), code)) {
candidateInfo.preserveRead.set(true);
iterator.previous();
break;
@@ -396,10 +398,10 @@
return candidateInfo.invalidate();
}
- if (candidateValue.numberOfUsers() != 2) {
- // We expect only two users for each instantiation: constructor call and
- // static field write. We only check count here, since the exact instructions
- // will be checked later.
+ if (candidateValue.numberOfUsers() < 2) {
+ // We expect two special users for each instantiation: constructor call and static field
+ // write. We allow the instance to have other users as well, as long as they are valid
+ // according to the user analysis.
return candidateInfo.invalidate();
}
@@ -411,7 +413,7 @@
// invoke-direct {v0, ...}, void <candidate-type>.<init>(...)
// sput-object v0, <instance-field>
// ...
- // ...
+ // ... // other usages that are valid according to the user analysis.
//
// In case we guarantee candidate constructor does not access <instance-field>
// directly or indirectly we can guarantee that all the potential reads get
@@ -422,26 +424,33 @@
// Intentionally empty.
}
iterator.previous();
-
if (!iterator.hasNext()) {
return candidateInfo.invalidate();
}
- if (!isValidInitCall(candidateInfo, iterator.next(), candidateValue, candidateType)) {
+ Set<Instruction> users = SetUtils.newIdentityHashSet(candidateValue.uniqueUsers());
+ Instruction constructorCall = iterator.next();
+ if (!isValidInitCall(candidateInfo, constructorCall, candidateValue, candidateType)) {
iterator.previous();
return candidateInfo.invalidate();
}
-
+ boolean removedConstructorCall = users.remove(constructorCall);
+ assert removedConstructorCall;
if (!iterator.hasNext()) {
return candidateInfo.invalidate();
}
- if (!isValidStaticPut(candidateInfo, iterator.next())) {
+ Instruction staticPut = iterator.next();
+ if (!isValidStaticPut(candidateInfo, staticPut)) {
iterator.previous();
return candidateInfo.invalidate();
}
+ boolean removedStaticPut = users.remove(staticPut);
+ assert removedStaticPut;
if (candidateInfo.fieldWrites.incrementAndGet() > 1) {
return candidateInfo.invalidate();
}
-
+ if (!isSelectedValueUsersValid(candidateInfo, candidateValue, false, users)) {
+ return candidateInfo.invalidate();
+ }
return candidateInfo;
}
@@ -463,7 +472,7 @@
if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0
|| methodInvoked == null
- || methodInvoked.method.holder != candidateType) {
+ || methodInvoked.holder() != candidateType) {
return false;
}
@@ -567,59 +576,79 @@
private CandidateInfo analyzeAllValueUsers(
CandidateInfo candidateInfo, Value value, boolean ignoreSuperClassInitInvoke) {
assert value != null && value == value.getAliasedValue();
-
if (value.numberOfPhiUsers() > 0) {
return candidateInfo.invalidate();
}
+ if (!isSelectedValueUsersValid(
+ candidateInfo, value, ignoreSuperClassInitInvoke, value.uniqueUsers())) {
+ return candidateInfo.invalidate();
+ }
+ return candidateInfo;
+ }
- Set<Instruction> currentUsers = value.uniqueUsers();
+ private boolean isSelectedValueUsersValid(
+ CandidateInfo candidateInfo,
+ Value value,
+ boolean ignoreSuperClassInitInvoke,
+ Set<Instruction> currentUsers) {
while (!currentUsers.isEmpty()) {
Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
for (Instruction user : currentUsers) {
- if (user.isAssume()) {
- if (user.outValue().numberOfPhiUsers() > 0) {
- return candidateInfo.invalidate();
- }
- indirectUsers.addAll(user.outValue().uniqueUsers());
- continue;
+ if (!isValidValueUser(
+ candidateInfo, value, ignoreSuperClassInitInvoke, indirectUsers, user)) {
+ return false;
}
- if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) {
- InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
- Predicate<Value> isAliasedValue = v -> v.getAliasedValue() == value;
- DexMethod methodReferenced = invoke.getInvokedMethod();
- if (factory.isConstructor(methodReferenced)) {
- assert user.isInvokeDirect();
- if (ignoreSuperClassInitInvoke
- && ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
- && methodReferenced == factory.objectMembers.constructor) {
- // If we are inside candidate constructor and analyzing usages
- // of the receiver, we want to ignore invocations of superclass
- // constructor which will be removed after staticizing.
- continue;
- }
- return candidateInfo.invalidate();
- }
- AppInfoWithLiveness appInfo = appView.appInfo();
- ResolutionResult resolutionResult =
- appInfo.resolveMethod(methodReferenced.holder, methodReferenced);
- DexEncodedMethod methodInvoked =
- user.isInvokeDirect()
- ? resolutionResult.lookupInvokeDirectTarget(candidateInfo.candidate, appInfo)
- : resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
- if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
- && methodInvoked != null
- && methodInvoked.method.holder == candidateInfo.candidate.type) {
- continue;
- }
- }
-
- // All other users are not allowed.
- return candidateInfo.invalidate();
}
currentUsers = indirectUsers;
}
+ return true;
+ }
- return candidateInfo;
+ private boolean isValidValueUser(
+ CandidateInfo candidateInfo,
+ Value value,
+ boolean ignoreSuperClassInitInvoke,
+ Set<Instruction> indirectUsers,
+ Instruction user) {
+ if (user.isAssume()) {
+ if (user.outValue().numberOfPhiUsers() > 0) {
+ return false;
+ }
+ indirectUsers.addAll(user.outValue().uniqueUsers());
+ return true;
+ }
+ if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) {
+ InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
+ Predicate<Value> isAliasedValue = v -> v.getAliasedValue() == value;
+ DexMethod methodReferenced = invoke.getInvokedMethod();
+ if (factory.isConstructor(methodReferenced)) {
+ assert user.isInvokeDirect();
+ if (ignoreSuperClassInitInvoke
+ && ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
+ && methodReferenced == factory.objectMembers.constructor) {
+ // If we are inside candidate constructor and analyzing usages
+ // of the receiver, we want to ignore invocations of superclass
+ // constructor which will be removed after staticizing.
+ return true;
+ }
+ return false;
+ }
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ ResolutionResult resolutionResult =
+ appInfo.resolveMethod(methodReferenced.holder, methodReferenced);
+ DexEncodedMethod methodInvoked =
+ user.isInvokeDirect()
+ ? resolutionResult.lookupInvokeDirectTarget(candidateInfo.candidate, appInfo)
+ : resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
+ if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
+ && methodInvoked != null
+ && methodInvoked.holder() == candidateInfo.candidate.type) {
+ return true;
+ }
+ }
+
+ // All other users are not allowed.
+ return false;
}
// Perform staticizing candidates:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index f79593b..4a1924b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.staticizer;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
@@ -18,8 +20,10 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
@@ -184,7 +188,7 @@
if (method.isStatic() || factory().isConstructor(method.method)) {
continue;
}
- IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder));
+ IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder()));
assert code != null;
Value thisValue = code.getThis();
assert thisValue != null;
@@ -205,7 +209,7 @@
// CHECK: references to field read usages are fixable.
boolean fixableFieldReads = true;
for (DexEncodedMethod method : info.referencedFrom) {
- IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.method.holder));
+ IRCode code = method.buildIR(appView, appView.appInfo().originFor(method.holder()));
assert code != null;
List<StaticGet> singletonFieldReads =
Streams.stream(code.instructionIterator())
@@ -301,7 +305,7 @@
Collection<BiConsumer<IRCode, MethodProcessor>> codeOptimizations,
OptimizationFeedback feedback,
OneTimeMethodProcessor methodProcessor) {
- Origin origin = appView.appInfo().originFor(method.method.holder);
+ Origin origin = appView.appInfo().originFor(method.holder());
IRCode code = method.buildIR(appView, origin);
codeOptimizations.forEach(codeOptimization -> codeOptimization.accept(code, methodProcessor));
CodeRewriter.removeAssumeInstructions(appView, code);
@@ -329,9 +333,8 @@
assert candidateInfo != null;
// Find and remove instantiation and its users.
- for (Instruction instruction : code.instructions()) {
- if (instruction.isNewInstance()
- && instruction.asNewInstance().clazz == candidateInfo.candidate.type) {
+ for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) {
+ if (newInstance.clazz == candidateInfo.candidate.type) {
// Remove all usages
// NOTE: requiring (a) the instance initializer to be trivial, (b) not allowing
// candidates with instance fields and (c) requiring candidate to directly
@@ -340,10 +343,31 @@
assert candidateInfo.candidate.superType == factory().objectType;
assert candidateInfo.candidate.instanceFields().size() == 0;
- Value singletonValue = instruction.outValue();
+ Value singletonValue = newInstance.outValue();
assert singletonValue != null;
- singletonValue.uniqueUsers().forEach(user -> user.removeOrReplaceByDebugLocalRead(code));
- instruction.removeOrReplaceByDebugLocalRead(code);
+
+ InvokeDirect uniqueConstructorInvoke =
+ newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
+ assert uniqueConstructorInvoke != null;
+ uniqueConstructorInvoke.removeOrReplaceByDebugLocalRead(code);
+
+ StaticPut uniqueStaticPut = null;
+ for (Instruction user : singletonValue.uniqueUsers()) {
+ if (user.isStaticPut()) {
+ assert uniqueStaticPut == null;
+ uniqueStaticPut = user.asStaticPut();
+ }
+ }
+ assert uniqueStaticPut != null;
+ uniqueStaticPut.removeOrReplaceByDebugLocalRead(code);
+
+ if (newInstance.outValue().hasAnyUsers()) {
+ TypeElement type = TypeElement.fromDexType(newInstance.clazz, maybeNull(), appView);
+ newInstance.replace(
+ new StaticGet(code.createValue(type), candidateInfo.singletonField.field), code);
+ } else {
+ newInstance.removeOrReplaceByDebugLocalRead(code);
+ }
return;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 20ea5df..140c844 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -228,24 +228,23 @@
int concatenationCount = 0;
// During the second iteration, count builders' usage.
for (Instruction instr : code.instructions()) {
- if (!instr.isInvokeVirtual()) {
- continue;
- }
- InvokeVirtual invoke = instr.asInvokeVirtual();
- DexMethod invokedMethod = invoke.getInvokedMethod();
- if (optimizationConfiguration.isAppendMethod(invokedMethod)) {
- concatenationCount++;
- // The analysis might be overwhelmed.
- if (concatenationCount > CONCATENATION_THRESHOLD) {
- return ImmutableSet.of();
- }
- } else if (optimizationConfiguration.isToStringMethod(invokedMethod)) {
- assert invoke.inValues().size() == 1;
- Value receiver = invoke.getReceiver().getAliasedValue();
- for (Value builder : collectAllLinkedBuilders(receiver)) {
- if (builderToStringCounts.containsKey(builder)) {
- int count = builderToStringCounts.getInt(builder);
- builderToStringCounts.put(builder, count + 1);
+ if (instr.isInvokeMethod()) {
+ InvokeMethod invoke = instr.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (optimizationConfiguration.isAppendMethod(invokedMethod)) {
+ concatenationCount++;
+ // The analysis might be overwhelmed.
+ if (concatenationCount > CONCATENATION_THRESHOLD) {
+ return ImmutableSet.of();
+ }
+ } else if (optimizationConfiguration.isToStringMethod(invokedMethod)) {
+ assert invoke.arguments().size() == 1;
+ Value receiver = invoke.getArgument(0).getAliasedValue();
+ for (Value builder : collectAllLinkedBuilders(receiver)) {
+ if (builderToStringCounts.containsKey(builder)) {
+ int count = builderToStringCounts.getInt(builder);
+ builderToStringCounts.put(builder, count + 1);
+ }
}
}
}
@@ -582,9 +581,9 @@
if (instr == null) {
break;
}
- InvokeVirtual invoke = instr.asInvokeVirtual();
- assert invoke.inValues().size() == 1;
- Value builder = invoke.getReceiver().getAliasedValue();
+ InvokeMethod invoke = instr.asInvokeMethod();
+ assert invoke.arguments().size() == 1;
+ Value builder = invoke.getArgument(0).getAliasedValue();
Value outValue = invoke.outValue();
if (outValue == null || outValue.isDead(appView, code)) {
// If the out value is still used but potentially dead, replace it with a dummy string.
@@ -627,16 +626,16 @@
}
private boolean isToStringOfInterest(Set<Value> candidateBuilders, Instruction instr) {
- if (!instr.isInvokeVirtual()) {
+ if (!instr.isInvokeMethod()) {
return false;
}
- InvokeVirtual invoke = instr.asInvokeVirtual();
+ InvokeMethod invoke = instr.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
if (!optimizationConfiguration.isToStringMethod(invokedMethod)) {
return false;
}
- assert invoke.inValues().size() == 1;
- Value builder = invoke.getReceiver().getAliasedValue();
+ assert invoke.arguments().size() == 1;
+ Value builder = invoke.getArgument(0).getAliasedValue();
if (!candidateBuilders.contains(builder)) {
return false;
}
@@ -736,11 +735,11 @@
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instr = it.next();
- if (instr.isInvokeVirtual()) {
- InvokeVirtual invoke = instr.asInvokeVirtual();
+ if (instr.isInvokeMethod()) {
+ InvokeMethod invoke = instr.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
if (optimizationConfiguration.isToStringMethod(invokedMethod)
- && buildersToRemove.contains(invoke.getReceiver().getAliasedValue())) {
+ && buildersToRemove.contains(invoke.getArgument(0).getAliasedValue())) {
it.removeOrReplaceByDebugLocalRead();
}
}
@@ -852,7 +851,8 @@
@Override
public boolean isToStringMethod(DexMethod method) {
return method == factory.stringBuilderMethods.toString
- || method == factory.stringBufferMethods.toString;
+ || method == factory.stringBufferMethods.toString
+ || method == factory.stringMethods.valueOf;
}
private boolean canHandleArgumentType(DexType argType) {
@@ -900,20 +900,12 @@
InvokeMethod invoke = escapeRoute.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
- // Make sure builder's uses are local, i.e., not escaping from the current method.
- if (invokedMethod.holder != builderType) {
- logEscapingRoute(false);
- return false;
- }
- // <init> is legitimate.
- if (optimizationConfiguration.isBuilderInit(invokedMethod, builderType)) {
- return true;
- }
+
if (optimizationConfiguration.isToStringMethod(invokedMethod)) {
Value out = escapeRoute.outValue();
if (out != null) {
- // If Builder#toString is interned, it could be used for equality check.
- // Replacing builder-based runtime result with a compile time constant may change
+ // If Builder#toString or String#valueOf is interned, it could be used for equality
+ // check. Replacing builder-based runtime result with a compile time constant may change
// the program's runtime behavior.
for (Instruction outUser : out.uniqueUsers()) {
if (outUser.isInvokeMethodWithReceiver()
@@ -924,7 +916,18 @@
}
}
}
- // Otherwise, use of Builder#toString is legitimate.
+ // Otherwise, use of Builder#toString and String#valueOf is legitimate.
+ return true;
+ }
+
+ // Make sure builder's uses are local, i.e., not escaping from the current method.
+ if (invokedMethod.holder != builderType) {
+ logEscapingRoute(false);
+ return false;
+ }
+
+ // <init> is legitimate.
+ if (optimizationConfiguration.isBuilderInit(invokedMethod, builderType)) {
return true;
}
// Even though all invocations belong to the builder type, there are some methods other
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index 1cb6482..f7e377d 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -139,14 +139,14 @@
if (move.definition.isArgument()) {
Argument argument = move.definition.asArgument();
int argumentRegister = argument.outValue().getLiveIntervals().getRegister();
- Value to = new FixedRegisterValue(argument.outValue().getType(), move.dst);
- Value from = new FixedRegisterValue(argument.outValue().getType(), argumentRegister);
+ Value to = new FixedRegisterValue(argument.getOutType(), move.dst);
+ Value from = new FixedRegisterValue(argument.getOutType(), argumentRegister);
instruction = new Move(to, from);
} else {
assert move.definition.isOutConstant();
ConstInstruction definition = move.definition.getOutConstantConstInstruction();
if (definition.isConstNumber()) {
- Value to = new FixedRegisterValue(move.definition.outValue().getType(), move.dst);
+ Value to = new FixedRegisterValue(move.definition.getOutType(), move.dst);
instruction = new ConstNumber(to, definition.asConstNumber().getRawValue());
} else {
throw new Unreachable("Unexpected definition");
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 50885dc..cb1823a 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -30,7 +30,6 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.utils.StringDiagnostic;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -41,24 +40,14 @@
super(appView, holder);
}
- boolean shouldConvert(
- DexType type, DesugaredLibraryAPIConverter converter, DexString methodName) {
+ boolean shouldConvert(DexType type, DesugaredLibraryAPIConverter converter, DexMethod method) {
if (!appView.rewritePrefix.hasRewrittenType(type, appView)) {
return false;
}
if (converter.canConvert(type)) {
return true;
}
- appView
- .options()
- .reporter
- .warning(
- new StringDiagnostic(
- "Desugared library API conversion failed for "
- + type
- + ", unexpected behavior for method "
- + methodName
- + "."));
+ converter.reportInvalidInvoke(type, method, "");
return false;
}
@@ -101,7 +90,7 @@
DexType[] newParameters = forwardMethod.proto.parameters.values.clone();
for (DexType param : forwardMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
- if (shouldConvert(param, converter, forwardMethod.name)) {
+ if (shouldConvert(param, converter, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
@@ -132,7 +121,7 @@
instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false));
}
- if (shouldConvert(returnType, converter, forwardMethod.name)) {
+ if (shouldConvert(returnType, converter, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
@@ -188,7 +177,7 @@
int stackIndex = 1;
for (DexType param : forwardMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
- if (shouldConvert(param, converter, forwardMethod.name)) {
+ if (shouldConvert(param, converter, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
@@ -208,7 +197,7 @@
}
DexType returnType = forwardMethod.proto.returnType;
- if (shouldConvert(returnType, converter, forwardMethod.name)) {
+ if (shouldConvert(returnType, converter, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
new file mode 100644
index 0000000..fbfacb8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.synthetic;
+
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfIf;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.ValueType;
+import java.util.ArrayList;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+public abstract class EnumUnboxingCfCodeProvider extends SyntheticCfCodeProvider {
+
+ EnumUnboxingCfCodeProvider(AppView<?> appView, DexType holder) {
+ super(appView, holder);
+ }
+
+ public static class EnumUnboxingValueOfCfCodeProvider extends EnumUnboxingCfCodeProvider {
+
+ private DexType enumType;
+ private EnumValueInfoMap map;
+
+ public EnumUnboxingValueOfCfCodeProvider(
+ AppView<?> appView, DexType holder, DexType enumType, EnumValueInfoMap map) {
+ super(appView, holder);
+ this.enumType = enumType;
+ this.map = map;
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ // Generated static method, for class com.x.MyEnum {A,B} would look like:
+ // int UtilityClass#com.x.MyEnum_valueOf(String s) {
+ // if (s == null) { throw npe("Name is null"); }
+ // if (s.equals("A")) { return 1;}
+ // if (s.equals("B")) { return 2;}
+ // throw new IllegalArgumentException(
+ // "No enum constant com.x.MyEnum." + s);
+ DexItemFactory factory = appView.dexItemFactory();
+ List<CfInstruction> instructions = new ArrayList<>();
+
+ // if (s == null) { throw npe("Name is null"); }
+ CfLabel nullDest = new CfLabel();
+ instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0));
+ instructions.add(new CfIf(If.Type.NE, ValueType.OBJECT, nullDest));
+ instructions.add(new CfNew(factory.npeType));
+ instructions.add(new CfStackInstruction(Opcode.Dup));
+ instructions.add(new CfConstString(appView.dexItemFactory().createString("Name is null")));
+ instructions.add(
+ new CfInvoke(Opcodes.INVOKESPECIAL, factory.npeMethods.initWithMessage, false));
+ instructions.add(new CfThrow());
+ instructions.add(nullDest);
+
+ // if (s.equals("A")) { return 1;}
+ // if (s.equals("B")) { return 2;}
+ map.forEach(
+ (field, enumValueInfo) -> {
+ CfLabel dest = new CfLabel();
+ instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0));
+ instructions.add(new CfConstString(field.name));
+ instructions.add(
+ new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.stringMethods.equals, false));
+ instructions.add(new CfIf(If.Type.EQ, ValueType.INT, dest));
+ instructions.add(new CfConstNumber(enumValueInfo.convertToInt(), ValueType.INT));
+ instructions.add(new CfReturn(ValueType.INT));
+ instructions.add(dest);
+ });
+
+ // throw new IllegalArgumentException("No enum constant com.x.MyEnum." + s);
+ instructions.add(new CfNew(factory.illegalArgumentExceptionType));
+ instructions.add(new CfStackInstruction(Opcode.Dup));
+ instructions.add(
+ new CfConstString(
+ appView
+ .dexItemFactory()
+ .createString(
+ "No enum constant " + enumType.toSourceString().replace('$', '.') + ".")));
+ instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0));
+ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.stringMethods.concat, false));
+ instructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESPECIAL,
+ factory.illegalArgumentExceptionMethods.initWithMessage,
+ false));
+ instructions.add(new CfThrow());
+ return standardCfCodeFromInstructions(instructions);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 688d59b..e04f07c 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -128,7 +128,8 @@
ClassWriter writer = new ClassWriter(0);
int markerStringPoolIndex = writer.newConst(markerString);
assert markerStringPoolIndex == MARKER_STRING_CONSTANT_POOL_INDEX;
- writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null);
+ String sourceDebug = getSourceDebugExtension(clazz.annotations());
+ writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
int version = getClassFileVersion(clazz);
int access = clazz.accessFlags.getAsCfAccessFlags();
String desc = namingLens.lookupDescriptor(clazz.type).toString();
@@ -242,6 +243,16 @@
return res.toString();
}
+ private String getSourceDebugExtension(DexAnnotationSet annotations) {
+ DexValue debugExtensions =
+ getSystemAnnotationValue(
+ annotations, application.dexItemFactory.annotationSourceDebugExtension);
+ if (debugExtensions == null) {
+ return null;
+ }
+ return debugExtensions.asDexValueString().getValue().toString();
+ }
+
private ImmutableMap<DexString, DexValue> getAnnotationDefaults(DexAnnotationSet annotations) {
DexValue value =
getSystemAnnotationValue(annotations, application.dexItemFactory.annotationDefault);
diff --git a/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java b/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java
new file mode 100644
index 0000000..ea1ddc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+import java.util.function.Function;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeProjection;
+import kotlinx.metadata.KmVariance;
+
+class ClassTypeSignatureToRenamedKmTypeConverter implements ClassTypeSignature.Converter<KmType> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final List<KmTypeParameter> typeParameters;
+ private final Function<DexType, String> toRenamedClassClassifier;
+
+ ClassTypeSignatureToRenamedKmTypeConverter(
+ AppView<AppInfoWithLiveness> appView,
+ List<KmTypeParameter> typeParameters,
+ Function<DexType, String> toRenamedClassClassifier) {
+ this.appView = appView;
+ this.typeParameters = typeParameters;
+ this.toRenamedClassClassifier = toRenamedClassClassifier;
+ }
+
+ @Override
+ public KmType init() {
+ return new KmType(flagsOf());
+ }
+
+ @Override
+ public KmType visitType(DexType type, KmType result) {
+ String classifier = toRenamedClassClassifier.apply(type);
+ if (classifier == null) {
+ return null;
+ }
+ result.visitClass(classifier);
+ return result;
+ }
+
+ @Override
+ public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) {
+ if (result == null) {
+ return null;
+ }
+ List<KmTypeProjection> arguments = result.getArguments();
+ KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature(
+ typeArgument,
+ () -> {
+ KmType kmTypeArgument = new KmType(flagsOf());
+ arguments.add(new KmTypeProjection(KmVariance.INVARIANT, kmTypeArgument));
+ return kmTypeArgument;
+ },
+ typeParameters,
+ appView.dexItemFactory());
+ return result;
+ }
+
+ @Override
+ public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
+ // Do nothing
+ return result;
+ }
+
+ public List<KmTypeParameter> getTypeParameters() {
+ return typeParameters;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 136947b..c891897 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -6,9 +6,6 @@
import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedClassifier;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmConstructor;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType;
import static kotlinx.metadata.Flag.IS_SEALED;
import com.android.tools.r8.graph.AppView;
@@ -22,7 +19,6 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.StringUtils;
import java.util.List;
import kotlinx.metadata.KmClass;
import kotlinx.metadata.KmConstructor;
@@ -77,21 +73,26 @@
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
if (appView.options().enableKotlinMetadataRewritingForRenamedClasses
&& lens.lookupType(clazz.type, appView.dexItemFactory()) != clazz.type) {
- String renamedClassifier = toRenamedClassifier(clazz.type, appView, lens);
+ String renamedClassifier = synthesizer.toRenamedClassifier(clazz.type);
if (renamedClassifier != null) {
assert !kmClass.getName().equals(renamedClassifier);
kmClass.setName(renamedClassifier);
}
}
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, getTypeParameters(), synthesizer::toRenamedClassifier);
+
// Rewriting upward hierarchy.
List<KmType> superTypes = kmClass.getSupertypes();
superTypes.clear();
for (DexType itfType : clazz.interfaces.values) {
// TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures
- KmType kmType = toRenamedKmType(itfType, null, appView, lens);
+ KmType kmType = synthesizer.toRenamedKmType(itfType, null, null, converter);
if (kmType != null) {
superTypes.add(kmType);
}
@@ -99,7 +100,8 @@
assert clazz.superType != null;
if (clazz.superType != appView.dexItemFactory().objectType) {
// TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature
- KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, null, appView, lens);
+ KmType kmTypeForSupertype =
+ synthesizer.toRenamedKmType(clazz.superType, null, null, converter);
if (kmTypeForSupertype != null) {
superTypes.add(kmTypeForSupertype);
}
@@ -131,7 +133,7 @@
sealedSubclasses.clear();
if (IS_SEALED.invoke(kmClass.getFlags())) {
for (DexType subtype : appView.appInfo().allImmediateSubtypes(clazz.type)) {
- String classifier = toRenamedClassifier(subtype, appView, lens);
+ String classifier = synthesizer.toRenamedClassifier(subtype);
if (classifier != null) {
sealedSubclasses.add(classifier);
}
@@ -149,7 +151,7 @@
if (!method.isInstanceInitializer()) {
continue;
}
- KmConstructor constructor = toRenamedKmConstructor(method, appView, lens);
+ KmConstructor constructor = synthesizer.toRenamedKmConstructor(method);
if (constructor != null) {
constructors.add(constructor);
}
@@ -162,7 +164,7 @@
// TODO(b/151193864): enum entries
- rewriteDeclarationContainer(appView, lens);
+ rewriteDeclarationContainer(synthesizer);
}
@Override
@@ -190,11 +192,13 @@
@Override
public String toString(String indent) {
StringBuilder sb = new StringBuilder(indent);
- sb.append("Metadata.Class {");
- sb.append(StringUtils.LINE_SEPARATOR);
- sb.append(kmDeclarationContainerToString(indent + INDENT));
- sb.append(indent);
- sb.append("}");
+ appendKmSection(
+ indent,
+ "Metadata.Class",
+ sb,
+ newIndent -> {
+ appendKmClass(newIndent, sb, kmClass);
+ });
return sb.toString();
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
index 627ac9d..fc3fbbe 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedBinaryName;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
@@ -46,12 +44,13 @@
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
ListIterator<String> partClassIterator = partClassNames.listIterator();
+ KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
while (partClassIterator.hasNext()) {
String partClassName = partClassIterator.next();
partClassIterator.remove();
DexType partClassType = appView.dexItemFactory().createType(
DescriptorUtils.getDescriptorFromClassBinaryName(partClassName));
- String renamedPartClassName = toRenamedBinaryName(partClassType, appView, lens);
+ String renamedPartClassName = synthesizer.toRenamedBinaryName(partClassType);
if (renamedPartClassName != null) {
partClassIterator.add(renamedPartClassName);
}
@@ -82,6 +81,6 @@
@Override
public String toString(String indent) {
- return indent + "MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")";
+ return indent + "MetaData.MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")";
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index e67cd05..90f265c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,15 +4,12 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedBinaryName;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -45,11 +42,12 @@
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
DexType facadeClassType = appView.dexItemFactory().createType(
DescriptorUtils.getDescriptorFromClassBinaryName(facadeClassName));
- facadeClassName = toRenamedBinaryName(facadeClassType, appView, lens);
+ KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
+ facadeClassName = synthesizer.toRenamedBinaryName(facadeClassType);
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
- rewriteDeclarationContainer(appView, lens);
+ rewriteDeclarationContainer(synthesizer);
}
@Override
@@ -85,12 +83,14 @@
@Override
public String toString(String indent) {
StringBuilder sb = new StringBuilder(indent);
- sb.append("Metadata.MultiFileClassPart {");
- sb.append(StringUtils.LINE_SEPARATOR);
- sb.append(kmDeclarationContainerToString(indent + INDENT));
- appendKeyValue(indent + INDENT, "facadeClassName", facadeClassName, sb);
- sb.append(indent);
- sb.append("}");
+ appendKmSection(
+ indent,
+ "Metadata.MultiFileClassPart",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "facadeClassName", sb, facadeClassName);
+ appendKmPackage(newIndent, sb, kmPackage);
+ });
return sb.toString();
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index 08fc8ef..8ec966b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -38,7 +38,7 @@
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
- rewriteDeclarationContainer(appView, lens);
+ rewriteDeclarationContainer(new KotlinMetadataSynthesizer(appView, lens, this));
}
@Override
@@ -66,11 +66,13 @@
@Override
public String toString(String indent) {
StringBuilder sb = new StringBuilder(indent);
- sb.append("Metadata.MultiFileClassPart {\n");
- sb.append(kmDeclarationContainerToString(indent + INDENT));
- appendKeyValue(indent + INDENT, "package", kmPackage.toString(), sb);
- sb.append(indent);
- sb.append("}");
+ appendKmSection(
+ indent,
+ "Metadata.FileFacade",
+ sb,
+ newIndent -> {
+ appendKmPackage(newIndent, sb, kmPackage);
+ });
return sb.toString();
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
index 967cf05..64ed559 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
+import static com.android.tools.r8.utils.StringUtils.LINE_SEPARATOR;
import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
import com.android.tools.r8.errors.Unreachable;
@@ -12,29 +12,46 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFieldInfo;
+import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo;
import com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.KmPropertyGroup;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.BiFunction;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import kotlinx.metadata.KmAnnotation;
+import kotlinx.metadata.KmAnnotationArgument;
+import kotlinx.metadata.KmClass;
+import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmPackage;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmTypeAlias;
import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeProjection;
+import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmMethodSignature;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
// Provides access to package/class-level Kotlin information.
public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> {
+
final DexClass clazz;
+ private static final List<KmTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of();
KotlinInfo(MetadataKind metadata, DexClass clazz) {
assert clazz != null;
@@ -52,6 +69,13 @@
abstract KotlinClassHeader createHeader();
+ public final List<KmTypeParameter> getTypeParameters() {
+ if (!this.isClass()) {
+ return EMPTY_TYPE_PARAMS;
+ }
+ return this.asClass().kmClass.getTypeParameters();
+ }
+
public enum Kind {
Class, File, Synthetic, Part, Facade
}
@@ -116,15 +140,37 @@
// {@link KmClass} and {@link KmPackage} are inherited from {@link KmDeclarationContainer} that
// abstract functions and properties. Rewriting of those portions can be unified here.
- void rewriteDeclarationContainer(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ void rewriteDeclarationContainer(KotlinMetadataSynthesizer synthesizer) {
assert clazz != null;
KmDeclarationContainer kmDeclarationContainer = getDeclarations();
- Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>();
+ rewriteFunctions(synthesizer, kmDeclarationContainer.getFunctions());
+ rewriteProperties(synthesizer, kmDeclarationContainer.getProperties());
+ }
+ private void rewriteFunctions(KotlinMetadataSynthesizer synthesizer, List<KmFunction> functions) {
+ functions.clear();
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.isInitializer()) {
+ continue;
+ }
+ if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) {
+ KmFunction function = synthesizer.toRenamedKmFunction(method);
+ if (function != null) {
+ functions.add(function);
+ }
+ }
+ // TODO(b/151194869): What should we do for methods that fall into this category---no mark?
+ }
+ }
+
+ private void rewriteProperties(
+ KotlinMetadataSynthesizer synthesizer, List<KmProperty> properties) {
+ Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>();
// Backing fields for a companion object are declared in its host class.
Iterable<DexEncodedField> fields = clazz.fields();
Predicate<DexEncodedField> backingFieldTester = DexEncodedField::isKotlinBackingField;
+ List<KmTypeParameter> classTypeParameters = getTypeParameters();
if (isClass()) {
KotlinClass ktClass = asClass();
if (IS_COMPANION_OBJECT.invoke(ktClass.kmClass.getFlags()) && ktClass.hostClass != null) {
@@ -132,54 +178,46 @@
backingFieldTester = DexEncodedField::isKotlinBackingFieldForCompanionObject;
}
}
-
for (DexEncodedField field : fields) {
if (backingFieldTester.test(field)) {
- String name = field.getKotlinMemberInfo().propertyName;
+ KotlinFieldInfo kotlinFieldInfo = field.getKotlinMemberInfo().asFieldInfo();
+ assert kotlinFieldInfo != null;
+ String name = kotlinFieldInfo.propertyName;
assert name != null;
KmPropertyGroup.Builder builder =
propertyGroupBuilderMap.computeIfAbsent(
name,
- k -> KmPropertyGroup.builder(field.getKotlinMemberInfo().propertyFlags, name));
+ k -> KmPropertyGroup.builder(kotlinFieldInfo.flags, name, classTypeParameters));
builder.foundBackingField(field);
}
}
-
- List<KmFunction> functions = kmDeclarationContainer.getFunctions();
- functions.clear();
for (DexEncodedMethod method : clazz.methods()) {
if (method.isInitializer()) {
continue;
}
-
- if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) {
- KmFunction function = toRenamedKmFunction(method, appView, lens);
- if (function != null) {
- functions.add(function);
- }
- continue;
- }
if (method.isKotlinProperty() || method.isKotlinExtensionProperty()) {
- String name = method.getKotlinMemberInfo().propertyName;
+ assert method.getKotlinMemberInfo().isPropertyInfo();
+ KotlinPropertyInfo kotlinPropertyInfo = method.getKotlinMemberInfo().asPropertyInfo();
+ String name = kotlinPropertyInfo.propertyName;
assert name != null;
KmPropertyGroup.Builder builder =
propertyGroupBuilderMap.computeIfAbsent(
name,
// Hitting here (creating a property builder) after visiting all fields means that
// this property doesn't have a backing field. Don't use members' flags.
- k -> KmPropertyGroup.builder(method.getKotlinMemberInfo().propertyFlags, name));
- switch (method.getKotlinMemberInfo().memberKind) {
+ k -> KmPropertyGroup.builder(kotlinPropertyInfo.flags, name, classTypeParameters));
+ switch (kotlinPropertyInfo.memberKind) {
case EXTENSION_PROPERTY_GETTER:
builder.isExtensionGetter();
// fallthrough;
case PROPERTY_GETTER:
- builder.foundGetter(method, method.getKotlinMemberInfo().flags);
+ builder.foundGetter(method, kotlinPropertyInfo);
break;
case EXTENSION_PROPERTY_SETTER:
builder.isExtensionSetter();
// fallthrough;
case PROPERTY_SETTER:
- builder.foundSetter(method, method.getKotlinMemberInfo().flags);
+ builder.foundSetter(method, kotlinPropertyInfo);
break;
case EXTENSION_PROPERTY_ANNOTATIONS:
builder.isExtensionAnnotations();
@@ -190,20 +228,16 @@
default:
throw new Unreachable("Not a Kotlin property: " + method.getKotlinMemberInfo());
}
- continue;
}
-
// TODO(b/151194869): What should we do for methods that fall into this category---no mark?
}
-
- List<KmProperty> properties = kmDeclarationContainer.getProperties();
properties.clear();
for (KmPropertyGroup.Builder builder : propertyGroupBuilderMap.values()) {
KmPropertyGroup group = builder.build();
if (group == null) {
continue;
}
- KmProperty property = group.toRenamedKmProperty(appView, lens);
+ KmProperty property = group.toRenamedKmProperty(synthesizer);
if (property != null) {
properties.add(property);
}
@@ -212,124 +246,557 @@
public abstract String toString(String indent);
- String kmDeclarationContainerToString(String indent) {
- StringBuilder sb = new StringBuilder();
- KmDeclarationContainer declarations = getDeclarations();
- appendKmSection(indent, "functions", declarations.getFunctions(), this::kmFunctionToString, sb);
- appendKmSection(
- indent, "properties", declarations.getProperties(), this::kmPropertyToString, sb);
- appendKmSection(
- indent, "typeAliases", declarations.getTypeAliases(), this::kmTypeAliasToString, sb);
- return sb.toString();
+ static final String INDENT = " ";
+
+ private static <T> void appendKmHelper(
+ String key, StringBuilder sb, Action appendContent, String start, String end) {
+ sb.append(key);
+ sb.append(start);
+ appendContent.execute();
+ sb.append(end);
}
- final String INDENT = " ";
+ static <T> void appendKmSection(
+ String indent, String typeDescription, StringBuilder sb, Consumer<String> appendContent) {
+ appendKmHelper(
+ typeDescription,
+ sb,
+ () -> appendContent.accept(indent + INDENT),
+ "{" + LINE_SEPARATOR,
+ indent + "}");
+ }
- private <T> void appendKmSection(
+ static <T> void appendKmList(
String indent,
- String header,
+ String typeDescription,
+ StringBuilder sb,
List<T> items,
- BiFunction<String, T, String> stringify,
- StringBuilder sb) {
- if (items.size() > 0) {
- sb.append(indent);
- sb.append(header);
- sb.append(": [");
- sb.append(StringUtils.LINE_SEPARATOR);
+ BiConsumer<String, T> appendItem) {
+ if (items.isEmpty()) {
+ sb.append(typeDescription).append("[]");
+ return;
}
- for (T item : items) {
- sb.append(stringify.apply(indent + INDENT, item));
- sb.append(",");
- sb.append(StringUtils.LINE_SEPARATOR);
- }
- if (items.size() > 0) {
- sb.append(indent);
- sb.append("]");
- sb.append(StringUtils.LINE_SEPARATOR);
- }
+ appendKmHelper(
+ typeDescription,
+ sb,
+ () -> {
+ for (T kmItem : items) {
+ sb.append(indent).append(INDENT);
+ appendItem.accept(indent + INDENT, kmItem);
+ sb.append(LINE_SEPARATOR);
+ }
+ },
+ "[" + LINE_SEPARATOR,
+ indent + "]");
}
- private String kmFunctionToString(String indent, KmFunction function) {
- assert function != null;
- StringBuilder sb = new StringBuilder();
+ static void appendKeyValue(
+ String indent, String key, StringBuilder sb, Consumer<String> appendValue) {
sb.append(indent);
- sb.append("KmFunction {");
- sb.append(StringUtils.LINE_SEPARATOR);
- String newIndent = indent + INDENT;
- KmType receiverParameterType = function.getReceiverParameterType();
+ appendKmHelper(key, sb, () -> appendValue.accept(indent), ": ", "," + LINE_SEPARATOR);
+ }
+
+ static void appendKeyValue(String indent, String key, StringBuilder sb, String value) {
+ sb.append(indent);
+ appendKmHelper(key, sb, () -> sb.append(value), ": ", "," + LINE_SEPARATOR);
+ }
+
+ static void appendKmDeclarationContainer(
+ String indent, StringBuilder sb, KmDeclarationContainer container) {
appendKeyValue(
- newIndent,
- "receiverParameterType",
- receiverParameterType == null ? "null" : kmTypeToString(receiverParameterType),
- sb);
- appendKeyValue(newIndent, "returnType", kmTypeToString(function.returnType), sb);
- appendKeyValue(newIndent, "name", function.getName(), sb);
- // TODO(b/148581822): Print flags, generic signature etc.
- sb.append(indent);
- sb.append("}");
- return sb.toString();
+ indent,
+ "functions",
+ sb,
+ newIndent -> {
+ appendKmList(
+ newIndent,
+ "KmFunction",
+ sb,
+ container.getFunctions().stream()
+ .sorted(Comparator.comparing(KmFunction::getName))
+ .collect(Collectors.toList()),
+ (nextIndent, kmFunction) -> {
+ appendKmFunction(nextIndent, sb, kmFunction);
+ });
+ });
+ appendKeyValue(
+ indent,
+ "properties",
+ sb,
+ newIndent -> {
+ appendKmList(
+ newIndent,
+ "KmProperty",
+ sb,
+ container.getProperties().stream()
+ .sorted(Comparator.comparing(KmProperty::getName))
+ .collect(Collectors.toList()),
+ (nextIndent, kmProperty) -> {
+ appendKmProperty(nextIndent, sb, kmProperty);
+ });
+ });
+ appendKeyValue(
+ indent,
+ "typeAliases",
+ sb,
+ newIndent -> {
+ appendKmList(
+ newIndent,
+ "KmTypeAlias",
+ sb,
+ container.getTypeAliases().stream()
+ .sorted(Comparator.comparing(KmTypeAlias::getName))
+ .collect(Collectors.toList()),
+ (nextIndent, kmTypeAlias) -> {
+ appendTypeAlias(nextIndent, sb, kmTypeAlias);
+ });
+ });
}
- private String kmPropertyToString(String indent, KmProperty property) {
- // TODO(b/148581822): Add information.
- return indent + "KmProperty { " + property + "}";
+ static void appendKmPackage(String indent, StringBuilder sb, KmPackage kmPackage) {
+ appendKmDeclarationContainer(indent, sb, kmPackage);
+ appendKeyValue(indent, "moduleName", sb, JvmExtensionsKt.getModuleName(kmPackage));
+ appendKeyValue(
+ indent,
+ "localDelegatedProperties",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmProperty",
+ sb,
+ JvmExtensionsKt.getLocalDelegatedProperties(kmPackage),
+ (nextNextIndent, kmProperty) -> {
+ appendKmProperty(nextNextIndent, sb, kmProperty);
+ });
+ });
}
- private String kmTypeAliasToString(String indent, KmTypeAlias alias) {
- assert alias != null;
- StringBuilder sb = new StringBuilder(indent);
- sb.append("KmTypeAlias {");
- sb.append(StringUtils.LINE_SEPARATOR);
- String newIndent = indent + INDENT;
- appendKeyValue(newIndent, "name", alias.getName(), sb);
- if (!alias.getTypeParameters().isEmpty()) {
- appendKeyValue(
- newIndent,
- "typeParameters",
- alias.getTypeParameters().stream()
- .map(KmTypeParameter::getName)
- .collect(Collectors.joining(",")),
- sb);
+ static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) {
+ appendKeyValue(indent, "flags", sb, kmClass.getFlags() + "");
+ appendKeyValue(indent, "name", sb, kmClass.getName());
+ appendKeyValue(
+ indent,
+ "typeParameters",
+ sb,
+ newIndent -> {
+ appendTypeParameters(newIndent, sb, kmClass.getTypeParameters());
+ });
+ appendKeyValue(
+ indent,
+ "superTypes",
+ sb,
+ newIndent -> {
+ appendKmList(
+ newIndent,
+ "KmType",
+ sb,
+ kmClass.getSupertypes(),
+ (nextIndent, kmType) -> {
+ appendKmType(nextIndent, sb, kmType);
+ });
+ });
+ String companionObject = kmClass.getCompanionObject();
+ appendKeyValue(
+ indent, "enumEntries", sb, "[" + StringUtils.join(kmClass.getEnumEntries(), ",") + "]");
+ appendKeyValue(
+ indent, "companionObject", sb, companionObject == null ? "null" : companionObject);
+ appendKeyValue(
+ indent,
+ "sealedSubclasses",
+ sb,
+ "[" + StringUtils.join(kmClass.getSealedSubclasses(), ",") + "]");
+ appendKeyValue(
+ indent, "nestedClasses", sb, "[" + StringUtils.join(kmClass.getNestedClasses(), ",") + "]");
+ appendKeyValue(
+ indent,
+ "anonymousObjectOriginName",
+ sb,
+ JvmExtensionsKt.getAnonymousObjectOriginName(kmClass));
+ appendKeyValue(indent, "moduleName", sb, JvmExtensionsKt.getModuleName(kmClass));
+ appendKeyValue(
+ indent,
+ "localDelegatedProperties",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmProperty",
+ sb,
+ JvmExtensionsKt.getLocalDelegatedProperties(kmClass),
+ (nextNextIndent, kmProperty) -> {
+ appendKmProperty(nextNextIndent, sb, kmProperty);
+ });
+ });
+ appendKeyValue(
+ indent,
+ "constructors",
+ sb,
+ newIndent -> {
+ appendKmList(
+ newIndent,
+ "KmConstructor",
+ sb,
+ kmClass.getConstructors(),
+ (nextIndent, constructor) -> {
+ appendKmConstructor(nextIndent, sb, constructor);
+ });
+ });
+ appendKmDeclarationContainer(indent, sb, kmClass);
+ }
+
+ private static void appendKmConstructor(
+ String indent, StringBuilder sb, KmConstructor constructor) {
+ appendKmSection(
+ indent,
+ "KmConstructor",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "flags", sb, constructor.getFlags() + "");
+ appendKeyValue(
+ newIndent,
+ "valueParameters",
+ sb,
+ nextIndent ->
+ appendValueParameters(nextIndent, sb, constructor.getValueParameters()));
+ JvmMethodSignature signature = JvmExtensionsKt.getSignature(constructor);
+ appendKeyValue(
+ newIndent, "signature", sb, signature != null ? signature.asString() : "null");
+ });
+ }
+
+ private static void appendKmFunction(String indent, StringBuilder sb, KmFunction function) {
+ appendKmSection(
+ indent,
+ "KmFunction",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "flags", sb, function.getFlags() + "");
+ appendKeyValue(newIndent, "name", sb, function.getName());
+ appendKeyValue(
+ newIndent,
+ "receiverParameterType",
+ sb,
+ nextIndent -> appendKmType(nextIndent, sb, function.getReceiverParameterType()));
+ appendKeyValue(
+ newIndent,
+ "returnType",
+ sb,
+ nextIndent -> appendKmType(nextIndent, sb, function.getReturnType()));
+ appendKeyValue(
+ newIndent,
+ "typeParameters",
+ sb,
+ nextIndent -> appendTypeParameters(nextIndent, sb, function.getTypeParameters()));
+ appendKeyValue(
+ newIndent,
+ "valueParameters",
+ sb,
+ nextIndent -> appendValueParameters(nextIndent, sb, function.getValueParameters()));
+ JvmMethodSignature signature = JvmExtensionsKt.getSignature(function);
+ appendKeyValue(
+ newIndent, "signature", sb, signature != null ? signature.asString() : "null");
+ appendKeyValue(
+ newIndent,
+ "lambdaClassOriginName",
+ sb,
+ JvmExtensionsKt.getLambdaClassOriginName(function));
+ });
+ }
+
+ private static void appendKmProperty(String indent, StringBuilder sb, KmProperty kmProperty) {
+ appendKmSection(
+ indent,
+ "KmProperty",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "flags", sb, kmProperty.getFlags() + "");
+ appendKeyValue(newIndent, "name", sb, kmProperty.getName());
+ appendKeyValue(
+ newIndent,
+ "receiverParameterType",
+ sb,
+ nextIndent -> appendKmType(nextIndent, sb, kmProperty.getReceiverParameterType()));
+ appendKeyValue(
+ newIndent,
+ "returnType",
+ sb,
+ nextIndent -> appendKmType(nextIndent, sb, kmProperty.getReturnType()));
+ appendKeyValue(
+ newIndent,
+ "typeParameters",
+ sb,
+ nextIndent -> appendTypeParameters(nextIndent, sb, kmProperty.getTypeParameters()));
+ appendKeyValue(newIndent, "getterFlags", sb, kmProperty.getGetterFlags() + "");
+ appendKeyValue(newIndent, "setterFlags", sb, kmProperty.getSetterFlags() + "");
+ appendKeyValue(
+ newIndent,
+ "setterParameter",
+ sb,
+ nextIndent -> appendValueParameter(nextIndent, sb, kmProperty.getSetterParameter()));
+ appendKeyValue(newIndent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmProperty) + "");
+ JvmFieldSignature fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
+ appendKeyValue(
+ newIndent,
+ "fieldSignature",
+ sb,
+ fieldSignature != null ? fieldSignature.asString() : "null");
+ JvmMethodSignature getterSignature = JvmExtensionsKt.getGetterSignature(kmProperty);
+ appendKeyValue(
+ newIndent,
+ "getterSignature",
+ sb,
+ getterSignature != null ? getterSignature.asString() : "null");
+ JvmMethodSignature setterSignature = JvmExtensionsKt.getSetterSignature(kmProperty);
+ appendKeyValue(
+ newIndent,
+ "setterSignature",
+ sb,
+ setterSignature != null ? setterSignature.asString() : "null");
+ JvmMethodSignature syntheticMethod =
+ JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty);
+ appendKeyValue(
+ newIndent,
+ "syntheticMethodForAnnotations",
+ sb,
+ syntheticMethod != null ? syntheticMethod.asString() : "null");
+ });
+ }
+
+ private static void appendKmType(String indent, StringBuilder sb, KmType kmType) {
+ if (kmType == null) {
+ sb.append("null");
+ return;
}
- appendType(newIndent, "underlyingType", alias.underlyingType, sb);
- appendType(newIndent, "expandedType", alias.expandedType, sb);
- // TODO(b/151783973): Extend with annotations.
- sb.append(indent);
- sb.append("}");
- return sb.toString();
+ appendKmSection(
+ indent,
+ "KmType",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "flags", sb, kmType.getFlags() + "");
+ appendKeyValue(newIndent, "classifier", sb, kmType.classifier.toString());
+ appendKeyValue(
+ newIndent,
+ "arguments",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmTypeProjection",
+ sb,
+ kmType.getArguments(),
+ (nextNextIndent, kmTypeProjection) -> {
+ appendKmTypeProjection(nextNextIndent, sb, kmTypeProjection);
+ });
+ });
+ appendKeyValue(
+ newIndent,
+ "abbreviatedType",
+ sb,
+ nextIndent -> appendKmType(newIndent, sb, kmType.getAbbreviatedType()));
+ appendKeyValue(
+ newIndent,
+ "outerType",
+ sb,
+ nextIndent -> appendKmType(newIndent, sb, kmType.getOuterType()));
+ appendKeyValue(
+ newIndent,
+ "extensions",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmAnnotion",
+ sb,
+ JvmExtensionsKt.getAnnotations(kmType),
+ (nextNextIndent, kmAnnotation) -> {
+ appendKmAnnotation(nextNextIndent, sb, kmAnnotation);
+ });
+ });
+ });
}
- void appendType(String indent, String key, KmType kmType, StringBuilder sb) {
- sb.append(indent);
- sb.append(key);
- sb.append(" {");
- sb.append(StringUtils.LINE_SEPARATOR);
- String newIndent = indent + INDENT;
- appendKeyValue(newIndent, "classifier", kmType.classifier.toString(), sb);
- if (!kmType.getArguments().isEmpty()) {
- appendKeyValue(
- newIndent,
- "arguments",
- kmType.getArguments().stream()
- .map(arg -> arg.getType().classifier.toString())
- .collect(Collectors.joining(",")),
- sb);
+ private static void appendKmTypeProjection(
+ String indent, StringBuilder sb, KmTypeProjection projection) {
+ appendKmSection(
+ indent,
+ "KmTypeProjection",
+ sb,
+ newIndent -> {
+ appendKeyValue(
+ newIndent,
+ "type",
+ sb,
+ nextIndent -> {
+ appendKmType(nextIndent, sb, projection.getType());
+ });
+ if (projection.getVariance() != null) {
+ appendKeyValue(newIndent, "variance", sb, projection.getVariance().name());
+ }
+ });
+ }
+
+ private static void appendValueParameters(
+ String indent, StringBuilder sb, List<KmValueParameter> valueParameters) {
+ appendKmList(
+ indent,
+ "KmValueParameter",
+ sb,
+ valueParameters,
+ (newIndent, parameter) -> {
+ appendValueParameter(newIndent, sb, parameter);
+ });
+ }
+
+ private static void appendValueParameter(
+ String indent, StringBuilder sb, KmValueParameter valueParameter) {
+ if (valueParameter == null) {
+ sb.append("null");
+ return;
}
- sb.append(indent);
- sb.append("}");
- sb.append(StringUtils.LINE_SEPARATOR);
+ appendKmSection(
+ indent,
+ "KmValueParameter",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "flags", sb, valueParameter.getFlags() + "");
+ appendKeyValue(newIndent, "name", sb, valueParameter.getName());
+ appendKeyValue(
+ newIndent,
+ "type",
+ sb,
+ nextIndent -> {
+ appendKmType(nextIndent, sb, valueParameter.getType());
+ });
+ appendKeyValue(
+ newIndent,
+ "varargElementType",
+ sb,
+ nextIndent -> {
+ appendKmType(nextIndent, sb, valueParameter.getVarargElementType());
+ });
+ });
}
- void appendKeyValue(String indent, String key, String value, StringBuilder sb) {
- sb.append(indent);
- sb.append(key);
- sb.append(": ");
- sb.append(value);
- sb.append(StringUtils.LINE_SEPARATOR);
+ private static void appendTypeParameters(
+ String indent, StringBuilder sb, List<KmTypeParameter> typeParameters) {
+ appendKmList(
+ indent,
+ "KmTypeParameter",
+ sb,
+ typeParameters,
+ (newIndent, parameter) -> {
+ appendTypeParameter(newIndent, sb, parameter);
+ });
}
- private String kmTypeToString(KmType type) {
- return DescriptorUtils.getDescriptorFromKmType(type);
+ private static void appendTypeParameter(
+ String indent, StringBuilder sb, KmTypeParameter typeParameter) {
+ appendKmSection(
+ indent,
+ "KmTypeParameter",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "id", sb, typeParameter.getId() + "");
+ appendKeyValue(newIndent, "flags", sb, typeParameter.getFlags() + "");
+ appendKeyValue(newIndent, "name", sb, typeParameter.getName());
+ appendKeyValue(newIndent, "variance", sb, typeParameter.getVariance().name());
+ appendKeyValue(
+ newIndent,
+ "upperBounds",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmType",
+ sb,
+ typeParameter.getUpperBounds(),
+ (nextNextIndent, kmType) -> {
+ appendKmType(nextNextIndent, sb, kmType);
+ });
+ });
+ appendKeyValue(
+ newIndent,
+ "extensions",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmAnnotion",
+ sb,
+ JvmExtensionsKt.getAnnotations(typeParameter),
+ (nextNextIndent, kmAnnotation) -> {
+ appendKmAnnotation(nextNextIndent, sb, kmAnnotation);
+ });
+ });
+ });
+ }
+
+ private static void appendTypeAlias(String indent, StringBuilder sb, KmTypeAlias kmTypeAlias) {
+ appendKmSection(
+ indent,
+ "KmTypeAlias",
+ sb,
+ newIndent -> {
+ appendKeyValue(
+ newIndent,
+ "annotations",
+ sb,
+ nextIndent -> {
+ appendKmList(
+ nextIndent,
+ "KmAnnotation",
+ sb,
+ kmTypeAlias.getAnnotations(),
+ (nextNextIndent, kmAnnotation) -> {
+ appendKmAnnotation(nextNextIndent, sb, kmAnnotation);
+ });
+ });
+ appendKeyValue(
+ newIndent,
+ "expandedType",
+ sb,
+ nextIndent -> {
+ appendKmType(nextIndent, sb, kmTypeAlias.expandedType);
+ });
+ appendKeyValue(newIndent, "flags", sb, kmTypeAlias.getFlags() + "");
+ appendKeyValue(newIndent, "name", sb, kmTypeAlias.getName());
+ appendKeyValue(
+ newIndent,
+ "typeParameters",
+ sb,
+ nextIndent -> {
+ appendTypeParameters(nextIndent, sb, kmTypeAlias.getTypeParameters());
+ });
+ appendKeyValue(
+ newIndent,
+ "underlyingType",
+ sb,
+ nextIndent -> {
+ appendKmType(nextIndent, sb, kmTypeAlias.underlyingType);
+ });
+ });
+ }
+
+ private static void appendKmAnnotation(
+ String indent, StringBuilder sb, KmAnnotation kmAnnotation) {
+ appendKmSection(
+ indent,
+ "KmAnnotation",
+ sb,
+ newIndent -> {
+ appendKeyValue(newIndent, "className", sb, kmAnnotation.getClassName());
+ appendKeyValue(
+ newIndent,
+ "arguments",
+ sb,
+ nextIndent -> {
+ Map<String, KmAnnotationArgument<?>> arguments = kmAnnotation.getArguments();
+ for (String key : arguments.keySet()) {
+ appendKeyValue(nextIndent, key, sb, arguments.get(key).toString());
+ }
+ });
+ });
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
index dfdb28b..ed7b49f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -3,6 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.CONSTRUCTOR;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_FUNCTION;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_GETTER;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_SETTER;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.FUNCTION;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_GETTER;
+import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_SETTER;
+import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.getJvmMethodSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension;
@@ -10,7 +18,6 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor;
import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
@@ -18,18 +25,20 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.jvm.JvmMethodSignature;
// Provides access to field/method-level Kotlin information.
-public class KotlinMemberInfo {
- private static final List<KmValueParameter> EMPTY_PARAM = ImmutableList.of();
+public abstract class KotlinMemberInfo {
+
private static final List<KotlinValueParameterInfo> EMPTY_PARAM_INFO = ImmutableList.of();
- private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO =
- new KotlinMemberInfo(MemberKind.NONE, 0, EMPTY_PARAM);
+ private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = new NoKotlinMemberInfo();
public static KotlinMemberInfo getNoKotlinMemberInfo() {
return NO_KOTLIN_MEMBER_INFO;
@@ -38,76 +47,198 @@
public final MemberKind memberKind;
// Original member flags. May be necessary to keep Kotlin-specific flag, e.g., suspend function.
final int flags;
- // TODO(b/151194869): better to split into FunctionInfo v.s. PropertyInfo ?
- // Original property flags. E.g., for property getter, getter flags are stored to `flags`, while
- // the property's flags are stored here, in case of properties without a backing field.
- final int propertyFlags;
- // Original property name for (extension) property. Otherwise, null.
- final String propertyName;
- // Information from original KmValueParameter(s) if available. Otherwise, null.
- private final List<KotlinValueParameterInfo> valueParameterInfos;
- // Constructor for KmFunction
- private KotlinMemberInfo(
- MemberKind memberKind, int flags, List<KmValueParameter> kmValueParameters) {
- this(memberKind, flags, 0, null, kmValueParameters);
- }
-
- // Constructor for a backing field and a getter in KmProperty
- private KotlinMemberInfo(
- MemberKind memberKind, int flags, int propertyFlags, String propertyName) {
- this(memberKind, flags, propertyFlags, propertyName, EMPTY_PARAM);
- }
-
- // Constructor for a setter in KmProperty
- private KotlinMemberInfo(
- MemberKind memberKind,
- int flags,
- int propertyFlags,
- String propertyName,
- KmValueParameter kmValueParameter) {
- this(
- memberKind,
- flags,
- propertyFlags,
- propertyName,
- kmValueParameter != null ? ImmutableList.of(kmValueParameter) : EMPTY_PARAM);
- }
-
- private KotlinMemberInfo(
- MemberKind memberKind,
- int flags,
- int propertyFlags,
- String propertyName,
- List<KmValueParameter> kmValueParameters) {
+ private KotlinMemberInfo(MemberKind memberKind, int flags) {
this.memberKind = memberKind;
this.flags = flags;
- this.propertyFlags = propertyFlags;
- this.propertyName = propertyName;
- assert kmValueParameters != null;
- if (kmValueParameters.isEmpty()) {
- this.valueParameterInfos = EMPTY_PARAM_INFO;
- } else {
- this.valueParameterInfos = new ArrayList<>(kmValueParameters.size());
- for (KmValueParameter kmValueParameter : kmValueParameters) {
- valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter));
- }
+ }
+
+ public boolean isFunctionInfo() {
+ return false;
+ }
+
+ public KotlinFunctionInfo asFunctionInfo() {
+ return null;
+ }
+
+ public boolean isFieldInfo() {
+ return false;
+ }
+
+ public KotlinFieldInfo asFieldInfo() {
+ return null;
+ }
+
+ public boolean isPropertyInfo() {
+ return false;
+ }
+
+ public KotlinPropertyInfo asPropertyInfo() {
+ return null;
+ }
+
+ private static class NoKotlinMemberInfo extends KotlinMemberInfo {
+
+ private NoKotlinMemberInfo() {
+ super(MemberKind.NONE, 0);
}
}
- KotlinValueParameterInfo getValueParameterInfo(int i) {
- if (valueParameterInfos.isEmpty()) {
- return null;
+ public static class KotlinFunctionInfo extends KotlinMemberInfo {
+
+ // Information from original KmValueParameter(s) if available.
+ final List<KotlinValueParameterInfo> valueParameterInfos;
+ // Information from original KmFunction.returnType. Null if this is from a KmConstructor.
+ public final KotlinTypeInfo returnType;
+
+ private KotlinFunctionInfo(
+ MemberKind memberKind,
+ int flags,
+ KotlinTypeInfo returnType,
+ List<KotlinValueParameterInfo> valueParameterInfos) {
+ super(memberKind, flags);
+ assert memberKind.isFunction() || memberKind.isConstructor();
+ this.returnType = returnType;
+ this.valueParameterInfos = valueParameterInfos;
}
- if (i < 0 || i >= valueParameterInfos.size()) {
- return null;
+
+ KotlinValueParameterInfo getValueParameterInfo(int i) {
+ if (valueParameterInfos.isEmpty()) {
+ return null;
+ }
+ if (i < 0 || i >= valueParameterInfos.size()) {
+ return null;
+ }
+ return valueParameterInfos.get(i);
}
- return valueParameterInfos.get(i);
+
+ @Override
+ public boolean isFunctionInfo() {
+ return true;
+ }
+
+ @Override
+ public KotlinFunctionInfo asFunctionInfo() {
+ return this;
+ }
+ }
+
+ public static class KotlinFieldInfo extends KotlinMemberInfo {
+
+ // Original property name for (extension) property. Otherwise, null.
+ final String propertyName;
+
+ private KotlinFieldInfo(MemberKind memberKind, int flags, String propertyName) {
+ super(memberKind, flags);
+ this.propertyName = propertyName;
+ }
+
+ @Override
+ public boolean isFieldInfo() {
+ return true;
+ }
+
+ @Override
+ public KotlinFieldInfo asFieldInfo() {
+ return this;
+ }
+ }
+
+ public static class KotlinPropertyInfo extends KotlinMemberInfo {
+
+ // Original getter flags. E.g., for property getter.
+ final int getterFlags;
+
+ // Original setter flags. E.g., for property setter.
+ final int setterFlags;
+
+ // Original property name for (extension) property. Otherwise, null.
+ final String propertyName;
+
+ // Original return type information. This should never be NULL (even for setters without field).
+ final KotlinTypeInfo returnType;
+
+ // Information from original KmValueParameter if available.
+ final KotlinValueParameterInfo valueParameterInfo;
+
+ private KotlinPropertyInfo(
+ MemberKind memberKind,
+ int flags,
+ int getterFlags,
+ int setterFlags,
+ String propertyName,
+ KotlinTypeInfo returnType,
+ KotlinValueParameterInfo valueParameterInfo) {
+ super(memberKind, flags);
+ this.getterFlags = getterFlags;
+ this.setterFlags = setterFlags;
+ this.propertyName = propertyName;
+ this.returnType = returnType;
+ this.valueParameterInfo = valueParameterInfo;
+ }
+
+ @Override
+ public KotlinPropertyInfo asPropertyInfo() {
+ return this;
+ }
+
+ @Override
+ public boolean isPropertyInfo() {
+ return true;
+ }
+ }
+
+ private static KotlinFunctionInfo createFunctionInfoFromConstructor(KmConstructor kmConstructor) {
+ return createFunctionInfo(
+ CONSTRUCTOR, kmConstructor.getFlags(), null, kmConstructor.getValueParameters());
+ }
+
+ private static KotlinFunctionInfo createFunctionInfo(
+ MemberKind memberKind, KmFunction kmFunction) {
+ return createFunctionInfo(
+ memberKind,
+ kmFunction.getFlags(),
+ kmFunction.getReturnType(),
+ kmFunction.getValueParameters());
+ }
+
+ private static KotlinFunctionInfo createFunctionInfo(
+ MemberKind memberKind, int flags, KmType returnType, List<KmValueParameter> valueParameters) {
+ assert memberKind.isFunction() || memberKind.isConstructor();
+ KotlinTypeInfo returnTypeInfo = KotlinTypeInfo.create(returnType);
+ assert memberKind.isFunction() || memberKind.isConstructor();
+ if (valueParameters.isEmpty()) {
+ return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, EMPTY_PARAM_INFO);
+ }
+ List<KotlinValueParameterInfo> valueParameterInfos = new ArrayList<>(valueParameters.size());
+ for (KmValueParameter kmValueParameter : valueParameters) {
+ valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter));
+ }
+ return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, valueParameterInfos);
+ }
+
+ private static KotlinFieldInfo createFieldInfo(MemberKind memberKind, KmProperty kmProperty) {
+ assert memberKind.isBackingField() || memberKind.isBackingFieldForCompanionObject();
+ return new KotlinFieldInfo(memberKind, kmProperty.getFlags(), kmProperty.getName());
+ }
+
+ private static KotlinPropertyInfo createPropertyInfo(
+ MemberKind memberKind, KmProperty kmProperty) {
+ assert memberKind.isProperty();
+ return new KotlinPropertyInfo(
+ memberKind,
+ kmProperty.getFlags(),
+ kmProperty.getGetterFlags(),
+ kmProperty.getSetterFlags(),
+ kmProperty.getName(),
+ KotlinTypeInfo.create(kmProperty.getReturnType()),
+ KotlinValueParameterInfo.fromKmValueParameter(kmProperty.getSetterParameter()));
}
public enum MemberKind {
NONE,
+ CONSTRUCTOR,
FUNCTION,
EXTENSION_FUNCTION,
@@ -122,6 +253,10 @@
EXTENSION_PROPERTY_SETTER,
EXTENSION_PROPERTY_ANNOTATIONS;
+ public boolean isConstructor() {
+ return this == CONSTRUCTOR;
+ }
+
public boolean isFunction() {
return this == FUNCTION || isExtensionFunction();
}
@@ -159,23 +294,37 @@
return;
}
- KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations();
- String companionObject = null;
- if (kotlinInfo.isClass()) {
- companionObject = kotlinInfo.asClass().kmClass.getCompanionObject();
- }
-
+ Map<String, KmConstructor> kmConstructorMap = new HashMap<>();
Map<String, KmFunction> kmFunctionMap = new HashMap<>();
Map<String, KmProperty> kmPropertyFieldMap = new HashMap<>();
Map<String, KmProperty> kmPropertyGetterMap = new HashMap<>();
Map<String, KmProperty> kmPropertySetterMap = new HashMap<>();
- kmDeclarationContainer.getFunctions().forEach(kmFunction -> {
- KmFunctionProcessor functionProcessor = new KmFunctionProcessor(kmFunction, reporter);
- if (functionProcessor.signature() != null) {
- kmFunctionMap.put(functionProcessor.signature().asString(), kmFunction);
- }
- });
+ KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations();
+ String companionObject = null;
+ if (kotlinInfo.isClass()) {
+ companionObject = kotlinInfo.asClass().kmClass.getCompanionObject();
+ kotlinInfo
+ .asClass()
+ .kmClass
+ .getConstructors()
+ .forEach(
+ kmConstructor -> {
+ JvmMethodSignature methodSignature = getJvmMethodSignature(kmConstructor, reporter);
+ if (methodSignature != null) {
+ kmConstructorMap.put(methodSignature.asString(), kmConstructor);
+ }
+ });
+ }
+ kmDeclarationContainer
+ .getFunctions()
+ .forEach(
+ kmFunction -> {
+ JvmMethodSignature methodSignature = getJvmMethodSignature(kmFunction, reporter);
+ if (methodSignature != null) {
+ kmFunctionMap.put(methodSignature.asString(), kmFunction);
+ }
+ });
kmDeclarationContainer.getProperties().forEach(kmProperty -> {
KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty, reporter);
if (propertyProcessor.fieldSignature() != null) {
@@ -200,72 +349,35 @@
if (kmPropertyFieldMap.containsKey(key)) {
KmProperty kmProperty = kmPropertyFieldMap.get(key);
field.setKotlinMemberInfo(
- new KotlinMemberInfo(
+ createFieldInfo(
clazz == kotlinInfo.clazz
? MemberKind.PROPERTY_BACKING_FIELD
: MemberKind.COMPANION_OBJECT_BACKING_FIELD,
- kmProperty.getFlags(),
- kmProperty.getFlags(),
- kmProperty.getName()));
+ kmProperty));
}
}
for (DexEncodedMethod method : clazz.methods()) {
- if (method.isInitializer()) {
- continue;
- }
String key = toJvmMethodSignature(method.method).asString();
- if (kmFunctionMap.containsKey(key)) {
+ if (kmConstructorMap.containsKey(key)) {
+ // Interestingly we cannot assert that the method is a jvm initializer, because the jvm
+ // signature can be a different.
+ method.setKotlinMemberInfo(createFunctionInfoFromConstructor(kmConstructorMap.get(key)));
+ } else if (kmFunctionMap.containsKey(key)) {
KmFunction kmFunction = kmFunctionMap.get(key);
- if (isExtension(kmFunction)) {
- method.setKotlinMemberInfo(
- new KotlinMemberInfo(
- MemberKind.EXTENSION_FUNCTION,
- kmFunction.getFlags(),
- kmFunction.getValueParameters()));
- } else {
- method.setKotlinMemberInfo(
- new KotlinMemberInfo(
- MemberKind.FUNCTION,
- kmFunction.getFlags(),
- kmFunction.getValueParameters()));
- }
+ method.setKotlinMemberInfo(
+ createFunctionInfo(
+ isExtension(kmFunction) ? EXTENSION_FUNCTION : FUNCTION, kmFunction));
} else if (kmPropertyGetterMap.containsKey(key)) {
KmProperty kmProperty = kmPropertyGetterMap.get(key);
- if (isExtension(kmProperty)) {
- method.setKotlinMemberInfo(
- new KotlinMemberInfo(
- MemberKind.EXTENSION_PROPERTY_GETTER,
- kmProperty.getGetterFlags(),
- kmProperty.getFlags(),
- kmProperty.getName()));
- } else {
- method.setKotlinMemberInfo(
- new KotlinMemberInfo(
- MemberKind.PROPERTY_GETTER,
- kmProperty.getGetterFlags(),
- kmProperty.getFlags(),
- kmProperty.getName()));
- }
+ method.setKotlinMemberInfo(
+ createPropertyInfo(
+ isExtension(kmProperty) ? EXTENSION_PROPERTY_GETTER : PROPERTY_GETTER, kmProperty));
} else if (kmPropertySetterMap.containsKey(key)) {
KmProperty kmProperty = kmPropertySetterMap.get(key);
- if (isExtension(kmProperty)) {
- method.setKotlinMemberInfo(
- new KotlinMemberInfo(
- MemberKind.EXTENSION_PROPERTY_SETTER,
- kmProperty.getSetterFlags(),
- kmProperty.getFlags(),
- kmProperty.getName(),
- kmProperty.getSetterParameter()));
- } else {
- method.setKotlinMemberInfo(
- new KotlinMemberInfo(
- MemberKind.PROPERTY_SETTER,
- kmProperty.getSetterFlags(),
- kmProperty.getFlags(),
- kmProperty.getName(),
- kmProperty.getSetterParameter()));
- }
+ method.setKotlinMemberInfo(
+ createPropertyInfo(
+ isExtension(kmProperty) ? EXTENSION_PROPERTY_SETTER : PROPERTY_SETTER, kmProperty));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
index 474c123..855a71f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -24,14 +24,12 @@
import kotlinx.metadata.KmConstructorVisitor;
import kotlinx.metadata.KmExtensionType;
import kotlinx.metadata.KmFunction;
-import kotlinx.metadata.KmFunctionExtensionVisitor;
-import kotlinx.metadata.KmFunctionVisitor;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmPropertyExtensionVisitor;
import kotlinx.metadata.KmPropertyVisitor;
import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
import kotlinx.metadata.jvm.JvmFieldSignature;
-import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
import kotlinx.metadata.jvm.JvmMethodSignature;
import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
@@ -174,37 +172,25 @@
}
}
- static class KmFunctionProcessor {
- // Custom name via @JvmName("..."). Otherwise, null.
- private JvmMethodSignature signature = null;
+ // Custom name via @JvmName("..."). Otherwise, null.
+ static JvmMethodSignature getJvmMethodSignature(KmConstructor kmConstructor, Reporter reporter) {
+ return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmConstructor), reporter);
+ }
- KmFunctionProcessor(KmFunction kmFunction, Reporter reporter) {
- kmFunction.accept(new KmFunctionVisitor() {
- @Override
- public KmFunctionExtensionVisitor visitExtensions(KmExtensionType type) {
- if (type != JvmFunctionExtensionVisitor.TYPE) {
- return null;
- }
- return new JvmFunctionExtensionVisitor() {
- @Override
- public void visit(JvmMethodSignature desc) {
- assert signature == null : signature.asString();
- signature = desc;
- }
- };
- }
- });
- if (signature != null) {
- String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
- if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
- signature = new JvmMethodSignature(signature.getName(), remappedDesc);
- }
+ // Custom name via @JvmName("..."). Otherwise, null.
+ static JvmMethodSignature getJvmMethodSignature(KmFunction kmFunction, Reporter reporter) {
+ return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmFunction), reporter);
+ }
+
+ private static JvmMethodSignature remapJvmMethodSignature(
+ JvmMethodSignature signature, Reporter reporter) {
+ if (signature != null) {
+ String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
+ if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
+ signature = new JvmMethodSignature(signature.getName(), remappedDesc);
}
}
-
- JvmMethodSignature signature() {
- return signature;
- }
+ return signature;
}
static class KmPropertyProcessor {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 5d1777f..f83796f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -61,6 +61,8 @@
}
public void run(ExecutorService executorService) throws ExecutionException {
+ // TODO(b/152283077): Don't disable the assert.
+ appView.appInfo().disableDefinitionForAssert();
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
@@ -92,6 +94,7 @@
}
},
executorService);
+ appView.appInfo().enableDefinitionForAssert();
}
private DexAnnotation createKotlinMetadataAnnotation(KotlinClassHeader header) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index 43b5cf2..2e90094 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -3,9 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.Kotlin.NAME;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.toClassifier;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
@@ -24,16 +25,21 @@
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFunctionInfo;
+import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Box;
import java.util.List;
import java.util.function.Consumer;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeParameter;
import kotlinx.metadata.KmTypeProjection;
import kotlinx.metadata.KmValueParameter;
import kotlinx.metadata.KmVariance;
@@ -41,6 +47,17 @@
class KotlinMetadataSynthesizer {
+ private final AppView<AppInfoWithLiveness> appView;
+ private final NamingLens lens;
+ private final List<KmTypeParameter> classTypeParameters;
+
+ public KotlinMetadataSynthesizer(
+ AppView<AppInfoWithLiveness> appView, NamingLens lens, KotlinInfo<?> kotlinInfo) {
+ this.appView = appView;
+ this.lens = lens;
+ this.classTypeParameters = kotlinInfo.getTypeParameters();
+ }
+
static boolean isExtension(KmFunction kmFunction) {
return kmFunction.getReceiverParameterType() != null;
}
@@ -55,8 +72,7 @@
return kmType;
}
- static DexType toRenamedType(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ private DexType toRenamedType(DexType type) {
// For library or classpath class, synthesize @Metadata always.
// For a program class, make sure it is live.
if (!appView.appInfo().isNonProgramTypeOrLiveProgramType(type)) {
@@ -70,141 +86,92 @@
return renamedType;
}
- static String toRenamedBinaryName(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- DexType renamedType = toRenamedType(type, appView, lens);
+ String toRenamedBinaryName(DexType type) {
+ DexType renamedType = toRenamedType(type);
if (renamedType == null) {
return null;
}
return getBinaryNameFromDescriptor(renamedType.toDescriptorString());
}
- static String toRenamedClassifier(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray
- if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) {
- DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type);
- assert convertedType != null;
- return descriptorToKotlinClassifier(convertedType.toDescriptorString());
- }
- // E.g., [Ljava/lang/String; -> kotlin/Array
- if (type.isArrayType()) {
- return NAME + "/Array";
- }
- DexType renamedType = toRenamedType(type, appView, lens);
- if (renamedType == null) {
+ String toRenamedClassifier(DexType type) {
+ type = type.isClassType() ? toRenamedType(type) : type;
+ if (type == null) {
return null;
}
- return descriptorToKotlinClassifier(renamedType.toDescriptorString());
+ return toClassifier(type, appView.dexItemFactory());
}
// TODO(b/148654451): Canonicalization?
- static KmType toRenamedKmType(
+ KmType toRenamedKmType(
DexType type,
TypeSignature typeSignature,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
- if (typeSignature != null
- && typeSignature.isFieldTypeSignature()
- && typeSignature.asFieldTypeSignature().isClassTypeSignature()
- && typeSignature.asFieldTypeSignature().asClassTypeSignature().type() == type) {
- return toRenamedKmType(
- typeSignature.asFieldTypeSignature().asClassTypeSignature(), appView, lens);
+ KotlinTypeInfo originalKotlinTypeInfo,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ if (originalKotlinTypeInfo != null && originalKotlinTypeInfo.isTypeAlias()) {
+ KmType kmType = new KmType(flagsOf());
+ kmType.visitTypeAlias(originalKotlinTypeInfo.asTypeAlias().getName());
+ return kmType;
}
+ return toRenamedKmTypeWithClassifier(type, typeSignature, converter);
+ }
- String classifier = toRenamedClassifier(type, appView, lens);
+ private KmType toRenamedKmTypeWithClassifierForFieldSignature(
+ DexType type,
+ FieldTypeSignature fieldSignature,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ if (fieldSignature.isClassTypeSignature()) {
+ ClassTypeSignature classTypeSignature = fieldSignature.asClassTypeSignature();
+ if (classTypeSignature.type() != type) {
+ return null;
+ }
+ return classTypeSignature.convert(converter);
+ }
+ // If we fail to set kmType.classifier we will get a UninitializedPropertyAccessException:
+ // lateinit property classifier has not been initialized.
+ Box<KmType> kmTypeBox = new Box<>();
+ populateKmTypeFromSignature(
+ fieldSignature,
+ () -> {
+ KmType value = new KmType(flagsOf());
+ kmTypeBox.set(value);
+ return value;
+ },
+ converter.getTypeParameters(),
+ appView.dexItemFactory());
+ return kmTypeBox.get();
+ }
+
+ private KmType toRenamedKmTypeWithClassifier(
+ DexType type,
+ TypeSignature typeSignature,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ if (typeSignature != null && typeSignature.isFieldTypeSignature()) {
+ KmType renamedKmType =
+ toRenamedKmTypeWithClassifierForFieldSignature(
+ type, typeSignature.asFieldTypeSignature(), converter);
+ if (renamedKmType != null) {
+ return renamedKmType;
+ }
+ }
+ String classifier = toRenamedClassifier(type);
if (classifier == null) {
return null;
}
- // TODO(b/151194869): Mysterious, why attempts to properly set flags bothers kotlinc?
- // and/or why wiping out flags works for KmType but not KmFunction?!
- KmType kmType = new KmType(flagsOf());
- kmType.visitClass(classifier);
+ // Seems like no flags for KmType are ever set, thus passing in flagsOf() seems ok.
+ KmType renamedKmType = new KmType(flagsOf());
+ renamedKmType.visitClass(classifier);
// TODO(b/151194164): Can be generalized too, like ArrayTypeSignature.Converter ?
// E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String))
if (type.isArrayType() && !type.isPrimitiveArrayType()) {
DexType elementType = type.toArrayElementType(appView.dexItemFactory());
- KmType argumentType = toRenamedKmType(elementType, null, appView, lens);
- kmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType));
- }
- return kmType;
- }
-
- static KmType toRenamedKmType(
- ClassTypeSignature classTypeSignature,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
- return classTypeSignature.convert(
- new ClassTypeSignatureToRenamedKmTypeConverter(appView, lens));
- }
-
- static class ClassTypeSignatureToRenamedKmTypeConverter
- implements ClassTypeSignature.Converter<KmType> {
-
- private final AppView<AppInfoWithLiveness> appView;
- private final NamingLens lens;
-
- ClassTypeSignatureToRenamedKmTypeConverter(
- AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- this.appView = appView;
- this.lens = lens;
- }
-
- @Override
- public KmType init() {
- return new KmType(flagsOf());
- }
-
- @Override
- public KmType visitType(DexType type, KmType result) {
- if (result == null) {
- return result;
- }
- String classifier = toRenamedClassifier(type, appView, lens);
- if (classifier == null) {
- return null;
- }
- result.visitClass(classifier);
- return result;
- }
-
- @Override
- public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) {
- if (result == null) {
- return result;
- }
- if (typeArgument.isClassTypeSignature()) {
- KmType argumentType = typeArgument.asClassTypeSignature().convert(this);
- result.getArguments().add(new KmTypeProjection(KmVariance.INVARIANT, argumentType));
- }
- // TODO(b/151194164): for TypeVariableSignature, there is KmType::visitTypeParameter.
- return result;
- }
-
- @Override
- public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
- // Do nothing
- return result;
- }
- }
-
- private static KmType setRenamedKmType(
- DexType type,
- TypeSignature signature,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens,
- Consumer<KmType> consumer) {
- KmType renamedKmType = toRenamedKmType(type, signature, appView, lens);
- if (renamedKmType != null) {
- consumer.accept(renamedKmType);
+ KmType argumentType = toRenamedKmTypeWithClassifier(elementType, null, converter);
+ renamedKmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType));
}
return renamedKmType;
}
- static KmConstructor toRenamedKmConstructor(
- DexEncodedMethod method,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ KmConstructor toRenamedKmConstructor(DexEncodedMethod method) {
// Make sure it is an instance initializer and live.
if (!method.isInstanceInitializer()
|| !appView.appInfo().liveMethods.contains(method.method)) {
@@ -213,17 +180,23 @@
KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags());
JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+ List<KmTypeParameter> typeParameters =
+ convertFormalTypeParameters(
+ signature.getFormalTypeParameters(),
+ kmTypeParameter -> {
+ assert false : "KmConstructor cannot have additional type parameters";
+ });
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, this::toRenamedClassifier);
List<KmValueParameter> parameters = kmConstructor.getValueParameters();
- if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
+ if (!populateKmValueParameters(method, signature, parameters, converter)) {
return null;
}
return kmConstructor;
}
- static KmFunction toRenamedKmFunction(
- DexEncodedMethod method,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ KmFunction toRenamedKmFunction(DexEncodedMethod method) {
// For library overrides, synthesize @Metadata always.
// For regular methods, make sure it is live or pinned.
if (!method.isLibraryMethodOverride().isTrue()
@@ -237,9 +210,11 @@
: method.toSourceString() + " -> " + renamedMethod.toSourceString();
// TODO(b/151194869): Should we keep kotlin-specific flags only while synthesizing the base
// value from general JVM flags?
+ KotlinFunctionInfo kotlinMemberInfo = method.getKotlinMemberInfo().asFunctionInfo();
+ assert kotlinMemberInfo != null;
int flag =
- appView.appInfo().isPinned(method.method) && method.getKotlinMemberInfo() != null
- ? method.getKotlinMemberInfo().flags
+ appView.appInfo().isPinned(method.method)
+ ? kotlinMemberInfo.flags
: method.accessFlags.getAsKotlinFlags();
KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
@@ -248,57 +223,65 @@
// on demand? That may require utils to map internal encoding of signature back to
// corresponding backend definitions, like DexAnnotation (DEX) or Signature attribute (CF).
MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+ List<KmTypeParameter> typeParameters = kmFunction.getTypeParameters();
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView,
+ convertFormalTypeParameters(signature.getFormalTypeParameters(), typeParameters::add),
+ this::toRenamedClassifier);
DexProto proto = method.method.proto;
DexType returnType = proto.returnType;
TypeSignature returnSignature = signature.returnType().typeSignature();
- KmType kmReturnType = setRenamedKmType(
- returnType, returnSignature, appView, lens, kmFunction::setReturnType);
+ KmType kmReturnType =
+ toRenamedKmType(returnType, returnSignature, kotlinMemberInfo.returnType, converter);
if (kmReturnType == null) {
return null;
}
-
+ kmFunction.setReturnType(kmReturnType);
if (method.isKotlinExtensionFunction()) {
- assert proto.parameters.values.length > 0
- : method.method.toSourceString();
+ assert proto.parameters.values.length > 0 : method.method.toSourceString();
DexType receiverType = proto.parameters.values[0];
TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- KmType kmReceiverType = setRenamedKmType(
- receiverType, receiverSignature, appView, lens, kmFunction::setReceiverParameterType);
+ KmType kmReceiverType = toRenamedKmType(receiverType, receiverSignature, null, converter);
if (kmReceiverType == null) {
return null;
}
+ kmFunction.setReceiverParameterType(kmReceiverType);
}
- List<KmValueParameter> parameters = kmFunction.getValueParameters();
- if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
+ if (!populateKmValueParameters(method, signature, kmFunction.getValueParameters(), converter)) {
return null;
}
return kmFunction;
}
- private static boolean populateKmValueParameters(
- List<KmValueParameter> parameters,
+ private List<KmTypeParameter> convertFormalTypeParameters(
+ List<FormalTypeParameter> parameters, Consumer<KmTypeParameter> addedFromParameters) {
+ return KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ classTypeParameters, parameters, appView.dexItemFactory(), addedFromParameters);
+ }
+
+ private boolean populateKmValueParameters(
DexEncodedMethod method,
MethodTypeSignature signature,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ List<KmValueParameter> parameters,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ KotlinFunctionInfo kotlinFunctionInfo = method.getKotlinMemberInfo().asFunctionInfo();
+ if (kotlinFunctionInfo == null) {
+ return false;
+ }
boolean isExtension = method.isKotlinExtensionFunction();
for (int i = isExtension ? 1 : 0; i < method.method.proto.parameters.values.length; i++) {
DexType parameterType = method.method.proto.parameters.values[i];
DebugLocalInfo debugLocalInfo = method.getParameterInfo().get(i);
String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
KotlinValueParameterInfo valueParameterInfo =
- method.getKotlinMemberInfo().getValueParameterInfo(isExtension ? i - 1 : i);
+ kotlinFunctionInfo.getValueParameterInfo(isExtension ? i - 1 : i);
TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i);
KmValueParameter kmValueParameter =
toRewrittenKmValueParameter(
- valueParameterInfo,
- parameterType,
- parameterTypeSignature,
- parameterName,
- appView,
- lens);
+ valueParameterInfo, parameterType, parameterTypeSignature, parameterName, converter);
if (kmValueParameter == null) {
return false;
}
@@ -307,26 +290,28 @@
return true;
}
- private static KmValueParameter toRewrittenKmValueParameter(
+ private KmValueParameter toRewrittenKmValueParameter(
KotlinValueParameterInfo valueParameterInfo,
DexType parameterType,
TypeSignature parameterTypeSignature,
String candidateParameterName,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
int flag = valueParameterInfo != null ? valueParameterInfo.flag : flagsOf();
String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName;
KmValueParameter kmValueParameter = new KmValueParameter(flag, name);
- KmType kmParamType = setRenamedKmType(
- parameterType, parameterTypeSignature, appView, lens, kmValueParameter::setType);
+ KotlinTypeInfo originalKmTypeInfo = valueParameterInfo != null ? valueParameterInfo.type : null;
+ KmType kmParamType =
+ toRenamedKmType(parameterType, parameterTypeSignature, originalKmTypeInfo, converter);
if (kmParamType == null) {
return null;
}
+ kmValueParameter.setType(kmParamType);
if (valueParameterInfo != null) {
JvmExtensionsKt.getAnnotations(kmParamType).addAll(valueParameterInfo.annotations);
}
if (valueParameterInfo != null && valueParameterInfo.isVararg) {
+ // TODO(b/152389234): Test for arrays in varargs.
if (!parameterType.isArrayType()) {
return null;
}
@@ -334,11 +319,11 @@
TypeSignature elementSignature =
parameterTypeSignature != null
? parameterTypeSignature.toArrayElementTypeSignature(appView) : null;
- KmType kmElementType = setRenamedKmType(
- elementType, elementSignature, appView, lens, kmValueParameter::setVarargElementType);
+ KmType kmElementType = toRenamedKmType(elementType, elementSignature, null, converter);
if (kmElementType == null) {
return null;
}
+ kmValueParameter.setVarargElementType(kmElementType);
}
return kmValueParameter;
@@ -370,58 +355,65 @@
* getter, and so on.
*/
static class KmPropertyGroup {
+
final int flags;
final String name;
final DexEncodedField field;
- final DexEncodedMethod getter;
- final int getterFlags;
- final DexEncodedMethod setter;
- final int setterFlags;
+ private final DexEncodedMethod getter;
+ private final KotlinPropertyInfo getterInfo;
+ private final DexEncodedMethod setter;
+ private final KotlinPropertyInfo setterInfo;
final DexEncodedMethod annotations;
final boolean isExtension;
+ private final List<KmTypeParameter> classTypeParameters;
private KmPropertyGroup(
int flags,
String name,
DexEncodedField field,
DexEncodedMethod getter,
- int getterFlags,
+ KotlinPropertyInfo getterInfo,
DexEncodedMethod setter,
- int setterFlags,
+ KotlinPropertyInfo setterInfo,
DexEncodedMethod annotations,
- boolean isExtension) {
+ boolean isExtension,
+ List<KmTypeParameter> classTypeParameters) {
this.flags = flags;
this.name = name;
this.field = field;
this.getter = getter;
- this.getterFlags = getterFlags;
+ this.getterInfo = getterInfo;
this.setter = setter;
- this.setterFlags = setterFlags;
+ this.setterInfo = setterInfo;
this.annotations = annotations;
this.isExtension = isExtension;
+ this.classTypeParameters = classTypeParameters;
}
- static Builder builder(int flags, String name) {
- return new Builder(flags, name);
+ static Builder builder(int flags, String name, List<KmTypeParameter> classTypeParameters) {
+ return new Builder(flags, name, classTypeParameters);
}
static class Builder {
+
private final int flags;
private final String name;
private DexEncodedField field;
private DexEncodedMethod getter;
- private int getterFlags;
+ private KotlinPropertyInfo getterInfo;
private DexEncodedMethod setter;
- private int setterFlags;
+ private KotlinPropertyInfo setterInfo;
private DexEncodedMethod annotations;
+ private List<KmTypeParameter> classTypeParameters;
private boolean isExtensionGetter;
private boolean isExtensionSetter;
private boolean isExtensionAnnotations;
- private Builder(int flags, String name) {
+ private Builder(int flags, String name, List<KmTypeParameter> classTypeParameters) {
this.flags = flags;
this.name = name;
+ this.classTypeParameters = classTypeParameters;
}
Builder foundBackingField(DexEncodedField field) {
@@ -429,15 +421,15 @@
return this;
}
- Builder foundGetter(DexEncodedMethod getter, int flags) {
+ Builder foundGetter(DexEncodedMethod getter, KotlinPropertyInfo propertyInfo) {
this.getter = getter;
- this.getterFlags = flags;
+ this.getterInfo = propertyInfo;
return this;
}
- Builder foundSetter(DexEncodedMethod setter, int flags) {
+ Builder foundSetter(DexEncodedMethod setter, KotlinPropertyInfo propertyInfo) {
this.setter = setter;
- this.setterFlags = flags;
+ this.setterInfo = propertyInfo;
return this;
}
@@ -476,177 +468,234 @@
}
}
return new KmPropertyGroup(
- flags, name, field, getter, getterFlags, setter, setterFlags, annotations, isExtension);
+ flags,
+ name,
+ field,
+ getter,
+ getterInfo,
+ setter,
+ setterInfo,
+ annotations,
+ isExtension,
+ classTypeParameters);
}
}
- KmProperty toRenamedKmProperty(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf());
- KmType kmPropertyType = null;
- KmType kmReceiverType = null;
-
- // A flag to indicate we can rename the property name. This will become false if any member
- // is pinned. Then, we conservatively assume that users want the property to be pinned too.
- // That is, we won't rename the property even though some other members could be renamed.
- boolean canChangePropertyName = true;
- // A candidate property name. Not overwritten by the following members, hence the order of
- // preference: a backing field, getter, and setter.
- String renamedPropertyName = name;
- if (field != null) {
- if (appView.appInfo().isPinned(field.field)) {
- canChangePropertyName = false;
+ private String setFieldForProperty(
+ KmProperty kmProperty,
+ KmType defaultPropertyType,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ KotlinMetadataSynthesizer synthesizer) {
+ DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory());
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, this.classTypeParameters, synthesizer::toRenamedClassifier);
+ if (kmProperty.getReturnType() == defaultPropertyType) {
+ KmType kmPropertyType =
+ synthesizer.toRenamedKmType(field.field.type, null, null, converter);
+ if (kmPropertyType != null) {
+ kmProperty.setReturnType(kmPropertyType);
}
- DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory());
- if (canChangePropertyName && renamedField.name != field.field.name) {
- renamedPropertyName = renamedField.name.toString();
- }
- FieldTypeSignature signature =
- GenericSignature.Parser.toFieldTypeSignature(field, appView);
- kmPropertyType =
- setRenamedKmType(field.field.type, signature, appView, lens, kmProperty::setReturnType);
- JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField));
}
+ JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField));
+ return appView.appInfo().isPinned(field.field) ? null : renamedField.name.toString();
+ }
- GetterSetterCriteria criteria = checkGetterCriteria();
- if (criteria == GetterSetterCriteria.VIOLATE) {
- return null;
+ private boolean setReceiverParameterTypeForExtensionProperty(
+ KmProperty kmProperty,
+ DexEncodedMethod method,
+ AppView<AppInfoWithLiveness> appView,
+ KotlinMetadataSynthesizer synthesizer) {
+ if (!isExtension) {
+ return true;
}
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(method, appView);
+ List<KmTypeParameter> typeParameters =
+ KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ classTypeParameters,
+ signature.getFormalTypeParameters(),
+ appView.dexItemFactory(),
+ kmTypeParameter -> {});
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, synthesizer::toRenamedClassifier);
+ DexType receiverType = method.method.proto.parameters.values[0];
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+ KmType kmReceiverType =
+ synthesizer.toRenamedKmType(receiverType, receiverSignature, null, converter);
+ if (kmProperty.getReceiverParameterType() != null) {
+ // If the receiver type for the extension property is set already make sure it's consistent.
+ return getDescriptorFromKmType(kmReceiverType)
+ .equals(getDescriptorFromKmType(kmProperty.getReceiverParameterType()));
+ }
+ kmProperty.setReceiverParameterType(kmReceiverType);
+ return true;
+ }
- if (criteria == GetterSetterCriteria.MET) {
- assert getter != null && getter.method.proto.parameters.size() == (isExtension ? 1 : 0)
- : "checkGetterCriteria: " + this.toString();
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(getter, appView);
- if (isExtension) {
- TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- kmReceiverType =
- setRenamedKmType(
- getter.method.proto.parameters.values[0],
- receiverSignature,
- appView,
- lens,
- kmProperty::setReceiverParameterType);
- }
-
- DexType returnType = getter.method.proto.returnType;
- TypeSignature returnSignature = signature.returnType().typeSignature();
- if (kmPropertyType == null) {
- // The property type is not set yet.
- kmPropertyType = setRenamedKmType(
- returnType, returnSignature, appView, lens, kmProperty::setReturnType);
- } else {
- // If property type is set already (via backing field), make sure it's consistent.
- KmType kmPropertyTypeFromGetter =
- toRenamedKmType(returnType, returnSignature, appView, lens);
- if (!getDescriptorFromKmType(kmPropertyType)
- .equals(getDescriptorFromKmType(kmPropertyTypeFromGetter))) {
- return null;
- }
- }
- if (appView.appInfo().isPinned(getter.method)) {
- canChangePropertyName = false;
- }
- DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory());
- if (canChangePropertyName
- && renamedGetter.name != getter.method.name
- && renamedPropertyName.equals(name)) {
- renamedPropertyName = renamedGetter.name.toString();
- }
- kmProperty.setGetterFlags(getterFlags);
- JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter));
- } else if (field != null) {
+ private String setGetterForProperty(
+ KmProperty kmProperty,
+ KmType defaultPropertyType,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ KotlinMetadataSynthesizer synthesizer) {
+ if (checkGetterCriteria() == GetterSetterCriteria.NOT_AVAILABLE) {
// Property without getter.
// Even though a getter does not exist, `kotlinc` still set getter flags and use them to
// determine when to direct field access v.s. getter calls for property resolution.
- kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
+ if (field != null) {
+ kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
+ }
+ return kmProperty.getName();
}
+ assert checkGetterCriteria() == GetterSetterCriteria.MET;
+ assert getter != null;
+ DexProto proto = getter.method.proto;
+ assert proto.parameters.size() == (isExtension ? 1 : 0)
+ : "checkGetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(getter, appView);
+ List<KmTypeParameter> typeParameters =
+ KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ this.classTypeParameters,
+ signature.getFormalTypeParameters(),
+ appView.dexItemFactory(),
+ kmTypeParameter -> {});
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, synthesizer::toRenamedClassifier);
+ DexType returnType = proto.returnType;
+ TypeSignature returnSignature = signature.returnType().typeSignature();
+ KmType kmPropertyType =
+ synthesizer.toRenamedKmType(
+ returnType, returnSignature, getterInfo.returnType, converter);
+ if (kmProperty.getReturnType() == defaultPropertyType) {
+ // The property type is not set yet.
+ kmProperty.setReturnType(kmPropertyType);
+ } else if (!getDescriptorFromKmType(kmPropertyType)
+ .equals(getDescriptorFromKmType(kmProperty.getReturnType()))) {
+ // If property type is set already (via backing field), make sure it's consistent.
+ return null;
+ }
+ DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory());
+ kmProperty.setGetterFlags(getter.accessFlags.getAsKotlinFlags());
+ JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter));
+ return appView.appInfo().isPinned(getter.method) ? null : renamedGetter.name.toString();
+ }
- criteria = checkSetterCriteria();
+ private String setSetterForProperty(
+ KmProperty kmProperty,
+ KmType defaultPropertyType,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ KotlinMetadataSynthesizer synthesizer) {
+ GetterSetterCriteria criteria = checkSetterCriteria();
if (criteria == GetterSetterCriteria.VIOLATE) {
- return null;
+ return kmProperty.getName();
}
-
- if (criteria == GetterSetterCriteria.MET) {
- assert setter != null && setter.method.proto.parameters.size() == (isExtension ? 2 : 1)
- : "checkSetterCriteria: " + this.toString();
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(setter, appView);
- if (isExtension) {
- DexType receiverType = setter.method.proto.parameters.values[0];
- TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- if (kmReceiverType == null) {
- kmReceiverType =
- setRenamedKmType(
- receiverType,
- receiverSignature,
- appView,
- lens,
- kmProperty::setReceiverParameterType);
- } else {
- // If the receiver type for the extension property is set already (via getter),
- // make sure it's consistent.
- KmType kmReceiverTypeFromSetter =
- toRenamedKmType(receiverType, receiverSignature, appView, lens);
- if (!getDescriptorFromKmType(kmReceiverType)
- .equals(getDescriptorFromKmType(kmReceiverTypeFromSetter))) {
- return null;
- }
- }
+ if (criteria == GetterSetterCriteria.NOT_AVAILABLE) {
+ if (field != null && IS_VAR.invoke(flags)) {
+ // Editable property without setter.
+ // Even though a setter does not exist, `kotlinc` still set setter flags and use them to
+ // determine when to direct field access v.s. setter calls for property resolution.
+ kmProperty.setSetterFlags(field.accessFlags.getAsKotlinFlags());
}
-
- int valueIndex = isExtension ? 1 : 0;
- DexType valueType = setter.method.proto.parameters.values[valueIndex];
- TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
- if (kmPropertyType == null) {
- // The property type is not set yet.
- kmPropertyType =
- setRenamedKmType(valueType, valueSignature, appView, lens, kmProperty::setReturnType);
- } else {
- // If property type is set already (via either backing field or getter),
- // make sure it's consistent.
- KmType kmPropertyTypeFromSetter =
- toRenamedKmType(valueType, valueSignature, appView, lens);
- if (!getDescriptorFromKmType(kmPropertyType)
- .equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) {
- return null;
- }
- }
- KotlinValueParameterInfo valueParameterInfo =
- setter.getKotlinMemberInfo().getValueParameterInfo(valueIndex);
- KmValueParameter kmValueParameter = toRewrittenKmValueParameter(
- valueParameterInfo, valueType, valueSignature, "value", appView, lens);
- if (kmValueParameter != null) {
- kmProperty.setSetterParameter(kmValueParameter);
- }
- if (appView.appInfo().isPinned(setter.method)) {
- canChangePropertyName = false;
- }
- DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory());
- if (canChangePropertyName
- && renamedSetter.name != setter.method.name
- && renamedPropertyName.equals(name)) {
- renamedPropertyName = renamedSetter.name.toString();
- }
- kmProperty.setSetterFlags(setterFlags);
- JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter));
- } else if (field != null && IS_VAR.invoke(flags)) {
- // Editable property without setter.
- // Even though a setter does not exist, `kotlinc` still set setter flags and use them to
- // determine when to direct field access v.s. setter calls for property resolution.
- kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
+ return kmProperty.getName();
}
-
- // If the property type remains null at the end, bail out to synthesize this property.
- if (kmPropertyType == null) {
- return null;
+ assert criteria == GetterSetterCriteria.MET;
+ assert setter != null;
+ DexProto proto = setter.method.proto;
+ assert proto.parameters.size() == (isExtension ? 2 : 1)
+ : "checkSetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(setter, appView);
+ List<KmTypeParameter> typeParameters =
+ KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ classTypeParameters,
+ signature.getFormalTypeParameters(),
+ appView.dexItemFactory(),
+ kmTypeParameter -> {});
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, synthesizer::toRenamedClassifier);
+ int valueIndex = isExtension ? 1 : 0;
+ DexType valueType = proto.parameters.values[valueIndex];
+ TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
+ KmType kmPropertyType =
+ synthesizer.toRenamedKmType(valueType, valueSignature, setterInfo.returnType, converter);
+ if (kmProperty.getReturnType() == defaultPropertyType) {
+ // The property type is not set yet.
+ kmProperty.setReturnType(kmPropertyType);
+ } else {
+ // If property type is set already make sure it's consistent.
+ if (!getDescriptorFromKmType(kmPropertyType)
+ .equals(getDescriptorFromKmType(kmProperty.getReturnType()))) {
+ return null;
+ }
}
- // For extension property, if the receiver type remains null at the end, bail out too.
- if (isExtension && kmReceiverType == null) {
- return null;
+ assert setter.getKotlinMemberInfo().isPropertyInfo();
+ KotlinValueParameterInfo valueParameterInfo =
+ setter.getKotlinMemberInfo().asPropertyInfo().valueParameterInfo;
+ KmValueParameter kmValueParameter =
+ synthesizer.toRewrittenKmValueParameter(
+ valueParameterInfo, valueType, valueSignature, "value", converter);
+ if (kmValueParameter != null) {
+ kmProperty.setSetterParameter(kmValueParameter);
+ }
+ DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory());
+ kmProperty.setSetterFlags(setterInfo.setterFlags);
+ JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter));
+ return appView.appInfo().isPinned(setter.method) ? null : renamedSetter.name.toString();
+ }
+
+ KmProperty toRenamedKmProperty(KotlinMetadataSynthesizer synthesizer) {
+ AppView<AppInfoWithLiveness> appView = synthesizer.appView;
+ NamingLens lens = synthesizer.lens;
+ KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf());
+
+ // Set default values
+ KmType defaultPropertyType = new KmType(flagsOf());
+ kmProperty.setReturnType(defaultPropertyType);
+
+ String renamedPropertyName = name;
+ if (getter != null) {
+ if (checkGetterCriteria() == GetterSetterCriteria.VIOLATE) {
+ return null;
+ }
+ if (!setReceiverParameterTypeForExtensionProperty(
+ kmProperty, getter, appView, synthesizer)) {
+ return null;
+ }
+ renamedPropertyName =
+ setGetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
+ }
+ if (setter != null) {
+ if (checkSetterCriteria() == GetterSetterCriteria.VIOLATE) {
+ return null;
+ }
+ if (!setReceiverParameterTypeForExtensionProperty(
+ kmProperty, setter, appView, synthesizer)) {
+ return null;
+ }
+ String renamedName =
+ setSetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
+ if (renamedPropertyName != null) {
+ renamedPropertyName = renamedName;
+ }
+ }
+ // Setting the property type from the field has to be done after the getter, otherwise we
+ // may potentially loose type-argument information.
+ if (field != null) {
+ String renamedName =
+ setFieldForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
+ if (renamedPropertyName != null) {
+ renamedPropertyName = renamedName;
+ }
}
// Rename the property name if and only if none of participating members is pinned, and
// any of them is indeed renamed (to a new name).
- if (canChangePropertyName && !renamedPropertyName.equals(name)) {
+ if (renamedPropertyName != null) {
kmProperty.setName(renamedPropertyName);
}
return kmProperty;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
new file mode 100644
index 0000000..9382cd9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
@@ -0,0 +1,174 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import static com.android.tools.r8.kotlin.Kotlin.NAME;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.TypeVariableSignature;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeVisitor;
+import kotlinx.metadata.KmVariance;
+
+class KotlinMetadataSynthesizerUtils {
+
+ static void populateKmTypeFromSignature(
+ FieldTypeSignature typeSignature,
+ Supplier<KmTypeVisitor> typeVisitor,
+ List<KmTypeParameter> allTypeParameters,
+ DexItemFactory factory) {
+ if (typeSignature.isClassTypeSignature()) {
+ populateKmTypeFromClassTypeSignature(
+ typeSignature.asClassTypeSignature(), typeVisitor, allTypeParameters, factory);
+ } else if (typeSignature.isArrayTypeSignature()) {
+ populateKmTypeFromArrayTypeSignature(
+ typeSignature.asArrayTypeSignature(), typeVisitor, allTypeParameters, factory);
+ } else {
+ assert typeSignature.isTypeVariableSignature();
+ populateKmTypeFromTypeVariableSignature(
+ typeSignature.asTypeVariableSignature(), typeVisitor, allTypeParameters);
+ }
+ }
+
+ private static void populateKmTypeFromTypeVariableSignature(
+ TypeVariableSignature typeSignature,
+ Supplier<KmTypeVisitor> visitor,
+ List<KmTypeParameter> allTypeParameters) {
+ for (KmTypeParameter typeParameter : allTypeParameters) {
+ if (typeParameter
+ .getName()
+ .equals(typeSignature.asTypeVariableSignature().getTypeVariable())) {
+ visitor.get().visitTypeParameter(typeParameter.getId());
+ return;
+ }
+ }
+ }
+
+ private static void populateKmTypeFromArrayTypeSignature(
+ ArrayTypeSignature typeSignature,
+ Supplier<KmTypeVisitor> visitor,
+ List<KmTypeParameter> allTypeParameters,
+ DexItemFactory factory) {
+ ArrayTypeSignature arrayTypeSignature = typeSignature.asArrayTypeSignature();
+ if (!arrayTypeSignature.elementSignature().isFieldTypeSignature()) {
+ return;
+ }
+ KmTypeVisitor kmType = visitor.get();
+ kmType.visitClass(NAME + "/Array");
+ populateKmTypeFromSignature(
+ arrayTypeSignature.elementSignature().asFieldTypeSignature(),
+ () -> kmType.visitArgument(flagsOf(), KmVariance.INVARIANT),
+ allTypeParameters,
+ factory);
+ }
+
+ private static void populateKmTypeFromClassTypeSignature(
+ ClassTypeSignature typeSignature,
+ Supplier<KmTypeVisitor> visitor,
+ List<KmTypeParameter> allTypeParameters,
+ DexItemFactory factory) {
+ // No need to record the trivial argument.
+ if (factory.objectType == typeSignature.type()) {
+ return;
+ }
+ KmTypeVisitor kmType = visitor.get();
+ kmType.visitClass(toClassifier(typeSignature.type(), factory));
+ for (FieldTypeSignature typeArgument : typeSignature.typeArguments()) {
+ populateKmTypeFromSignature(
+ typeArgument,
+ () -> kmType.visitArgument(flagsOf(), KmVariance.INVARIANT),
+ allTypeParameters,
+ factory);
+ }
+ }
+
+ static String toClassifier(DexType type, DexItemFactory factory) {
+ // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray
+ if (factory.kotlin.knownTypeConversion.containsKey(type)) {
+ DexType convertedType = factory.kotlin.knownTypeConversion.get(type);
+ assert convertedType != null;
+ return descriptorToKotlinClassifier(convertedType.toDescriptorString());
+ }
+ // E.g., [Ljava/lang/String; -> kotlin/Array
+ if (type.isArrayType()) {
+ return NAME + "/Array";
+ }
+ return descriptorToKotlinClassifier(type.toDescriptorString());
+ }
+
+ /**
+ * Utility method building up all type-parameters from {@code classTypeParameters} combined with
+ * {@code parameters}, where the consumer {@code addedFromParameters} is called for every
+ * conversion of {@Code FormalTypeParameter} to {@Code KmTypeParameter}.
+ *
+ * <pre>
+ * classTypeParameters: [KmTypeParameter(T), KmTypeParameter(S)]
+ * parameters: [FormalTypeParameter(R)]
+ * result: [KmTypeParameter(T), KmTypeParameter(S), KmTypeParameter(R)].
+ * </pre>
+ *
+ * @param classTypeParameters
+ * @param parameters
+ * @param factory
+ * @param addedFromParameters
+ * @return
+ */
+ static List<KmTypeParameter> convertFormalTypeParameters(
+ List<KmTypeParameter> classTypeParameters,
+ List<FormalTypeParameter> parameters,
+ DexItemFactory factory,
+ Consumer<KmTypeParameter> addedFromParameters) {
+ if (parameters.isEmpty()) {
+ return classTypeParameters;
+ }
+ ImmutableList.Builder<KmTypeParameter> builder = ImmutableList.builder();
+ builder.addAll(classTypeParameters);
+ int idCounter = classTypeParameters.size();
+ // Assign type-variables ids to names. All generic signatures has been minified at this point,
+ // but it may be that type-variables are used before we can see them (is that allowed?).
+ for (FormalTypeParameter parameter : parameters) {
+ KmTypeParameter element =
+ new KmTypeParameter(flagsOf(), parameter.getName(), idCounter++, KmVariance.INVARIANT);
+ builder.add(element);
+ addedFromParameters.accept(element);
+ }
+ idCounter = 0;
+ ImmutableList<KmTypeParameter> allTypeParameters = builder.build();
+ for (FormalTypeParameter parameter : parameters) {
+ KmTypeParameter kmTypeParameter =
+ allTypeParameters.get(classTypeParameters.size() + idCounter++);
+ visitUpperBound(parameter.getClassBound(), allTypeParameters, kmTypeParameter, factory);
+ if (parameter.getInterfaceBounds() != null) {
+ for (FieldTypeSignature interfaceBound : parameter.getInterfaceBounds()) {
+ visitUpperBound(interfaceBound, allTypeParameters, kmTypeParameter, factory);
+ }
+ }
+ }
+ return allTypeParameters;
+ }
+
+ private static void visitUpperBound(
+ FieldTypeSignature typeSignature,
+ List<KmTypeParameter> allTypeParameters,
+ KmTypeParameter parameter,
+ DexItemFactory factory) {
+ if (typeSignature.isUnknown()) {
+ return;
+ }
+ populateKmTypeFromSignature(
+ typeSignature, () -> parameter.visitUpperBound(flagsOf()), allTypeParameters, factory);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
new file mode 100644
index 0000000..130a660
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import kotlinx.metadata.KmClassifier;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeProjection;
+
+// Provides access to Kotlin information about a kotlin type.
+public class KotlinTypeInfo {
+
+ static final List<KotlinTypeProjectionInfo> EMPTY_ARGUMENTS = ImmutableList.of();
+
+ private final KmClassifier classifier;
+ private final List<KotlinTypeProjectionInfo> arguments;
+
+ private KotlinTypeInfo(KmClassifier classifier, List<KotlinTypeProjectionInfo> arguments) {
+ this.classifier = classifier;
+ this.arguments = arguments;
+ }
+
+ static KotlinTypeInfo create(KmType kmType) {
+ if (kmType == null) {
+ return null;
+ }
+ if (kmType.getArguments().isEmpty()) {
+ return new KotlinTypeInfo(kmType.classifier, EMPTY_ARGUMENTS);
+ }
+ ImmutableList.Builder<KotlinTypeProjectionInfo> arguments = new ImmutableList.Builder<>();
+ for (KmTypeProjection argument : kmType.getArguments()) {
+ arguments.add(KotlinTypeProjectionInfo.create(argument));
+ }
+ return new KotlinTypeInfo(kmType.classifier, arguments.build());
+ }
+
+ public boolean isTypeAlias() {
+ return classifier instanceof KmClassifier.TypeAlias;
+ }
+
+ public KmClassifier.TypeAlias asTypeAlias() {
+ return (KmClassifier.TypeAlias) classifier;
+ }
+
+ public List<KotlinTypeProjectionInfo> getArguments() {
+ return arguments;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
new file mode 100644
index 0000000..3782bd8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import kotlinx.metadata.KmTypeProjection;
+import kotlinx.metadata.KmVariance;
+
+// Provides access to Kotlin information about the type projection of a type (arguments).
+public class KotlinTypeProjectionInfo {
+
+ final KmVariance variance;
+ final KotlinTypeInfo typeInfo;
+
+ private KotlinTypeProjectionInfo(KmVariance variance, KotlinTypeInfo typeInfo) {
+ this.variance = variance;
+ this.typeInfo = typeInfo;
+ }
+
+ static KotlinTypeProjectionInfo create(KmTypeProjection kmTypeProjection) {
+ return new KotlinTypeProjectionInfo(
+ kmTypeProjection.getVariance(), KotlinTypeInfo.create(kmTypeProjection.getType()));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index 3134b8a..e358c50 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -19,24 +19,36 @@
final int flag;
// Indicates whether the formal parameter is originally `vararg`.
final boolean isVararg;
+ // Original information about the type.
+ final KotlinTypeInfo type;
+
// TODO(b/151194869): Should we treat them as normal annotations? E.g., shrinking and renaming?
// Annotations on the type of value parameter.
final List<KmAnnotation> annotations;
private KotlinValueParameterInfo(
- String name, int flag, boolean isVararg, List<KmAnnotation> annotations) {
+ String name,
+ int flag,
+ boolean isVararg,
+ KotlinTypeInfo type,
+ List<KmAnnotation> annotations) {
this.name = name;
this.flag = flag;
this.isVararg = isVararg;
+ this.type = type;
this.annotations = annotations;
}
static KotlinValueParameterInfo fromKmValueParameter(KmValueParameter kmValueParameter) {
+ if (kmValueParameter == null) {
+ return null;
+ }
KmType kmType = kmValueParameter.getType();
return new KotlinValueParameterInfo(
kmValueParameter.getName(),
kmValueParameter.getFlags(),
kmValueParameter.getVarargElementType() != null,
+ KotlinTypeInfo.create(kmType),
kmType != null ? JvmExtensionsKt.getAnnotations(kmType) : ImmutableList.of());
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index b178579..5506792 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -273,7 +273,7 @@
return;
}
assert definition.field != field;
- assert definition.field.holder != field.holder;
+ assert definition.holder() != field.holder;
// If the definition is renamed,
if (renaming.containsKey(definition.field)) {
// Assign the same, renamed name as the definition to the reference.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
index 9286684..6652d4a 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -52,7 +52,7 @@
public DexString getOrCreateNameFor(DexField field) {
DexEncodedField encodedField = appView.appInfo().resolveField(field);
if (encodedField != null) {
- DexClass clazz = appView.definitionFor(encodedField.field.holder);
+ DexClass clazz = appView.definitionFor(encodedField.holder());
if (clazz == null) {
return field.name;
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index d5873a9..332477a 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -148,13 +148,13 @@
}
Value in = instruction.value();
if (!in.isConstString()) {
- warnUndeterminedIdentifierIfNecessary(field, method.method.holder, instruction, null);
+ warnUndeterminedIdentifierIfNecessary(field, method.holder(), instruction, null);
return iterator;
}
DexString original = in.getConstInstruction().asConstString().getValue();
DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original);
if (itemBasedString == null) {
- warnUndeterminedIdentifierIfNecessary(field, method.method.holder, instruction, original);
+ warnUndeterminedIdentifierIfNecessary(field, method.holder(), instruction, original);
return iterator;
}
// Move the cursor back to $fieldPut
@@ -213,7 +213,7 @@
if (isReflectionMethod(appView.dexItemFactory(), invokedMethod) || isClassNameComparison) {
DexReference itemBasedString = identifyIdentifier(invoke, appView);
if (itemBasedString == null) {
- DexType context = method.method.holder;
+ DexType context = method.holder();
warnUndeterminedIdentifierIfNecessary(invokedMethod, context, invoke, null);
return iterator;
}
@@ -280,14 +280,13 @@
for (int i = 0; i < ins.size(); i++) {
Value in = ins.get(i);
if (!in.isConstString()) {
- warnUndeterminedIdentifierIfNecessary(invokedMethod, method.method.holder, invoke, null);
+ warnUndeterminedIdentifierIfNecessary(invokedMethod, method.holder(), invoke, null);
continue;
}
DexString original = in.getConstInstruction().asConstString().getValue();
DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original);
if (itemBasedString == null) {
- warnUndeterminedIdentifierIfNecessary(
- invokedMethod, method.method.holder, invoke, original);
+ warnUndeterminedIdentifierIfNecessary(invokedMethod, method.holder(), invoke, original);
continue;
}
// Move the cursor back to $invoke
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index da38ed4..d8d62ae 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptorIfValidJavaType;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
@@ -27,6 +28,7 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
@@ -282,13 +284,13 @@
}
static DexReference inferMemberOrTypeFromNameString(
- DexDefinitionSupplier definitions, DexString dexString) {
+ AppView<AppInfoWithLiveness> appView, DexString dexString) {
// "fully.qualified.ClassName.fieldOrMethodName"
// "fully.qualified.ClassName#fieldOrMethodName"
- DexReference itemBasedString = inferMemberFromNameString(definitions, dexString);
+ DexReference itemBasedString = inferMemberFromNameString(appView, dexString);
if (itemBasedString == null) {
// "fully.qualified.ClassName"
- return inferTypeFromNameString(definitions, dexString);
+ return inferTypeFromNameString(appView, dexString);
}
return itemBasedString;
}
@@ -320,7 +322,7 @@
}
private static DexReference inferMemberFromNameString(
- DexDefinitionSupplier definitions, DexString dexString) {
+ AppView<AppInfoWithLiveness> appView, DexString dexString) {
String identifier = dexString.toString();
String typeIdentifier = null;
String memberIdentifier = null;
@@ -348,8 +350,9 @@
if (maybeDescriptor == null) {
return null;
}
- DexType type = definitions.dexItemFactory().createType(maybeDescriptor);
- DexClass holder = definitions.definitionFor(type);
+ DexType type = appView.dexItemFactory().createType(maybeDescriptor);
+ // TODO(b/150736225): Should we move the identification of identifiers into the initial tracing?
+ DexClass holder = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (holder == null) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index 777d006..6c78e0b 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -9,15 +9,13 @@
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -28,9 +26,9 @@
// Naming lens for rewriting type prefixes.
public class PrefixRewritingNamingLens extends NamingLens {
- final Map<DexType, DexString> classRenaming = new IdentityHashMap<>();
final NamingLens namingLens;
final InternalOptions options;
+ final AppView<?> appView;
public static NamingLens createPrefixRewritingNamingLens(AppView<?> appView) {
return createPrefixRewritingNamingLens(appView, NamingLens.getIdentityLens());
@@ -45,18 +43,21 @@
}
public PrefixRewritingNamingLens(NamingLens namingLens, AppView<?> appView) {
+ this.appView = appView;
this.namingLens = namingLens;
this.options = appView.options();
- DexItemFactory itemFactory = options.itemFactory;
- PrefixRewritingMapper rewritePrefix = appView.rewritePrefix;
- itemFactory.forAllTypes(
- type -> {
- if (rewritePrefix.hasRewrittenType(type, appView)) {
- classRenaming.put(type, rewritePrefix.rewrittenType(type, appView).descriptor);
- }
- });
- // Verify that no type would have been renamed by both lenses.
- assert namingLens.verifyNoOverlap(classRenaming);
+ }
+
+ private boolean isRenamed(DexType type) {
+ return getRenaming(type) != null;
+ }
+
+ private DexString getRenaming(DexType type) {
+ DexString descriptor = null;
+ if (appView.rewritePrefix.hasRewrittenType(type, appView)) {
+ descriptor = appView.rewritePrefix.rewrittenType(type, appView).descriptor;
+ }
+ return descriptor;
}
@Override
@@ -66,17 +67,18 @@
@Override
public DexString prefixRewrittenType(DexType type) {
- return classRenaming.get(type);
+ return getRenaming(type);
}
@Override
public DexString lookupDescriptor(DexType type) {
- return classRenaming.getOrDefault(type, namingLens.lookupDescriptor(type));
+ DexString renaming = getRenaming(type);
+ return renaming != null ? renaming : namingLens.lookupDescriptor(type);
}
@Override
public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
- if (classRenaming.containsKey(attribute.getInner())) {
+ if (isRenamed(attribute.getInner())) {
// Prefix rewriting does not influence the inner name.
return attribute.getInnerName();
}
@@ -85,7 +87,7 @@
@Override
public DexString lookupName(DexMethod method) {
- if (classRenaming.containsKey(method.holder)) {
+ if (isRenamed(method.holder)) {
// Prefix rewriting does not influence the method name.
return method.name;
}
@@ -94,7 +96,7 @@
@Override
public DexString lookupMethodName(DexCallSite callSite) {
- if (classRenaming.containsKey(callSite.bootstrapMethod.rewrittenTarget.holder)) {
+ if (isRenamed(callSite.bootstrapMethod.rewrittenTarget.holder)) {
// Prefix rewriting does not influence the inner name.
return callSite.methodName;
}
@@ -103,7 +105,7 @@
@Override
public DexString lookupName(DexField field) {
- if (classRenaming.containsKey(field.holder)) {
+ if (isRenamed(field.holder)) {
// Prefix rewriting does not influence the field name.
return field.name;
}
@@ -127,9 +129,10 @@
}
private boolean verifyNotPrefixRewrittenPackage(String packageName) {
- for (DexType dexType : classRenaming.keySet()) {
- assert !dexType.getPackageDescriptor().equals(packageName);
- }
+ appView.rewritePrefix.forAllRewrittenTypes(
+ dexType -> {
+ assert !dexType.getPackageDescriptor().equals(packageName);
+ });
return true;
}
@@ -140,7 +143,7 @@
// If compiling the desugared library, the mapping needs to be printed.
// When debugging the program, both mapping files need to be merged.
if (options.isDesugaredLibraryCompilation()) {
- classRenaming.keySet().forEach(consumer);
+ appView.rewritePrefix.forAllRewrittenTypes(consumer);
}
namingLens.forAllRenamedTypes(consumer);
}
@@ -150,13 +153,16 @@
Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
Map<String, T> renamedItemsPrefixRewritting;
if (clazz == DexType.class) {
- renamedItemsPrefixRewritting =
- classRenaming.keySet().stream()
- .filter(item -> predicate.test(clazz.cast(item)))
- .map(clazz::cast)
- .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+ renamedItemsPrefixRewritting = new HashMap<>();
+ appView.rewritePrefix.forAllRewrittenTypes(
+ item -> {
+ T cast = clazz.cast(item);
+ if (predicate.test(cast)) {
+ renamedItemsPrefixRewritting.put(namer.apply(cast), cast);
+ }
+ });
} else {
- renamedItemsPrefixRewritting = ImmutableMap.of();
+ renamedItemsPrefixRewritting = Collections.emptyMap();
}
Map<String, T> renamedItemsMinifier = namingLens.getRenamedItems(clazz, predicate, namer);
// The Collector throws an exception for duplicated keys.
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 9f32739..da62097 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -340,7 +340,8 @@
// have no effect.
continue;
}
- DexClass dexClass = appView.definitionFor(type);
+ // TODO(b/150736225): Is this sound? What if the type is a library type that has been pruned?
+ DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (dexClass == null) {
computeDefaultInterfaceMethodMappingsForType(
type,
@@ -411,7 +412,7 @@
// TODO(b/136694827): Check for already used and report an error. It seems like this can be
// done already in the reservation step for classes since there is only one 'path', unlike
// members that can be reserved differently in the hierarchy.
- DexClass clazz = appView.definitionFor(type);
+ DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (clazz == null) {
return type.descriptor;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 4d20f6b..42e03e0 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -142,7 +142,7 @@
// TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow
// searching in library for methods, but this should be done on classpath instead.
if (target != null && target.method != method) {
- DexClass targetClass = appView.definitionFor(target.method.holder);
+ DexClass targetClass = appView.definitionFor(target.holder());
if (originalClass.isProgramClass()) {
// In Java bytecode, it is only possible to target interface methods that are in one of
// the immediate super-interfaces via a super-invocation (see IndirectSuperInterfaceTest).
@@ -159,14 +159,14 @@
// visibility problems when rebinding.
final DexEncodedMethod finalTarget = target;
Set<DexEncodedMethod> contexts = methodsWithContexts.get(method);
- if (contexts.stream().anyMatch(context ->
- mayNeedBridgeForVisibility(context.method.holder, finalTarget))) {
+ if (contexts.stream()
+ .anyMatch(context -> mayNeedBridgeForVisibility(context.holder(), finalTarget))) {
target =
insertBridgeForVisibilityIfNeeded(
method, target, originalClass, targetClass, lookupTarget);
}
}
- builder.map(method, lense.lookupMethod(validTargetFor(target.method, method)));
+ builder.map(method, lense.lookupMethod(validTargetFor(target.method, method)), invokeType);
}
}
}
@@ -217,7 +217,7 @@
}
private boolean mayNeedBridgeForVisibility(DexType context, DexEncodedMethod method) {
- DexType holderType = method.method.holder;
+ DexType holderType = method.holder();
DexClass holder = appView.definitionFor(holderType);
if (holder == null) {
return false;
@@ -315,7 +315,7 @@
.allMatch(
context ->
isMemberVisibleFromOriginalContext(
- appView, context.method.holder, target.field.holder, target.accessFlags))) {
+ appView, context.holder(), target.holder(), target.accessFlags))) {
builder.map(
field, lense.lookupField(validTargetFor(target.field, field, DexClass::lookupField)));
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
index a845de2..95e2772 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -9,45 +9,72 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Set;
public class MemberRebindingLense extends NestedGraphLense {
- public static class Builder extends NestedGraphLense.Builder {
+ public static class Builder {
private final AppView<?> appView;
+ private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
+ private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>();
+
protected Builder(AppView<?> appView) {
this.appView = appView;
}
+ public void map(DexField from, DexField to) {
+ if (from == to) {
+ assert !fieldMap.containsKey(from);
+ return;
+ }
+ fieldMap.put(from, to);
+ }
+
+ public void map(DexMethod from, DexMethod to, Invoke.Type type) {
+ if (from == to) {
+ assert !methodMaps.containsKey(type) || methodMaps.get(type).getOrDefault(from, to) == to;
+ return;
+ }
+ Map<DexMethod, DexMethod> methodMap =
+ methodMaps.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
+ assert methodMap.getOrDefault(from, to) == to;
+ methodMap.put(from, to);
+ }
+
public GraphLense build(GraphLense previousLense) {
- assert typeMap.isEmpty();
- if (methodMap.isEmpty() && fieldMap.isEmpty()) {
+ if (fieldMap.isEmpty() && methodMaps.isEmpty()) {
return previousLense;
}
- return new MemberRebindingLense(appView, methodMap, fieldMap, previousLense);
+ return new MemberRebindingLense(appView, methodMaps, fieldMap, previousLense);
}
}
private final AppView<?> appView;
+ private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps;
public MemberRebindingLense(
AppView<?> appView,
- Map<DexMethod, DexMethod> methodMap,
+ Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps,
Map<DexField, DexField> fieldMap,
GraphLense previousLense) {
super(
ImmutableMap.of(),
- methodMap,
+ ImmutableMap.of(),
fieldMap,
null,
null,
previousLense,
appView.dexItemFactory());
this.appView = appView;
+ this.methodMaps = methodMaps;
}
public static Builder builder(AppView<?> appView) {
@@ -55,6 +82,28 @@
}
@Override
+ public boolean isLegitimateToHaveEmptyMappings() {
+ return true;
+ }
+
+ @Override
+ public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+ GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
+ Map<DexMethod, DexMethod> methodMap = methodMaps.getOrDefault(type, Collections.emptyMap());
+ DexMethod newMethod = methodMap.get(previous.getMethod());
+ if (newMethod != null) {
+ return new GraphLenseLookupResult(
+ newMethod, mapInvocationType(newMethod, method, previous.getType()));
+ }
+ return previous;
+ }
+
+ @Override
+ public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) {
+ return previousLense.lookupMethodInAllContexts(method);
+ }
+
+ @Override
protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 6bf54dd..4bb1b2b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -31,20 +30,18 @@
private final Set<DexAnnotation> annotationsToRetain;
private final Set<DexType> classesToRetainInnerClassAttributeFor;
private final ProguardKeepAttributes keep;
-
- public AnnotationRemover(
- AppView<AppInfoWithLiveness> appView, Set<DexType> classesToRetainInnerClassAttributeFor) {
- this(appView, classesToRetainInnerClassAttributeFor, ImmutableSet.of());
- }
+ private final Set<DexType> removedClasses;
private AnnotationRemover(
AppView<AppInfoWithLiveness> appView,
Set<DexType> classesToRetainInnerClassAttributeFor,
- Set<DexAnnotation> annotationsToRetain) {
+ Set<DexAnnotation> annotationsToRetain,
+ Set<DexType> removedClasses) {
this.appView = appView;
this.annotationsToRetain = annotationsToRetain;
this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
this.keep = appView.options().getProguardConfiguration().getKeepAttributes();
+ this.removedClasses = removedClasses;
}
public static Builder builder() {
@@ -230,12 +227,15 @@
private DexEncodedAnnotation rewriteEncodedAnnotation(DexEncodedAnnotation original) {
GraphLense graphLense = appView.graphLense();
DexType annotationType = original.type.toBaseType(appView.dexItemFactory());
+ if (removedClasses.contains(annotationType)) {
+ return null;
+ }
DexType rewrittenType = graphLense.lookupType(annotationType);
DexEncodedAnnotation rewrite =
original.rewrite(
graphLense::lookupType, element -> rewriteAnnotationElement(rewrittenType, element));
assert rewrite != null;
- DexClass annotationClass = appView.definitionFor(rewrittenType);
+ DexClass annotationClass = appView.appInfo().definitionFor(rewrittenType);
assert annotationClass == null
|| appView.appInfo().isNonProgramTypeOrLiveProgramType(rewrittenType);
return rewrite;
@@ -378,8 +378,7 @@
}
for (DexProgramClass clazz : appView.appInfo().classes()) {
// If [clazz] is mentioned by a keep rule, it could be used for reflection, and we
- // therefore
- // need to keep the enclosing method and inner classes attributes, if requested.
+ // therefore need to keep the enclosing method and inner classes attributes, if requested.
if (appView.appInfo().isPinned(clazz.type)) {
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
DexType inner = innerClassAttribute.getInner();
@@ -413,10 +412,11 @@
annotationsToRetain.add(annotation);
}
- public AnnotationRemover build(AppView<AppInfoWithLiveness> appView) {
+ public AnnotationRemover build(
+ AppView<AppInfoWithLiveness> appView, Set<DexType> removedClasses) {
assert classesToRetainInnerClassAttributeFor != null;
return new AnnotationRemover(
- appView, classesToRetainInnerClassAttributeFor, annotationsToRetain);
+ appView, classesToRetainInnerClassAttributeFor, annotationsToRetain, removedClasses);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index fdd1274..78136a9 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -35,15 +35,16 @@
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.PredicateSet;
-import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableSet;
@@ -56,7 +57,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -70,7 +70,8 @@
/** Encapsulates liveness and reachability information for an application. */
public class AppInfoWithLiveness extends AppInfoWithSubtyping implements InstantiatedSubTypeInfo {
-
+ /** Set of reachable proto types that will be dead code eliminated. */
+ private final Set<DexType> deadProtoTypes;
/** Set of types that are mentioned in the program, but for which no definition exists. */
private final Set<DexType> missingTypes;
/**
@@ -83,9 +84,6 @@
* ServiceLoader.load() or ServiceLoader.loadInstalled().
*/
public final Set<DexType> instantiatedAppServices;
- /** Cache for {@link #isInstantiatedDirectlyOrIndirectly(DexProgramClass)}. */
- private final IdentityHashMap<DexType, Boolean> indirectlyInstantiatedTypes =
- new IdentityHashMap<>();
/**
* Set of methods that are the immediate target of an invoke. They might not actually be live but
* are required so that invokes can find the method. If such a method is not live (i.e. not
@@ -188,14 +186,13 @@
/** A map from enum types to their value types and ordinals. */
final EnumValueInfoMapCollection enumValueInfoMaps;
- final Set<DexType> instantiatedLambdas;
-
/* A cache to improve the lookup performance of lookupSingleVirtualTarget */
private final SingleTargetLookupCache singleTargetLookupCache = new SingleTargetLookupCache();
// TODO(zerny): Clean up the constructors so we have just one.
AppInfoWithLiveness(
DirectMappedDexApplication application,
+ Set<DexType> deadProtoTypes,
Set<DexType> missingTypes,
Set<DexType> liveTypes,
Set<DexType> instantiatedAppServices,
@@ -233,10 +230,10 @@
Set<DexType> prunedTypes,
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
EnumValueInfoMapCollection enumValueInfoMaps,
- Set<DexType> instantiatedLambdas,
Set<DexType> constClassReferences,
Map<DexType, Visibility> initClassReferences) {
super(application);
+ this.deadProtoTypes = deadProtoTypes;
this.missingTypes = missingTypes;
this.liveTypes = liveTypes;
this.instantiatedAppServices = instantiatedAppServices;
@@ -274,13 +271,13 @@
this.prunedTypes = prunedTypes;
this.switchMaps = switchMaps;
this.enumValueInfoMaps = enumValueInfoMaps;
- this.instantiatedLambdas = instantiatedLambdas;
this.constClassReferences = constClassReferences;
this.initClassReferences = initClassReferences;
}
public AppInfoWithLiveness(
AppInfoWithSubtyping appInfoWithSubtyping,
+ Set<DexType> deadProtoTypes,
Set<DexType> missingTypes,
Set<DexType> liveTypes,
Set<DexType> instantiatedAppServices,
@@ -318,10 +315,10 @@
Set<DexType> prunedTypes,
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
EnumValueInfoMapCollection enumValueInfoMaps,
- Set<DexType> instantiatedLambdas,
Set<DexType> constClassReferences,
Map<DexType, Visibility> initClassReferences) {
super(appInfoWithSubtyping);
+ this.deadProtoTypes = deadProtoTypes;
this.missingTypes = missingTypes;
this.liveTypes = liveTypes;
this.instantiatedAppServices = instantiatedAppServices;
@@ -359,7 +356,6 @@
this.prunedTypes = prunedTypes;
this.switchMaps = switchMaps;
this.enumValueInfoMaps = enumValueInfoMaps;
- this.instantiatedLambdas = instantiatedLambdas;
this.constClassReferences = constClassReferences;
this.initClassReferences = initClassReferences;
}
@@ -367,6 +363,7 @@
private AppInfoWithLiveness(AppInfoWithLiveness previous) {
this(
previous,
+ previous.deadProtoTypes,
previous.missingTypes,
previous.liveTypes,
previous.instantiatedAppServices,
@@ -404,7 +401,6 @@
previous.prunedTypes,
previous.switchMaps,
previous.enumValueInfoMaps,
- previous.instantiatedLambdas,
previous.constClassReferences,
previous.initClassReferences);
copyMetadataFromPrevious(previous);
@@ -417,6 +413,7 @@
Collection<DexReference> additionalPinnedItems) {
this(
application,
+ previous.deadProtoTypes,
previous.missingTypes,
previous.liveTypes,
previous.instantiatedAppServices,
@@ -458,7 +455,6 @@
: CollectionUtils.mergeSets(previous.prunedTypes, removedClasses),
previous.switchMaps,
previous.enumValueInfoMaps,
- previous.instantiatedLambdas,
previous.constClassReferences,
previous.initClassReferences);
copyMetadataFromPrevious(previous);
@@ -470,10 +466,10 @@
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
EnumValueInfoMapCollection enumValueInfoMaps) {
super(previous);
+ this.deadProtoTypes = previous.deadProtoTypes;
this.missingTypes = previous.missingTypes;
this.liveTypes = previous.liveTypes;
this.instantiatedAppServices = previous.instantiatedAppServices;
- this.instantiatedLambdas = previous.instantiatedLambdas;
this.targetedMethods = previous.targetedMethods;
this.failedResolutionTargets = previous.failedResolutionTargets;
this.bootstrapMethods = previous.bootstrapMethods;
@@ -513,21 +509,39 @@
previous.markObsolete();
}
- // TODO(b/150736225): Don't disable this assert.
- private boolean dontAssertDefinitionFor = true;
-
public static AppInfoWithLivenessModifier modifier() {
return new AppInfoWithLivenessModifier();
}
+ private boolean assertDefinitionFor = true;
+
+ public void disableDefinitionForAssert() {
+ assertDefinitionFor = false;
+ }
+
+ public void enableDefinitionForAssert() {
+ assertDefinitionFor = true;
+ }
+
@Override
public DexClass definitionFor(DexType type) {
DexClass definition = super.definitionFor(type);
- assert dontAssertDefinitionFor
- || definition != null
- || missingTypes.contains(type)
- // TODO(b/149363884): Remove this exception once fixed.
- || type.toDescriptorString().endsWith("$Builder;")
+ assert !assertDefinitionFor
+ || definition != null
+ || deadProtoTypes.contains(type)
+ || missingTypes.contains(type)
+ // TODO(b/150693139): Remove these exceptions once fixed.
+ || InterfaceMethodRewriter.isCompanionClassType(type)
+ || InterfaceMethodRewriter.hasDispatchClassSuffix(type)
+ || InterfaceMethodRewriter.isEmulatedLibraryClassType(type)
+ || type.toDescriptorString().startsWith("L$r8$backportedMethods$")
+ || type.toDescriptorString().startsWith("Lj$/$r8$backportedMethods$")
+ || type.toDescriptorString().startsWith("Lj$/$r8$retargetLibraryMember$")
+ || TwrCloseResourceRewriter.isUtilityClassDescriptor(type)
+ // TODO(b/150736225): Not sure how to remove these.
+ || DesugaredLibraryAPIConverter.isVivifiedType(type)
+ // TODO(b/149363884): Handle references to dead proto builders.
+ || type.toDescriptorString().endsWith("$Builder;")
: "Failed lookup of non-missing type: " + type;
return definition;
}
@@ -561,7 +575,7 @@
}
for (DexCallSite callSite : callSites) {
for (DexEncodedMethod method : lookupLambdaImplementedMethods(callSite)) {
- worklist.add(method.method.holder);
+ worklist.add(method.holder());
}
}
while (!worklist.isEmpty()) {
@@ -735,8 +749,9 @@
return objectAllocationInfoCollection;
}
- ObjectAllocationInfoCollectionImpl getMutableObjectAllocationInfoCollection() {
- return objectAllocationInfoCollection;
+ void mutateObjectAllocationInfoCollection(
+ Consumer<ObjectAllocationInfoCollectionImpl.Builder> mutator) {
+ objectAllocationInfoCollection.mutate(mutator, this);
}
void removeFromSingleTargetLookupCache(DexClass clazz) {
@@ -764,30 +779,14 @@
assert checkIfObsolete();
DexType type = clazz.type;
return type.isD8R8SynthesizedClassType()
- || objectAllocationInfoCollection.isInstantiatedDirectly(clazz)
+ || (!clazz.isInterface() && objectAllocationInfoCollection.isInstantiatedDirectly(clazz))
+ // TODO(b/145344105): Model annotations in the object allocation info.
|| (clazz.isAnnotation() && liveTypes.contains(type));
}
public boolean isInstantiatedIndirectly(DexProgramClass clazz) {
assert checkIfObsolete();
- if (hasAnyInstantiatedLambdas(clazz)) {
- return true;
- }
- DexType type = clazz.type;
- synchronized (indirectlyInstantiatedTypes) {
- if (indirectlyInstantiatedTypes.containsKey(type)) {
- return indirectlyInstantiatedTypes.get(type).booleanValue();
- }
- for (DexType directSubtype : allImmediateSubtypes(type)) {
- DexProgramClass directSubClass = asProgramClassOrNull(definitionFor(directSubtype));
- if (directSubClass == null || isInstantiatedDirectlyOrIndirectly(directSubClass)) {
- indirectlyInstantiatedTypes.put(type, Boolean.TRUE);
- return true;
- }
- }
- indirectlyInstantiatedTypes.put(type, Boolean.FALSE);
- return false;
- }
+ return objectAllocationInfoCollection.hasInstantiatedStrictSubtype(clazz);
}
public boolean isInstantiatedDirectlyOrIndirectly(DexProgramClass clazz) {
@@ -855,16 +854,16 @@
if (fieldAccessInfo == null || !fieldAccessInfo.isWritten()) {
return false;
}
- DexType holder = field.field.holder;
+ DexType holder = field.holder();
return fieldAccessInfo.isWrittenOnlyInMethodSatisfying(
- method -> method.isInstanceInitializer() && method.method.holder == holder);
+ method -> method.isInstanceInitializer() && method.holder() == holder);
}
public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
DexEncodedMethod staticInitializer =
- definitionFor(field.field.holder).asProgramClass().getClassInitializer();
+ definitionFor(field.holder()).asProgramClass().getClassInitializer();
return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
}
@@ -876,7 +875,7 @@
}
private boolean isLibraryOrClasspathField(DexEncodedField field) {
- DexClass holder = definitionFor(field.field.holder);
+ DexClass holder = definitionFor(field.holder());
return holder == null || holder.isLibraryClass() || holder.isClasspathClass();
}
@@ -905,9 +904,9 @@
}
@Override
- public boolean hasAnyInstantiatedLambdas(DexProgramClass clazz) {
+ public boolean isInstantiatedInterface(DexProgramClass clazz) {
assert checkIfObsolete();
- return instantiatedLambdas.contains(clazz.type);
+ return objectAllocationInfoCollection.isInterfaceWithUnknownSubtypeHierarchy(clazz);
}
@Override
@@ -940,53 +939,6 @@
return false;
}
- private boolean canVirtualMethodBeImplementedInExtraSubclass(
- DexProgramClass clazz, DexMethod method) {
- // For functional interfaces that are instantiated by lambdas, we may not have synthesized all
- // the lambda classes yet, and therefore the set of subtypes for the holder may still be
- // incomplete.
- if (hasAnyInstantiatedLambdas(clazz)) {
- return true;
- }
- // If `clazz` is kept and `method` is a library method or a library method override, then it is
- // possible to create a class that inherits from `clazz` and overrides the library method.
- // Similarly, if `clazz` is kept and `method` is kept directly on `clazz` or indirectly on one
- // of its supertypes, then it is possible to create a class that inherits from `clazz` and
- // overrides the kept method.
- if (isPinned(clazz.type)) {
- ResolutionResult resolutionResult = resolveMethod(clazz, method);
- if (resolutionResult.isSingleResolution()) {
- DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- return !resolutionTarget.isProgramMethod(this)
- || resolutionTarget.isLibraryMethodOverride().isPossiblyTrue()
- || isVirtualMethodPinnedDirectlyOrInAncestor(clazz, method);
- }
- }
- return false;
- }
-
- private boolean isVirtualMethodPinnedDirectlyOrInAncestor(
- DexProgramClass currentClass, DexMethod method) {
- // Look in all ancestor types, including `currentClass` itself.
- Set<DexProgramClass> visited = SetUtils.newIdentityHashSet(currentClass);
- Deque<DexProgramClass> worklist = new ArrayDeque<>(visited);
- while (!worklist.isEmpty()) {
- DexClass clazz = worklist.removeFirst();
- assert visited.contains(clazz);
- DexEncodedMethod methodInClass = clazz.lookupVirtualMethod(method);
- if (methodInClass != null && isPinned(methodInClass.method)) {
- return true;
- }
- for (DexType superType : clazz.allImmediateSupertypes()) {
- DexProgramClass superClass = asProgramClassOrNull(definitionFor(superType));
- if (superClass != null && visited.add(superClass)) {
- worklist.addLast(superClass);
- }
- }
- }
- return false;
- }
-
public Set<DexReference> getPinnedItems() {
assert checkIfObsolete();
return pinnedItems;
@@ -1001,6 +953,10 @@
Collection<DexType> removedClasses,
Collection<DexReference> additionalPinnedItems) {
assert checkIfObsolete();
+ if (!removedClasses.isEmpty()) {
+ // Rebuild the hierarchy.
+ objectAllocationInfoCollection.mutate(mutator -> {}, this);
+ }
return new AppInfoWithLiveness(this, application, removedClasses, additionalPinnedItems);
}
@@ -1037,6 +993,7 @@
return new AppInfoWithLiveness(
application,
+ deadProtoTypes,
missingTypes,
rewriteItems(liveTypes, lens::lookupType),
rewriteItems(instantiatedAppServices, lens::lookupType),
@@ -1079,7 +1036,6 @@
prunedTypes,
rewriteReferenceKeys(switchMaps, lens::lookupField),
enumValueInfoMaps.rewrittenWithLens(lens),
- rewriteItems(instantiatedLambdas, lens::lookupType),
rewriteItems(constClassReferences, lens::lookupType),
rewriteReferenceKeys(initClassReferences, lens::lookupType));
}
@@ -1208,7 +1164,7 @@
// TODO(b/148769279): Disable lookup single target on lambda's for now.
if (resolvedHolder.isInterface()
&& resolvedHolder.isProgramClass()
- && hasAnyInstantiatedLambdas(resolvedHolder.asProgramClass())) {
+ && isInstantiatedInterface(resolvedHolder.asProgramClass())) {
singleTargetLookupCache.addToCache(refinedReceiverType, method, null);
return null;
}
@@ -1387,9 +1343,7 @@
}
private boolean isInstantiatedOrPinned(DexProgramClass clazz) {
- return isInstantiatedDirectly(clazz)
- || isPinned(clazz.type)
- || hasAnyInstantiatedLambdas(clazz);
+ return isInstantiatedDirectly(clazz) || isPinned(clazz.type) || isInstantiatedInterface(clazz);
}
public boolean isPinnedNotProgramOrLibraryOverride(DexReference reference) {
@@ -1406,7 +1360,7 @@
DexClass clazz = definitionFor(reference.asDexType());
return clazz == null
|| clazz.isNotProgramClass()
- || hasAnyInstantiatedLambdas(clazz.asProgramClass());
+ || isInstantiatedInterface(clazz.asProgramClass());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
index 99f2d84..0f1b348 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
-import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -34,12 +33,13 @@
public void modify(AppInfoWithLiveness appInfo) {
// Instantiated classes.
- ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection =
- appInfo.getMutableObjectAllocationInfoCollection();
- noLongerInstantiatedClasses.forEach(
- clazz -> {
- objectAllocationInfoCollection.markNoLongerInstantiated(clazz);
- appInfo.removeFromSingleTargetLookupCache(clazz);
+ appInfo.mutateObjectAllocationInfoCollection(
+ mutator -> {
+ noLongerInstantiatedClasses.forEach(
+ clazz -> {
+ mutator.markNoLongerInstantiated(clazz);
+ appInfo.removeFromSingleTargetLookupCache(clazz);
+ });
});
// Written fields.
FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index db955c9..c04ddf5 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -18,7 +18,7 @@
public class DefaultEnqueuerUseRegistry extends UseRegistry {
private final ProgramMethod context;
- private final Enqueuer enqueuer;
+ protected final Enqueuer enqueuer;
public DefaultEnqueuerUseRegistry(
AppView<?> appView, DexProgramClass holder, DexEncodedMethod method, Enqueuer enqueuer) {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 20dde1b..f636d05 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -220,6 +221,9 @@
*/
private final Set<DexClass> liveNonProgramTypes = Sets.newIdentityHashSet();
+ /** Set of reachable proto types that will be dead code eliminated. */
+ private final Set<DexProgramClass> deadProtoTypeCandidates = Sets.newIdentityHashSet();
+
/** Set of missing types. */
private final Set<DexType> missingTypes = Sets.newIdentityHashSet();
@@ -411,26 +415,121 @@
this.annotationRemoverBuilder = annotationRemoverBuilder;
}
+ public void addDeadProtoTypeCandidate(DexType type) {
+ assert type.isProgramType(appView);
+ addDeadProtoTypeCandidate(appView.definitionFor(type).asProgramClass());
+ }
+
+ public void addDeadProtoTypeCandidate(DexProgramClass clazz) {
+ deadProtoTypeCandidates.add(clazz);
+ }
+
private boolean isProgramClass(DexType type) {
return getProgramClassOrNull(type) != null;
}
+ private void recordReference(DexReference r) {
+ if (r.isDexType()) {
+ recordTypeReference(r.asDexType());
+ } else if (r.isDexField()) {
+ recordFieldReference(r.asDexField());
+ } else {
+ assert r.isDexMethod();
+ recordMethodReference(r.asDexMethod());
+ }
+ }
+
+ private void recordTypeReference(DexType type) {
+ if (type == null) {
+ return;
+ }
+ if (type.isArrayType()) {
+ type = type.toBaseType(appView.dexItemFactory());
+ }
+ if (!type.isClassType()) {
+ return;
+ }
+ // Lookup the definition, ignoring the result. This populates the missing and referenced sets.
+ definitionFor(type);
+ }
+
+ private void recordMethodReference(DexMethod method) {
+ recordTypeReference(method.holder);
+ recordTypeReference(method.proto.returnType);
+ for (DexType type : method.proto.parameters.values) {
+ recordTypeReference(type);
+ }
+ }
+
+ private void recordFieldReference(DexField field) {
+ recordTypeReference(field.holder);
+ recordTypeReference(field.type);
+ }
+
private DexClass definitionFor(DexType type) {
DexClass clazz = appView.definitionFor(type);
if (clazz == null) {
reportMissingClass(type);
return null;
}
- if (clazz.isProgramClass()) {
- return clazz;
+ if (clazz.isNotProgramClass()) {
+ addLiveNonProgramType(clazz);
}
- if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) {
+ return clazz;
+ }
+
+ private void addLiveNonProgramType(DexClass clazz) {
+ assert clazz.isNotProgramClass();
+ // Fast path to avoid the worklist when the class is already seen.
+ if (!liveNonProgramTypes.add(clazz)) {
+ return;
+ }
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ worklist.addLast(clazz);
+ while (!worklist.isEmpty()) {
+ DexClass definition = worklist.removeFirst();
+ processNewLiveNonProgramType(definition, worklist);
+ }
+ }
+
+ private void processNewLiveNonProgramType(DexClass clazz, Deque<DexClass> worklist) {
+ assert clazz.isNotProgramClass();
+ if (clazz.isLibraryClass()) {
// TODO(b/149201735): This likely needs to apply to classpath too.
ensureMethodsContinueToWidenAccess(clazz);
// Only libraries must not derive program. Classpath classes can, assuming correct keep rules.
warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
}
- return clazz;
+ for (DexEncodedField field : clazz.fields()) {
+ addNonProgramClassToWorklist(field.field.type, worklist);
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ addNonProgramClassToWorklist(method.method.proto.returnType, worklist);
+ for (DexType param : method.method.proto.parameters.values) {
+ addNonProgramClassToWorklist(param, worklist);
+ }
+ }
+ for (DexType supertype : clazz.allImmediateSupertypes()) {
+ addNonProgramClassToWorklist(supertype, worklist);
+ }
+ }
+
+ private void addNonProgramClassToWorklist(DexType type, Deque<DexClass> worklist) {
+ if (type.isArrayType()) {
+ type = type.toBaseType(appView.dexItemFactory());
+ }
+ if (!type.isClassType()) {
+ return;
+ }
+ DexClass definition = appView.definitionFor(type);
+ if (definition == null) {
+ reportMissingClass(type);
+ return;
+ }
+ if (definition.isProgramClass() || !liveNonProgramTypes.add(definition)) {
+ return;
+ }
+ worklist.addLast(definition);
}
private DexProgramClass getProgramClassOrNull(DexType type) {
@@ -495,7 +594,7 @@
}
} else if (item.isDexEncodedField()) {
DexEncodedField dexEncodedField = item.asDexEncodedField();
- DexProgramClass holder = getProgramClassOrNull(dexEncodedField.field.holder);
+ DexProgramClass holder = getProgramClassOrNull(dexEncodedField.holder());
if (holder != null) {
workList.enqueueMarkFieldKeptAction(
holder,
@@ -504,7 +603,7 @@
}
} else if (item.isDexEncodedMethod()) {
DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
- DexProgramClass holder = getProgramClassOrNull(encodedMethod.method.holder);
+ DexProgramClass holder = getProgramClassOrNull(encodedMethod.holder());
if (holder != null) {
workList.enqueueMarkMethodKeptAction(
holder,
@@ -537,7 +636,7 @@
// Utility to avoid adding to the worklist if already live.
private boolean enqueueMarkMethodLiveAction(
DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) {
- assert method.method.holder == clazz.type;
+ assert method.holder() == clazz.type;
if (liveMethods.add(clazz, method, reason)) {
workList.enqueueMarkMethodLiveAction(clazz, method, reason);
return true;
@@ -650,14 +749,12 @@
}
DexEncodedMethod contextMethod = context.getMethod();
- markLambdaAsInstantiated(descriptor, contextMethod);
- transitionMethodsForInstantiatedLambda(descriptor);
if (lambdaRewriter != null) {
assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
CfCode code = contextMethod.getCode().asCfCode();
if (code != null) {
LambdaClass lambdaClass =
- lambdaRewriter.getOrCreateLambdaClass(descriptor, contextMethod.method.holder);
+ lambdaRewriter.getOrCreateLambdaClass(descriptor, contextMethod.holder());
lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, contextMethod));
lambdaCallSites
.computeIfAbsent(contextMethod, k -> new IdentityHashMap<>())
@@ -670,6 +767,8 @@
desugaredLambdaImplementationMethods.add(descriptor.implHandle.asMethod());
}
} else {
+ markLambdaAsInstantiated(descriptor, contextMethod);
+ transitionMethodsForInstantiatedLambda(descriptor);
callSites.add(callSite);
}
@@ -839,6 +938,7 @@
workList.enqueueTraceInvokeDirectAction(
invokedMethod, currentHolder, currentMethod));
if (skipTracing) {
+ addDeadProtoTypeCandidate(invokedMethod.holder);
return false;
}
@@ -854,7 +954,10 @@
return appView.withGeneratedMessageLiteBuilderShrinker(
shrinker ->
shrinker.deferDeadProtoBuilders(
- clazz, currentMethod, () -> liveTypes.registerDeferredAction(clazz, action)),
+ clazz,
+ currentMethod,
+ () -> liveTypes.registerDeferredAction(clazz, action),
+ this),
false);
}
return false;
@@ -997,6 +1100,7 @@
registerDeferredActionForDeadProtoBuilder(
type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, context));
if (skipTracing) {
+ addDeadProtoTypeCandidate(type);
return false;
}
@@ -1059,7 +1163,7 @@
fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
}
- DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder);
+ DexProgramClass clazz = getProgramClassOrNull(encodedField.holder());
if (clazz == null) {
return false;
}
@@ -1108,7 +1212,7 @@
fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
}
- DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder);
+ DexProgramClass clazz = getProgramClassOrNull(encodedField.holder());
if (clazz == null) {
return false;
}
@@ -1156,7 +1260,8 @@
fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
}
- if (!isProgramClass(encodedField.field.holder)) {
+ DexProgramClass holder = getProgramClassOrNull(encodedField.holder());
+ if (holder == null) {
// No need to trace into the non-program code.
return false;
}
@@ -1174,6 +1279,7 @@
encodedField, fieldAccessInfoCollection, pinnedItems),
false);
if (skipTracing) {
+ addDeadProtoTypeCandidate(holder);
return false;
}
}
@@ -1213,7 +1319,8 @@
fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
}
- if (!isProgramClass(encodedField.field.holder)) {
+ DexProgramClass holder = getProgramClassOrNull(encodedField.holder());
+ if (holder == null) {
// No need to trace into the non-program code.
return false;
}
@@ -1231,6 +1338,7 @@
encodedField, fieldAccessInfoCollection, pinnedItems),
false);
if (skipTracing) {
+ addDeadProtoTypeCandidate(holder);
return false;
}
}
@@ -1255,7 +1363,7 @@
if (methodHolderClass != null && methodHolderClass.isInterface()) {
return method;
}
- DexClass holderClass = appView.definitionFor(currentMethod.method.holder);
+ DexClass holderClass = appView.definitionFor(currentMethod.holder());
if (holderClass == null || holderClass.superType == null || holderClass.isInterface()) {
// We do not know better or this call is made from an interface.
return method;
@@ -1331,10 +1439,9 @@
}
// Mark types in inner-class attributes referenced.
- InnerClassAttribute innerClassAttributes = holder.getInnerClassAttributeForThisClass();
- if (innerClassAttributes != null) {
- recordTypeReference(innerClassAttributes.getInner());
- recordTypeReference(innerClassAttributes.getOuter());
+ for (InnerClassAttribute innerClassAttribute : holder.getInnerClasses()) {
+ recordTypeReference(innerClassAttribute.getInner());
+ recordTypeReference(innerClassAttribute.getOuter());
}
if (Log.ENABLED) {
@@ -1525,11 +1632,7 @@
private SingleResolutionResult resolveMethod(DexMethod method, KeepReason reason) {
// Record the references in case they are not program types.
- recordTypeReference(method.holder);
- recordTypeReference(method.proto.returnType);
- for (DexType param : method.proto.parameters.values) {
- recordTypeReference(param);
- }
+ recordMethodReference(method);
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
if (resolutionResult.isFailedResolution()) {
reportMissingMethod(method);
@@ -1541,11 +1644,7 @@
private SingleResolutionResult resolveMethod(
DexMethod method, KeepReason reason, boolean interfaceInvoke) {
// Record the references in case they are not program types.
- recordTypeReference(method.holder);
- recordTypeReference(method.proto.returnType);
- for (DexType param : method.proto.parameters.values) {
- recordTypeReference(param);
- }
+ recordMethodReference(method);
ResolutionResult resolutionResult =
appInfo.resolveMethod(method.holder, method, interfaceInvoke);
if (resolutionResult.isFailedResolution()) {
@@ -1617,10 +1716,7 @@
DexType holder = method.holder;
DexProgramClass clazz = getProgramClassOrNull(holder);
if (clazz == null) {
- recordTypeReference(method.proto.returnType);
- for (DexType param : method.proto.parameters.values) {
- recordTypeReference(param);
- }
+ recordMethodReference(method);
return;
}
// TODO(zerny): Is it ok that we lookup in both the direct and virtual pool here?
@@ -1717,7 +1813,7 @@
private void markMethodAsTargeted(
DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) {
- assert method.method.holder == clazz.type;
+ assert method.holder() == clazz.type;
if (!targetedMethods.add(method, reason)) {
// Already targeted.
return;
@@ -1792,7 +1888,7 @@
void markInterfaceAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) {
assert !clazz.isAnnotation();
assert clazz.isInterface();
- if (!objectAllocationInfoCollection.recordInstantiatedInterface(clazz)) {
+ if (!objectAllocationInfoCollection.recordInstantiatedInterface(clazz, appInfo)) {
return;
}
markTypeAsLive(clazz, witness);
@@ -1804,12 +1900,11 @@
for (DexType iface : descriptor.interfaces) {
checkLambdaInterface(iface, context);
objectAllocationInfoCollection.recordInstantiatedLambdaInterface(iface, descriptor, appInfo);
- // TODO(b/150277553): Lambdas should be accurately traces and thus not be added here.
- if (lambdaRewriter == null) {
- DexProgramClass clazz = getProgramClassOrNull(iface);
- if (clazz != null) {
- objectAllocationInfoCollection.recordInstantiatedInterface(clazz);
- }
+ // TODO(b/150277553): Lambdas should be accurately traced and thus not be added here.
+ assert lambdaRewriter == null;
+ DexProgramClass clazz = getProgramClassOrNull(iface);
+ if (clazz != null) {
+ objectAllocationInfoCollection.recordInstantiatedInterface(clazz, appInfo);
}
}
}
@@ -1820,7 +1915,7 @@
StringDiagnostic message =
new StringDiagnostic(
"Lambda expression implements missing interface `" + itf.toSourceString() + "`",
- appInfo.originFor(context.method.holder));
+ appInfo.originFor(context.holder()));
options.reporter.warning(message);
} else if (!clazz.isInterface()) {
StringDiagnostic message =
@@ -1829,7 +1924,7 @@
+ "`"
+ itf.toSourceString()
+ "`",
- appInfo.originFor(context.method.holder));
+ appInfo.originFor(context.holder()));
options.reporter.warning(message);
}
}
@@ -1969,7 +2064,7 @@
&& appView.rewritePrefix.hasRewrittenTypeInSignature(method.method.proto, appView)) {
DexMethod methodToResolve =
DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
- method.method, method.method.holder, appView);
+ method.method, method.holder(), appView);
assert methodToResolve != method.method;
markLibraryOrClasspathOverrideLive(
instantiation,
@@ -2124,7 +2219,7 @@
DexProgramClass holder, DexEncodedField field, KeepReason reason) {
assert field != null;
assert field.isProgramField(appView);
- markTypeAsLive(field.field.holder, reason);
+ markTypeAsLive(field.holder(), reason);
markTypeAsLive(field.field.type, reason);
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
@@ -2141,7 +2236,7 @@
private void markDirectStaticOrConstructorMethodAsLive(
DexProgramClass clazz, DexEncodedMethod encodedMethod, KeepReason reason) {
- assert encodedMethod.method.holder == clazz.type;
+ assert encodedMethod.holder() == clazz.type;
if (!enqueueMarkMethodLiveAction(clazz, encodedMethod, reason)) {
// Already marked live.
@@ -2196,7 +2291,7 @@
if (info == null) {
return false;
}
- DexClass clazz = appView.definitionFor(field.field.holder);
+ DexClass clazz = appView.definitionFor(field.holder());
DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
return defaultInitializer != null
? info.isWrittenOutside(defaultInitializer)
@@ -2211,10 +2306,21 @@
return targetedMethods.contains(method);
}
+ public boolean isTypeLive(DexClass clazz) {
+ return clazz.isProgramClass()
+ ? isTypeLive(clazz.asProgramClass())
+ : isNonProgramTypeLive(clazz);
+ }
+
public boolean isTypeLive(DexProgramClass clazz) {
return liveTypes.contains(clazz);
}
+ public boolean isNonProgramTypeLive(DexClass clazz) {
+ assert !clazz.isProgramClass();
+ return liveNonProgramTypes.contains(clazz);
+ }
+
// Package protected due to entry point from worklist.
void markInstanceFieldAsReachable(DexEncodedField encodedField, KeepReason reason) {
DexField field = encodedField.field;
@@ -2250,19 +2356,6 @@
}
}
- private void recordTypeReference(DexType type) {
- if (type == null) {
- return;
- }
- if (type.isArrayType()) {
- type = type.toBaseType(appView.dexItemFactory());
- }
- if (!type.isClassType()) {
- return;
- }
- getProgramClassOrNull(type);
- }
-
private void markVirtualMethodAsReachable(
DexMethod method, boolean interfaceInvoke, ProgramMethod contextOrNull, KeepReason reason) {
if (method.holder.isArrayType()) {
@@ -2281,10 +2374,7 @@
if (holder == null) {
// TODO(b/139464956): clean this.
// Ensure that the full proto of the targeted method is referenced.
- recordTypeReference(method.proto.returnType);
- for (DexType type : method.proto.parameters.values) {
- recordTypeReference(type);
- }
+ recordMethodReference(method);
return;
}
@@ -2385,7 +2475,7 @@
failedResolutionTargets.add(symbolicMethod);
failedResolution.forEachFailureDependency(
method -> {
- DexProgramClass clazz = getProgramClassOrNull(method.method.holder);
+ DexProgramClass clazz = getProgramClassOrNull(method.holder());
if (clazz != null) {
failedResolutionTargets.add(method.method);
markMethodAsTargeted(clazz, method, reason);
@@ -2430,14 +2520,14 @@
}
// If invoke target is invalid (inaccessible or not an instance-method) record it and stop.
// TODO(b/146016987): We should be passing the full program context and not looking it up again.
- DexProgramClass fromHolder = appInfo.definitionFor(from.method.holder).asProgramClass();
+ DexProgramClass fromHolder = appInfo.definitionFor(from.holder()).asProgramClass();
DexEncodedMethod target = resolution.lookupInvokeSuperTarget(fromHolder, appInfo);
if (target == null) {
failedResolutionTargets.add(resolution.getResolvedMethod().method);
return;
}
- DexProgramClass clazz = getProgramClassOrNull(target.method.holder);
+ DexProgramClass clazz = getProgramClassOrNull(target.holder());
if (clazz == null) {
return;
}
@@ -2645,7 +2735,7 @@
// Ensure accessors if needed and mark them live too.
DexEncodedMethod accessor = lambdaClass.target.ensureAccessibilityIfNeeded(false);
if (accessor != null) {
- DexProgramClass clazz = getProgramClassOrNull(accessor.method.holder);
+ DexProgramClass clazz = getProgramClassOrNull(accessor.holder());
additions.addLiveMethod(new ProgramMethod(clazz, accessor));
}
}
@@ -2684,50 +2774,52 @@
}
private AppInfoWithLiveness createAppInfo(AppInfoWithSubtyping appInfo) {
+ // Compute the set of dead proto types.
+ deadProtoTypeCandidates.removeIf(this::isTypeLive);
+
// Remove the temporary mappings that have been inserted into the field access info collection
// and verify that the mapping is then one-to-one.
fieldAccessInfoCollection.removeIf(
(field, info) -> field != info.getField() || info == MISSING_FIELD_ACCESS_INFO);
assert fieldAccessInfoCollection.verifyMappingIsOneToOne();
- // Ensure references from various root set collections.
- rootSet
- .noSideEffects
- .keySet()
- .forEach(
- r -> {
- if (r.isDexType()) {
- recordTypeReference(r.asDexType());
- } else if (r.isDexField()) {
- recordTypeReference(r.asDexField().holder);
- recordTypeReference(r.asDexField().type);
- } else {
- assert r.isDexMethod();
- recordTypeReference(r.asDexMethod().holder);
- recordTypeReference(r.asDexMethod().proto.returnType);
- for (DexType param : r.asDexMethod().proto.parameters.values) {
- recordTypeReference(param);
- }
- }
- });
+ // Verify all references on the input app before synthesizing definitions.
+ assert verifyReferences(appInfo.app());
+
+ // Prune the root set items that turned out to be dead.
+ // TODO(b/150736225): Pruning of dead root set items is still incomplete.
+ rootSet.pruneDeadItems(appView, this);
+
+ // Ensure references from all hard coded factory items.
+ appView.dexItemFactory().forEachPossiblyCompilerSynthesizedType(this::recordTypeReference);
// Rebuild a new app only containing referenced types.
- appView.dexItemFactory().forEachPossiblyCompilerSynthesizedType(this::recordTypeReference);
Set<DexLibraryClass> libraryClasses = Sets.newIdentityHashSet();
Set<DexClasspathClass> classpathClasses = Sets.newIdentityHashSet();
for (DexClass clazz : liveNonProgramTypes) {
- traverseHierarchy(clazz, libraryClasses, classpathClasses);
+ if (clazz.isLibraryClass()) {
+ libraryClasses.add(clazz.asLibraryClass());
+ } else if (clazz.isClasspathClass()) {
+ classpathClasses.add(clazz.asClasspathClass());
+ } else {
+ assert false;
+ }
}
+
+ // Add just referenced non-program types. We can't replace the program classes at this point as
+ // they are needed in tree pruning.
Builder appBuilder = appInfo.app().asDirect().builder();
appBuilder.replaceLibraryClasses(libraryClasses);
appBuilder.replaceClasspathClasses(classpathClasses);
- // Can't replace the program classes at this point as they are needed in tree pruning.
- // Post process the app to add synthetic content.
DirectMappedDexApplication app = appBuilder.build();
+ // Verify the references on the pruned application after type synthesis.
+ assert verifyReferences(app);
+
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
app,
+ SetUtils.mapIdentityHashSet(deadProtoTypeCandidates, DexProgramClass::getType),
missingTypes,
SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
Collections.unmodifiableSet(instantiatedAppServices),
@@ -2740,7 +2832,7 @@
toSortedDescriptorSet(liveMethods.getItems()),
// Filter out library fields and pinned fields, because these are read by default.
fieldAccessInfoCollection,
- objectAllocationInfoCollection.build(),
+ objectAllocationInfoCollection.build(appInfo),
// TODO(b/132593519): Do we require these sets to be sorted for determinism?
toImmutableSortedMap(virtualInvokes, PresortedComparable::slowCompare),
toImmutableSortedMap(interfaceInvokes, PresortedComparable::slowCompare),
@@ -2768,54 +2860,72 @@
Collections.emptySet(),
Collections.emptyMap(),
EnumValueInfoMapCollection.empty(),
- // TODO(b/150277553): Remove this once object allocation contains the information.
- SetUtils.mapIdentityHashSet(
- objectAllocationInfoCollection.unknownInstantiatedInterfaceTypes,
- DexProgramClass::getType),
constClassReferences,
initClassReferences);
appInfo.markObsolete();
return appInfoWithLiveness;
}
- private void traverseHierarchy(
- DexClass clazz,
- Set<DexLibraryClass> libraryClasses,
- Set<DexClasspathClass> classpathClasses) {
- if (clazz.isLibraryClass()) {
- libraryClasses.add(clazz.asLibraryClass());
- } else if (clazz.isClasspathClass()) {
- classpathClasses.add(clazz.asClasspathClass());
+ private boolean verifyReferences(DexApplication app) {
+ WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
+ for (DexProgramClass clazz : liveTypes.getItems()) {
+ worklist.addIfNotSeen(clazz);
}
- Deque<DexType> worklist = new ArrayDeque<>();
- if (clazz.superType != null) {
- worklist.add(clazz.superType);
+ while (worklist.hasNext()) {
+ DexClass clazz = worklist.next();
+ assert verifyReferencedType(clazz, worklist, app);
}
- Collections.addAll(worklist, clazz.interfaces.values);
- while (!worklist.isEmpty()) {
- DexType type = worklist.pop();
- DexClass definition = appView.definitionFor(type);
- if (definition == null) {
- continue;
- }
- if (definition.isProgramClass()) {
- // TODO(b/120884788): This should assert not possible once fixed.
- continue;
- }
- if (definition.isLibraryClass()) {
- if (!libraryClasses.add(definition.asLibraryClass())) {
- continue;
- }
- } else if (definition.isClasspathClass()) {
- if (!classpathClasses.add(definition.asClasspathClass())) {
- continue;
- }
- }
- if (definition.superType != null) {
- worklist.add(definition.superType);
- }
- Collections.addAll(worklist, definition.interfaces.values);
+ return true;
+ }
+
+ private boolean verifyReferencedType(
+ DexType type, WorkList<DexClass> worklist, DexApplication app) {
+ if (type.isArrayType()) {
+ type = type.toBaseType(appView.dexItemFactory());
}
+ if (!type.isClassType()) {
+ return true;
+ }
+ DexClass clazz = app.definitionFor(type);
+ if (clazz == null) {
+ assert missingTypes.contains(type) : "Expected type to be in missing types': " + type;
+ } else {
+ assert !missingTypes.contains(type) : "Type with definition also in missing types: " + type;
+ // Eager assert while the context is still present.
+ assert clazz.isProgramClass() || liveNonProgramTypes.contains(clazz)
+ : "Expected type to be in live non-program types: " + clazz;
+ worklist.addIfNotSeen(clazz);
+ }
+ return true;
+ }
+
+ private boolean verifyReferencedType(
+ DexClass clazz, WorkList<DexClass> worklist, DexApplication app) {
+ for (DexType supertype : clazz.allImmediateSupertypes()) {
+ assert verifyReferencedType(supertype, worklist, app);
+ }
+ assert clazz.isProgramClass() || liveNonProgramTypes.contains(clazz)
+ : "Expected type to be in live non-program types: " + clazz;
+ for (DexEncodedField field : clazz.fields()) {
+ if (clazz.isNotProgramClass() || isFieldReferenced(field)) {
+ assert verifyReferencedType(field.field.type, worklist, app);
+ }
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (clazz.isNotProgramClass() || isMethodTargeted(method)) {
+ assert verifyReferencedMethod(method, worklist, app);
+ }
+ }
+ return true;
+ }
+
+ private boolean verifyReferencedMethod(
+ DexEncodedMethod method, WorkList<DexClass> worklist, DexApplication app) {
+ assert verifyReferencedType(method.method.proto.returnType, worklist, app);
+ for (DexType param : method.method.proto.parameters.values) {
+ assert verifyReferencedType(param, worklist, app);
+ }
+ return true;
}
private void synthesizeLibraryConversionWrappers(SyntheticAdditions additions) {
@@ -2826,7 +2936,7 @@
// Generate first the callbacks since they may require extra wrappers.
List<DexEncodedMethod> callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods();
for (DexEncodedMethod callback : callbacks) {
- DexProgramClass clazz = getProgramClassOrNull(callback.method.holder);
+ DexProgramClass clazz = getProgramClassOrNull(callback.holder());
additions.addLiveMethod(new ProgramMethod(clazz, callback));
}
@@ -3111,7 +3221,7 @@
} else {
DexEncodedMethod implementation = target.getDefaultInterfaceMethodImplementation();
if (implementation != null) {
- DexProgramClass companion = getProgramClassOrNull(implementation.method.holder);
+ DexProgramClass companion = getProgramClassOrNull(implementation.holder());
markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion));
markVirtualMethodAsLive(
companion,
@@ -3128,7 +3238,7 @@
// Package protected due to entry point from worklist.
void markFieldAsKept(DexProgramClass holder, DexEncodedField target, KeepReason reason) {
- assert holder.type == target.field.holder;
+ assert holder.type == target.holder();
if (target.accessFlags.isStatic()) {
markStaticFieldAsLive(target, reason);
} else {
@@ -3206,7 +3316,7 @@
void markMethodAsLive(DexEncodedMethod method, KeepReason reason) {
assert liveMethods.contains(method);
- DexProgramClass clazz = getProgramClassOrNull(method.method.holder);
+ DexProgramClass clazz = getProgramClassOrNull(method.holder());
if (clazz == null) {
return;
}
@@ -3221,7 +3331,7 @@
if (Log.ENABLED) {
Log.verbose(getClass(), "Found super invoke constraint on `%s`.", superCallTarget.method);
}
- DexProgramClass targetClass = getProgramClassOrNull(superCallTarget.method.holder);
+ DexProgramClass targetClass = getProgramClassOrNull(superCallTarget.holder());
assert targetClass != null;
if (targetClass != null) {
markMethodAsTargeted(
@@ -3246,7 +3356,7 @@
private void markReferencedTypesAsLive(DexEncodedMethod method) {
markTypeAsLive(
- method.method.holder, clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
+ method.holder(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
markParameterAndReturnTypesAsLive(method);
}
@@ -3290,7 +3400,7 @@
}
private void handleReflectiveBehavior(DexEncodedMethod method) {
- DexType originHolder = method.method.holder;
+ DexType originHolder = method.holder();
Origin origin = appInfo.originFor(originHolder);
IRCode code = method.buildIR(appView, origin);
InstructionIterator iterator = code.instructionIterator();
@@ -3578,7 +3688,7 @@
DexType type = invoke.inValues().get(0).definition.asConstClass().getValue();
DexProgramClass clazz = getProgramClassOrNull(type);
if (clazz != null && clazz.accessFlags.isEnum()) {
- DexProgramClass holder = getProgramClassOrNull(method.method.holder);
+ DexProgramClass holder = getProgramClassOrNull(method.holder());
markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(holder, method));
}
}
@@ -3603,7 +3713,7 @@
+ "` is being passed to the method `"
+ invoke.getInvokedMethod().toSourceString()
+ "`, but was not found in `META-INF/services/`.",
- appInfo.originFor(method.method.holder)));
+ appInfo.originFor(method.holder())));
}
return;
}
@@ -3772,10 +3882,7 @@
@Override
public boolean addMethod(DexMethod method) {
// Record the references in case they are not program types.
- recordTypeReference(method.proto.returnType);
- for (DexType param : method.proto.parameters.values) {
- recordTypeReference(param);
- }
+ recordMethodReference(method);
DexProgramClass holder = getProgramClassOrNull(method.holder);
if (holder == null) {
return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index c795bcc..2a46ba3 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -264,7 +264,7 @@
public void enqueueMarkReachableFieldAction(
DexProgramClass clazz, DexEncodedField field, KeepReason reason) {
- assert field.field.holder == clazz.type;
+ assert field.holder() == clazz.type;
queue.add(new MarkReachableFieldAction(field, reason));
}
@@ -294,13 +294,13 @@
void enqueueMarkMethodLiveAction(
DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) {
- assert method.method.holder == clazz.type;
+ assert method.holder() == clazz.type;
queue.add(new MarkMethodLiveAction(method, reason));
}
void enqueueMarkMethodKeptAction(
DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) {
- assert method.method.holder == clazz.type;
+ assert method.holder() == clazz.type;
queue.add(new MarkMethodKeptAction(clazz, method, reason));
}
@@ -317,7 +317,7 @@
public void enqueueTraceInvokeDirectAction(
DexMethod invokedMethod, DexProgramClass currentHolder, DexEncodedMethod currentMethod) {
- assert currentMethod.method.holder == currentHolder.type;
+ assert currentMethod.holder() == currentHolder.type;
queue.add(new TraceInvokeDirectAction(invokedMethod, currentHolder, currentMethod));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index cf863de..6b222d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -171,7 +171,7 @@
public KeepReasonWitness reportCompatKeepDefaultInitializer(
DexProgramClass holder, DexEncodedMethod defaultInitializer) {
- assert holder.type == defaultInitializer.method.holder;
+ assert holder.type == defaultInitializer.holder();
assert holder.getDefaultInitializer() == defaultInitializer;
if (keptGraphConsumer != null) {
reportEdge(
@@ -183,7 +183,7 @@
}
public KeepReasonWitness reportCompatKeepMethod(DexProgramClass holder, DexEncodedMethod method) {
- assert holder.type == method.method.holder;
+ assert holder.type == method.holder();
// TODO(b/141729349): This compat rule is from the method to itself and has not edge. Fix it.
// The rule is stating that if the method is targeted it is live. Since such an edge does
// not contribute to additional information in the kept graph as it stands (no distinction
@@ -234,7 +234,7 @@
public KeepReasonWitness reportReachableClassInitializer(
DexProgramClass clazz, DexEncodedMethod initializer) {
if (initializer != null) {
- assert clazz.type == initializer.method.holder;
+ assert clazz.type == initializer.holder();
assert initializer.isClassInitializer();
if (keptGraphConsumer != null) {
ClassGraphNode source = getClassGraphNode(clazz.type);
@@ -284,7 +284,7 @@
public KeepReasonWitness reportCompanionMethod(
DexEncodedMethod definition, DexEncodedMethod implementation) {
- assert InterfaceMethodRewriter.isCompanionClassType(implementation.method.holder);
+ assert InterfaceMethodRewriter.isCompanionClassType(implementation.holder());
if (keptGraphConsumer == null) {
return KeepReasonWitness.INSTANCE;
}
@@ -359,7 +359,7 @@
if (skipReporting(reason)) {
return KeepReasonWitness.INSTANCE;
}
- if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.method.holder)) {
+ if (reason.edgeKind() == EdgeKind.IsLibraryMethod && isNonProgramClass(method.holder())) {
// Don't report edges to actual library methods.
// TODO(b/120959039): This should be dead code once no library classes are ever enqueued.
return KeepReasonWitness.INSTANCE;
@@ -415,6 +415,11 @@
return reasonInfo.computeIfAbsent(kind, k -> new GraphEdgeInfo(k));
}
+ private DexClass definitionFor(DexType type) {
+ // The query of the graph can be outside program referenced types and should not fail.
+ return appView.appInfo().definitionForWithoutExistenceAssert(type);
+ }
+
AnnotationGraphNode getAnnotationGraphNode(DexItem type) {
return annotationNodes.computeIfAbsent(
type,
@@ -431,7 +436,7 @@
return classNodes.computeIfAbsent(
type,
t -> {
- DexClass definition = appView.definitionFor(t);
+ DexClass definition = definitionFor(t);
return new ClassGraphNode(
definition != null && definition.isNotProgramClass(),
Reference.classFromDescriptor(t.toDescriptorString()));
@@ -442,7 +447,7 @@
return methodNodes.computeIfAbsent(
context,
m -> {
- DexClass holderDefinition = appView.definitionFor(context.holder);
+ DexClass holderDefinition = definitionFor(context.holder);
Builder<TypeReference> builder = ImmutableList.builder();
for (DexType param : m.proto.parameters.values) {
builder.add(Reference.typeFromDescriptor(param.toDescriptorString()));
@@ -463,7 +468,7 @@
return fieldNodes.computeIfAbsent(
context,
f -> {
- DexClass holderDefinition = appView.definitionFor(context.holder);
+ DexClass holderDefinition = definitionFor(context.holder);
return new FieldGraphNode(
holderDefinition != null && holderDefinition.isNotProgramClass(),
Reference.field(
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index c66d4a4..dd76e19 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -152,7 +152,7 @@
private InvokedFrom(DexProgramClass holder, DexEncodedMethod method) {
super(method);
- assert holder.type == method.method.holder;
+ assert holder.type == method.holder();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 7e9ed91..5b1f672 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -473,13 +473,21 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString("neverreprocessclassinitializer")) {
+ configurationBuilder.addRule(
+ parseReprocessClassInitializerRule(
+ ReprocessClassInitializerRule.Type.NEVER, optionStart));
+ return true;
+ }
if (acceptString("neverreprocessmethod")) {
configurationBuilder.addRule(
parseReprocessMethodRule(ReprocessMethodRule.Type.NEVER, optionStart));
return true;
}
if (acceptString("reprocessclassinitializer")) {
- configurationBuilder.addRule(parseReprocessClassInitializerRule(optionStart));
+ configurationBuilder.addRule(
+ parseReprocessClassInitializerRule(
+ ReprocessClassInitializerRule.Type.ALWAYS, optionStart));
return true;
}
if (acceptString("reprocessmethod")) {
@@ -822,10 +830,11 @@
return keepRuleBuilder.build();
}
- private ReprocessClassInitializerRule parseReprocessClassInitializerRule(Position start)
+ private ReprocessClassInitializerRule parseReprocessClassInitializerRule(
+ ReprocessClassInitializerRule.Type type, Position start)
throws ProguardRuleParserException {
ReprocessClassInitializerRule.Builder builder =
- ReprocessClassInitializerRule.builder().setOrigin(origin).setStart(start);
+ ReprocessClassInitializerRule.builder().setOrigin(origin).setStart(start).setType(type);
parseClassSpec(builder, false);
Position end = getPosition();
builder.setSource(getSourceSnippet(contents, start, end));
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index 41aa9c5..d59c63e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -65,6 +65,14 @@
return null;
}
+ public boolean isReprocessClassInitializerRule() {
+ return false;
+ }
+
+ public ReprocessClassInitializerRule asReprocessClassInitializerRule() {
+ return null;
+ }
+
public boolean isReprocessMethodRule() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index f75a307..590ede0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -57,7 +57,7 @@
public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
// TODO(b/122295241): These generated rules should be linked into the graph, eg, the method
// using identified reflection should be the source keeping the target alive.
- assert clazz.type == method.method.holder;
+ assert clazz.type == method.holder();
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
diff --git a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
index fa7d352..58e03d9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ReprocessClassInitializerRule.java
@@ -3,19 +3,32 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import java.util.List;
public class ReprocessClassInitializerRule extends ProguardConfigurationRule {
+ public enum Type {
+ ALWAYS,
+ NEVER
+ }
+
public static class Builder
extends ProguardConfigurationRule.Builder<ReprocessClassInitializerRule, Builder> {
+ private Type type;
+
private Builder() {
super();
}
+ public Builder setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
@Override
public Builder self() {
return this;
@@ -36,10 +49,13 @@
inheritanceAnnotation,
inheritanceClassName,
inheritanceIsExtends,
- memberRules);
+ memberRules,
+ type);
}
}
+ private final Type type;
+
private ReprocessClassInitializerRule(
Origin origin,
Position position,
@@ -53,7 +69,8 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- List<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules,
+ Type type) {
super(
origin,
position,
@@ -68,14 +85,36 @@
inheritanceClassName,
inheritanceIsExtends,
memberRules);
+ this.type = type;
}
public static Builder builder() {
return new Builder();
}
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public boolean isReprocessClassInitializerRule() {
+ return true;
+ }
+
+ @Override
+ public ReprocessClassInitializerRule asReprocessClassInitializerRule() {
+ return this;
+ }
+
@Override
String typeString() {
- return "reprocessclassinitializer";
+ switch (type) {
+ case ALWAYS:
+ return "reprocessclassinitializer";
+ case NEVER:
+ return "neverreprocessclassinitializer";
+ default:
+ throw new Unreachable();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 928de3a..c89dd04 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.shaking.ReprocessClassInitializerRule.Type.ALWAYS;
+import static com.android.tools.r8.shaking.ReprocessClassInitializerRule.Type.NEVER;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -14,6 +17,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -377,7 +381,7 @@
DexEncodedMethod target =
appView.appInfo().resolveMethod(subType, referenceInSubType).getSingleTarget();
// But, the resolution should not be landed on the current type we are visiting.
- if (target == null || target.method.holder == type) {
+ if (target == null || target.holder() == type) {
continue;
}
ProguardMemberRule ruleInSubType = assumeRulePool.get(target.method);
@@ -598,7 +602,7 @@
private boolean canInsertForwardingMethod(DexClass holder, DexEncodedMethod target) {
return appView.options().isGeneratingDex()
- || ArrayUtils.contains(holder.interfaces.values, target.method.holder);
+ || ArrayUtils.contains(holder.interfaces.values, target.holder());
}
private void markMatchingOverriddenMethods(
@@ -1056,7 +1060,7 @@
if (options.isInterfaceMethodDesugaringEnabled()
&& encodedMethod.hasCode()
&& (encodedMethod.isPrivateMethod() || encodedMethod.isStaticMember())) {
- DexClass holder = appView.definitionFor(encodedMethod.method.holder);
+ DexClass holder = appView.definitionFor(encodedMethod.holder());
if (holder != null && holder.isInterface()) {
if (rule.isSpecific()) {
options.reporter.warning(
@@ -1219,7 +1223,16 @@
} else if (context instanceof ReprocessClassInitializerRule) {
DexProgramClass clazz = item.asProgramClass();
if (clazz != null && clazz.hasClassInitializer()) {
- reprocess.add(clazz.getClassInitializer().method);
+ switch (context.asReprocessClassInitializerRule().getType()) {
+ case ALWAYS:
+ reprocess.add(clazz.getClassInitializer().method);
+ break;
+ case NEVER:
+ neverReprocess.add(clazz.getClassInitializer().method);
+ break;
+ default:
+ throw new Unreachable();
+ }
context.markAsUsed();
}
} else if (context.isReprocessMethodRule()) {
@@ -1306,8 +1319,8 @@
this.noObfuscation = noObfuscation;
this.reasonAsked = reasonAsked;
this.checkDiscarded = checkDiscarded;
- this.alwaysInline = Collections.unmodifiableSet(alwaysInline);
- this.forceInline = Collections.unmodifiableSet(forceInline);
+ this.alwaysInline = alwaysInline;
+ this.forceInline = forceInline;
this.neverInline = neverInline;
this.bypassClinitForInlining = bypassClinitForInlining;
this.whyAreYouNotInlining = whyAreYouNotInlining;
@@ -1317,7 +1330,7 @@
this.neverReprocess = neverReprocess;
this.alwaysClassInline = alwaysClassInline;
this.neverClassInline = neverClassInline;
- this.neverMerge = Collections.unmodifiableSet(neverMerge);
+ this.neverMerge = neverMerge;
this.neverPropagateValue = neverPropagateValue;
this.mayHaveSideEffects = mayHaveSideEffects;
this.noSideEffects = noSideEffects;
@@ -1448,6 +1461,45 @@
assumedValues.remove(reference);
}
+ public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) {
+ pruneDeadReferences(neverMerge, definitions, enqueuer);
+ pruneDeadReferences(alwaysInline, definitions, enqueuer);
+ pruneDeadReferences(noSideEffects.keySet(), definitions, enqueuer);
+ }
+
+ private static void pruneDeadReferences(
+ Set<? extends DexReference> references,
+ DexDefinitionSupplier definitions,
+ Enqueuer enqueuer) {
+ references.removeIf(
+ reference -> {
+ if (reference.isDexField()) {
+ DexEncodedField definition = definitions.definitionFor(reference.asDexField());
+ if (definition == null) {
+ return true;
+ }
+ DexClass holder = definitions.definitionFor(definition.holder());
+ if (holder.isProgramClass()) {
+ return !enqueuer.isFieldReferenced(definition);
+ }
+ return !enqueuer.isNonProgramTypeLive(holder);
+ } else if (reference.isDexMethod()) {
+ DexEncodedMethod definition = definitions.definitionFor(reference.asDexMethod());
+ if (definition == null) {
+ return true;
+ }
+ DexClass holder = definitions.definitionFor(definition.holder());
+ if (holder.isProgramClass()) {
+ return !enqueuer.isMethodLive(definition) && !enqueuer.isMethodTargeted(definition);
+ }
+ return !enqueuer.isNonProgramTypeLive(holder);
+ } else {
+ DexClass definition = definitions.definitionFor(reference.asDexType());
+ return definition == null || !enqueuer.isTypeLive(definition);
+ }
+ });
+ }
+
public void move(DexReference original, DexReference rewritten) {
copy(original, rewritten);
prune(original);
diff --git a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
index 7326fad..4675107 100644
--- a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
+++ b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
@@ -63,8 +63,8 @@
}
if (method.accessFlags.isMoreVisibleThan(
existing.accessFlags,
- method.method.holder.getPackageName(),
- existing.method.holder.getPackageName())) {
+ method.holder().getPackageName(),
+ existing.holder().getPackageName())) {
items.put(wrapped, method);
return AddMethodIfMoreVisibleResult.ADDED_MORE_VISIBLE;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 1fb713e..7c679e0 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -349,8 +349,8 @@
: reachableOrReferencedFields.toArray(DexEncodedField.EMPTY_ARRAY);
}
- public Collection<DexType> getRemovedClasses() {
- return Collections.unmodifiableCollection(prunedTypes);
+ public Set<DexType> getRemovedClasses() {
+ return Collections.unmodifiableSet(prunedTypes);
}
public Collection<DexReference> getMethodsToKeepForConfigurationDebugging() {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 92fa339..2fb7435 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
@@ -332,9 +333,9 @@
private boolean isMergeCandidate(
DexProgramClass sourceClass, DexProgramClass targetClass, Set<DexType> pinnedTypes) {
assert targetClass != null;
-
- if (appInfo.getObjectAllocationInfoCollection().isInstantiatedDirectly(sourceClass)
- || appInfo.instantiatedLambdas.contains(sourceClass.type)
+ ObjectAllocationInfoCollection allocationInfo = appInfo.getObjectAllocationInfoCollection();
+ if (allocationInfo.isInstantiatedDirectly(sourceClass)
+ || allocationInfo.isInterfaceWithUnknownSubtypeHierarchy(sourceClass)
|| appInfo.isPinned(sourceClass.type)
|| pinnedTypes.contains(sourceClass.type)
|| appInfo.neverMerge.contains(sourceClass.type)) {
@@ -736,7 +737,7 @@
// Conservatively find all possible targets for this method.
LookupResultSuccess lookupResult =
appInfo
- .resolveMethodOnInterface(method.method.holder, method.method)
+ .resolveMethodOnInterface(method.holder(), method.method)
.lookupVirtualDispatchTargets(target, appInfo)
.asLookupResultSuccess();
assert lookupResult != null;
@@ -988,7 +989,7 @@
Rename.ALWAYS,
appView
.dexItemFactory()
- .prependTypeToProto(virtualMethod.method.holder, virtualMethod.method.proto));
+ .prependTypeToProto(virtualMethod.holder(), virtualMethod.method.proto));
makeStatic(resultingDirectMethod);
// Update method pool collection now that we are adding a new public method.
@@ -1327,7 +1328,7 @@
private DexEncodedMethod renameConstructor(
DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) {
assert method.isInstanceInitializer();
- DexType oldHolder = method.method.holder;
+ DexType oldHolder = method.holder();
DexMethod newSignature;
int count = 1;
@@ -1363,7 +1364,7 @@
// renamed already.
assert !method.accessFlags.isConstructor() || strategy == Rename.NEVER;
DexString oldName = method.method.name;
- DexType oldHolder = method.method.holder;
+ DexType oldHolder = method.holder();
DexMethod newSignature;
switch (strategy) {
@@ -1398,7 +1399,7 @@
private DexEncodedField renameFieldIfNeeded(
DexEncodedField field, Predicate<DexField> availableFieldSignatures) {
DexString oldName = field.field.name;
- DexType oldHolder = field.field.holder;
+ DexType oldHolder = field.holder();
DexField newSignature =
application.dexItemFactory.createField(target.type, field.field.type, oldName);
@@ -1647,7 +1648,7 @@
code.computeInliningConstraint(
method,
appView,
- new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
+ new SingleTypeMapperGraphLense(method.holder(), invocationContext),
invocationContext);
if (constraint == ConstraintWithTarget.NEVER) {
return AbortReason.UNSAFE_INLINING;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index eded127..1013539 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -125,7 +125,7 @@
if (virtualToDirectMethodMap != null) {
GraphLenseLookupResult lookup = virtualToDirectMethodMap.get(previous.getMethod());
if (lookup != null) {
- // If the super class A of the enclosing class B (i.e., context.method.holder)
+ // If the super class A of the enclosing class B (i.e., context.holder())
// has been merged into B during vertical class merging, and this invoke-super instruction
// was resolving to a method in A, then the target method has been changed to a direct
// method and moved into B, so that we need to use an invoke-direct instruction instead of
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index f9f955c..ad290b6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.errors.InvalidDebugInfoException;
+import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic;
import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.features.FeatureSplitConfiguration;
@@ -70,6 +71,7 @@
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import org.objectweb.asm.Opcodes;
public class InternalOptions {
@@ -183,6 +185,7 @@
enableLambdaMerging = false;
enableHorizontalClassMerging = false;
enableVerticalClassMerging = false;
+ enableEnumUnboxing = false;
enableUninstantiatedTypeOptimization = false;
enableUnusedArgumentRemoval = false;
outline.enabled = false;
@@ -701,6 +704,8 @@
/** A set of dexitems we have reported missing to dedupe warnings. */
private final Set<DexItem> reportedMissingForDesugaring = Sets.newConcurrentHashSet();
+ private final Set<DexItem> invalidLibraryClasses = Sets.newConcurrentHashSet();
+
public void errorMissingClassMissingNestHost(DexClass compiledClass) {
throw reporter.fatalError(messageErrorMissingNestHost(compiledClass));
}
@@ -844,6 +849,26 @@
}
}
+ public void warningInvalidLibrarySuperclassForDesugar(
+ Origin origin,
+ DexType libraryType,
+ DexType invalidSuperType,
+ String message,
+ Set<DexMethod> retarget,
+ AppView<?> appView) {
+ if (invalidLibraryClasses.add(invalidSuperType)) {
+ reporter.warning(
+ new InvalidLibrarySuperclassDiagnostic(
+ origin,
+ Reference.classFromDescriptor(libraryType.toDescriptorString()),
+ Reference.classFromDescriptor(invalidSuperType.toDescriptorString()),
+ message,
+ retarget.stream()
+ .map(method -> method.asMethodReference(appView))
+ .collect(Collectors.toList())));
+ }
+ }
+
public void warningMissingEnclosingMember(DexType clazz, Origin origin, int version) {
TypeVersionPair pair = new TypeVersionPair(version, clazz);
synchronized (missingEnclosingMembers) {
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 6bce9c1..9c18d3a 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -185,7 +185,7 @@
if (parsedData != null || parsedKotlinSourceDebugExtensions.containsKey(holder)) {
return parsedData;
}
- DexClass clazz = appView.definitionFor(currentMethod.method.holder);
+ DexClass clazz = appView.definitionFor(currentMethod.holder());
DexValueString dexValueString = appView.getSourceDebugExtensionForType(clazz);
if (dexValueString != null) {
parsedData = KotlinSourceDebugExtensionParser.parse(dexValueString.value.toString());
@@ -456,7 +456,7 @@
continue;
}
// We use the same name for interface names even if it has different types.
- DexProgramClass clazz = appView.definitionForProgramType(method.method.holder);
+ DexProgramClass clazz = appView.definitionForProgramType(method.holder());
DexClassAndMethod lookupResult =
appView.appInfo().lookupMaximallySpecificMethod(clazz, method.method);
if (lookupResult == null) {
@@ -783,7 +783,7 @@
}
method.setCode(
new CfCode(
- method.method.holder,
+ method.holder(),
oldCode.getMaxStack(),
oldCode.getMaxLocals(),
newInstructions,
diff --git a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
index 2c6fac0..880da5e 100644
--- a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
@@ -16,4 +16,8 @@
}
return null;
}
+
+ public static <T> Predicate<T> not(Predicate<T> predicate) {
+ return t -> !predicate.test(t);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 8c659b1..30f6489 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1000,7 +1000,9 @@
"lang.SecurityManager.checkMulticastLjava_net_InetAddress.SecurityManager_checkMulticast_A01",
anyDexVm())
.put("lang.SecurityManager.Constructor.SecurityManager_Constructor_A01", anyDexVm())
- .put("lang.SecurityManager.getClassContext.SecurityManager_getClassContext_A01", anyDexVm())
+ .put(
+ "lang.SecurityManager.getClassContext.SecurityManager_getClassContext_A01",
+ anyDexVm())
.put(
"lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A03",
anyDexVm())
@@ -1117,7 +1119,9 @@
.put(
"lang.SecurityManager.checkLinkLjava_lang_String.SecurityManager_checkLink_A02",
anyDexVm())
- .put("lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A01", anyDexVm())
+ .put(
+ "lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A01",
+ anyDexVm())
.put(
"lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A02",
anyDexVm())
@@ -1740,22 +1744,25 @@
.put("lang.reflect.Field.toGenericString.Field_toGenericString_A01", cf())
.build(); // end of failuresToTriage
-
public static final Multimap<String, TestCondition> bugs =
new ImmutableListMultimap.Builder<String, TestCondition>()
// The following StringBuffer/StringBuilder tests fails because we remove, e.g.,
// new StringBuffer(-5) if it is dead code (but it should trow), see b/133745205
- .put("lang.StringBuffer.ConstructorLjava_lang_String.StringBuffer_Constructor_A02",
+ .put(
+ "lang.StringBuffer.ConstructorLjava_lang_String.StringBuffer_Constructor_A02",
match(R8DEX_COMPILER))
- .put("lang.StringBuffer.ConstructorLjava_lang_CharSequence.StringBuffer_Constructor_A02",
+ .put(
+ "lang.StringBuffer.ConstructorLjava_lang_CharSequence.StringBuffer_Constructor_A02",
match(R8DEX_COMPILER))
- .put("lang.StringBuffer.ConstructorI.StringBuffer_Constructor_A02",
+ .put("lang.StringBuffer.ConstructorI.StringBuffer_Constructor_A02", match(R8DEX_COMPILER))
+ .put(
+ "lang.StringBuilder.ConstructorI.StringBuilder_Constructor_A02",
match(R8DEX_COMPILER))
- .put("lang.StringBuilder.ConstructorI.StringBuilder_Constructor_A02",
- match(R8DEX_COMPILER))
- .put("lang.StringBuilder.ConstructorLjava_lang_CharSequence.StringBuilder_Constructor_A02",
+ .put(
+ "lang.StringBuilder.ConstructorLjava_lang_CharSequence.StringBuilder_Constructor_A02",
match(R8DEX_COMPILER))
- .put("lang.StringBuilder.ConstructorLjava_lang_String.StringBuilder_Constructor_A02",
+ .put(
+ "lang.StringBuilder.ConstructorLjava_lang_String.StringBuilder_Constructor_A02",
match(R8DEX_COMPILER))
.build();
@@ -1814,9 +1821,11 @@
.put(
"util.concurrent.AbstractExecutorService.invokeAllLjava_util_CollectionJLjava_util_concurrent_TimeUnit.AbstractExecutorService_invokeAll_A06",
match(runtimes(Runtime.ART_V4_0_4)))
+ .put(
+ "util.concurrent.Executors.newCachedThreadPoolLjava_util_concurrent_ThreadFactory.Executors_newCachedThreadPool_A01",
+ anyDexVm())
.put("lang.ref.SoftReference.get.SoftReference_get_A01", cf())
.put("lang.ref.WeakReference.get.WeakReference_get_A01", cf())
-
.build(); // end of flakyWhenRun
public static final Multimap<String, TestCondition> timeoutsWhenRun =
diff --git a/src/test/java/com/android/tools/r8/NeverReprocessClassInitializer.java b/src/test/java/com/android/tools/r8/NeverReprocessClassInitializer.java
new file mode 100644
index 0000000..d0fbd19
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NeverReprocessClassInitializer.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface NeverReprocessClassInitializer {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 569c222..e28b4eb 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -53,6 +53,7 @@
private boolean enableMemberValuePropagationAnnotations = false;
private boolean enableMergeAnnotations = false;
private boolean enableNeverClassInliningAnnotations = false;
+ private boolean enableNeverReprocessClassInitializerAnnotations = false;
private boolean enableNeverReprocessMethodAnnotations = false;
private boolean enableReprocessClassInitializerAnnotations = false;
private boolean enableReprocessMethodAnnotations = false;
@@ -72,6 +73,7 @@
|| enableMemberValuePropagationAnnotations
|| enableMergeAnnotations
|| enableNeverClassInliningAnnotations
+ || enableNeverReprocessClassInitializerAnnotations
|| enableNeverReprocessMethodAnnotations
|| enableReprocessClassInitializerAnnotations
|| enableReprocessMethodAnnotations
@@ -407,6 +409,16 @@
return self();
}
+ public T enableNeverReprocessClassInitializerAnnotations() {
+ if (!enableNeverReprocessClassInitializerAnnotations) {
+ enableNeverReprocessClassInitializerAnnotations = true;
+ addInternalKeepRules(
+ "-neverreprocessclassinitializer @com.android.tools.r8.NeverReprocessClassInitializer"
+ + " class *");
+ }
+ return self();
+ }
+
public T enableReprocessMethodAnnotations() {
if (!enableReprocessMethodAnnotations) {
enableReprocessMethodAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
new file mode 100644
index 0000000..56cde8f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.annotations;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.retrace.KotlinInlineFunctionRetraceTest;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SourceDebugExtensionTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public SourceDebugExtensionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws IOException, CompilationFailedException, ExecutionException {
+ CfRuntime cfRuntime =
+ parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9();
+ Path kotlinSources =
+ kotlinc(cfRuntime, getStaticTemp(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addSourceFiles(
+ getFilesInTestFolderRelativeToClass(
+ KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+ .compile();
+ CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
+ inspectSourceDebugExtension(kotlinInspector);
+ testForR8(parameters.getBackend())
+ .addProgramFiles(kotlinSources)
+ .addKeepAttributes(ProguardKeepAttributes.SOURCE_DEBUG_EXTENSION)
+ .addKeepAllClassesRule()
+ .setMode(CompilationMode.RELEASE)
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M))
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString(
+ "Type `kotlin.jvm.internal.Intrinsics` was not found, it is required for default"
+ + " or static interface methods"))
+ .inspect(this::inspectSourceDebugExtension);
+ }
+
+ private void inspectSourceDebugExtension(CodeInspector inspector) {
+ ClassSubject clazz = inspector.clazz("retrace.InlineFunctionKt");
+ assertThat(clazz, isPresent());
+ AnnotationSubject sourceDebugExtensions =
+ clazz.annotation("dalvik.annotation.SourceDebugExtension");
+ assertThat(sourceDebugExtensions, isPresent());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 862bb79..3240d08 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -28,6 +28,7 @@
import java.nio.file.Paths;
import java.util.Calendar;
import java.util.List;
+import java.util.TreeSet;
public abstract class MethodGenerationBase extends TestBase {
@@ -102,8 +103,7 @@
if (method.isInitializer()) {
continue;
}
- String methodName =
- method.method.holder.getName() + "_" + method.method.name.toString();
+ String methodName = method.holder().getName() + "_" + method.method.name.toString();
codePrinter.visitMethod(methodName, method.getCode().asCfCode());
}
});
@@ -117,8 +117,15 @@
private void generateRawOutput(CfCodePrinter codePrinter, Path tempFile) throws IOException {
try (PrintStream printer = new PrintStream(Files.newOutputStream(tempFile))) {
printer.print(getHeaderString(this.getClass()));
+ printer.println("import com.android.tools.r8.graph.DexItemFactory;");
codePrinter.getImports().forEach(i -> printer.println("import " + i + ";"));
printer.println("public final class " + getGeneratedClassName() + " {\n");
+ printer.println(
+ "public static void registerSynthesizedCodeReferences(DexItemFactory factory) {");
+ for (String type : new TreeSet<>(codePrinter.getSynthesizedTypes())) {
+ printer.println("factory.createSynthesizedType(\"" + type + "\");");
+ }
+ printer.println("}");
codePrinter.getMethods().forEach(printer::println);
printer.println("}");
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java
new file mode 100644
index 0000000..64f7a72
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InvalidLibraryTest.java
@@ -0,0 +1,178 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.InvalidLibrarySuperclassDiagnostic;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.Date;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvalidLibraryTest extends DesugaredLibraryTestBase {
+
+ private static Path customLib;
+ private static Path superclassAsClasspath;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("1970-01-02T10:17:36.789Z", "1970-01-12T10:20:54.321123456Z");
+ private static final String INVALID_RESULT =
+ StringUtils.lines("1970-01-02T10:17:36.789Z", "1970-01-12T10:20:54.321Z");
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameterized.Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public InvalidLibraryTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ customLib =
+ testForD8(getStaticTemp())
+ .addProgramClasses(CustomLibraryClass.class)
+ .setMinApi(AndroidApiLevel.B)
+ .compile()
+ .writeToZip();
+ superclassAsClasspath =
+ testForD8(getStaticTemp())
+ .addProgramClasses(SuperLibraryClass.class)
+ .setMinApi(AndroidApiLevel.B)
+ .compile()
+ .writeToZip();
+ }
+
+ @Test
+ public void testProgramSupertype() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(
+ Executor.class, SuperLibraryClass.class, LocalClass.class, LocalClassOverride.class)
+ .addLibraryClasses(CustomLibraryClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(customLib)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testClasspathSupertype() throws Exception {
+ Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, LocalClass.class, LocalClassOverride.class)
+ .addClasspathClasses(SuperLibraryClass.class)
+ .addLibraryClasses(CustomLibraryClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspectDiagnosticMessages(this::assertWarningInvalidLibrary)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(customLib, superclassAsClasspath)
+ .run(parameters.getRuntime(), Executor.class)
+ // The code requires desugaring to be run correctly, but with the classpath superclass,
+ // desugaring is incorrectly performed. The code therefore falls-backs to the default
+ // implementation in Date, which happens to be correct in one case, but incorrect
+ // in the other case (Warning was raised).
+ .assertSuccessWithOutput(INVALID_RESULT);
+ }
+
+ @Test
+ public void testNullSupertype() throws Exception {
+ Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, LocalClass.class, LocalClassOverride.class)
+ .addLibraryClasses(CustomLibraryClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .inspectDiagnosticMessages(this::assertWarningInvalidLibrary)
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(customLib, superclassAsClasspath)
+ .run(parameters.getRuntime(), Executor.class)
+ // The code requires desugaring to be run correctly, but with the missing supertype,
+ // desugaring could not be performed and the code cannot simply run (Warning was raised).
+ .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"));
+ }
+
+ private void assertWarningInvalidLibrary(TestDiagnosticMessages testDiagnosticMessages) {
+ assert testDiagnosticMessages.getWarnings().stream()
+ .anyMatch(diagnostic -> diagnostic instanceof InvalidLibrarySuperclassDiagnostic);
+ }
+
+ static class Executor {
+ public static void main(String[] args) {
+ System.out.println(new LocalClass(123456789).toInstant());
+ System.out.println(getOverrideAsLocalClass().toInstant());
+ }
+
+ public static LocalClass getOverrideAsLocalClass() {
+ return new LocalClassOverride(987654321);
+ }
+ }
+
+ static class SuperLibraryClass extends Date {
+ public SuperLibraryClass(int nanos) {
+ super(nanos);
+ }
+ }
+
+ static class CustomLibraryClass extends SuperLibraryClass {
+ public CustomLibraryClass(int nanos) {
+ super(nanos);
+ }
+ }
+
+ static class LocalClass extends CustomLibraryClass {
+ public LocalClass(int nanos) {
+ super(nanos);
+ }
+ }
+
+ static class LocalClassOverride extends LocalClass {
+ public LocalClassOverride(int nanos) {
+ super(nanos);
+ }
+
+ @Override
+ public Instant toInstant() {
+ return super.toInstant().plusNanos(123456);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
deleted file mode 100644
index 403ae3c..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
-
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.fail;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestDiagnosticMessages;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import java.time.Year;
-import org.junit.Test;
-
-public class APIConversionFinalClassErrorTest extends DesugaredLibraryTestBase {
-
- @Test
- public void testFinalMethod() {
- try {
- testForD8()
- .setMinApi(AndroidApiLevel.B)
- .addProgramClasses(Executor.class)
- .addLibraryClasses(CustomLibClass.class)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
- .compileWithExpectedDiagnostics(this::assertDiagnosis);
- fail("Expected compilation error");
- } catch (CompilationFailedException ignored) {
-
- }
- }
-
- private void assertDiagnosis(TestDiagnosticMessages d) {
- assertEquals(
- "Cannot generate a wrapper for final class java.time.Year."
- + " Add a custom conversion in the desugared library.",
- d.getErrors().get(0).getDiagnosticMessage());
- }
-
- static class Executor {
-
- public static void main(String[] args) {
- System.out.println(CustomLibClass.call(Year.now()));
- }
- }
-
- // This class will be put at compilation time as library and on the runtime class path.
- // This class is convenient for easy testing. Each method plays the role of methods in the
- // platform APIs for which argument/return values need conversion.
- static class CustomLibClass {
-
- public static long call(Year year) {
- return 0L;
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
new file mode 100644
index 0000000..f9d1828
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.nio.file.Path;
+import java.time.Year;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class APIConversionFinalClassTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+
+ private static Path customLib;
+
+ @Parameterized.Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public APIConversionFinalClassTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ customLib =
+ testForD8(getStaticTemp())
+ .addProgramClasses(CustomLibClass.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
+ @Test
+ public void testFinalMethod() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .inspectDiagnosticMessages(this::assertDiagnosis)
+ .addRunClasspathFiles(customLib)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"));
+ }
+
+ private void assertDiagnosis(TestDiagnosticMessages d) {
+ assertTrue(d.getInfos().get(0).getDiagnosticMessage().contains("libCall"));
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ System.out.println(CustomLibClass.libCall(Year.now()));
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ static class CustomLibClass {
+
+ // We use Year because Year is a final class with no custom conversion but Year has been
+ // unused in the Android library so far.
+ public static long libCall(Year year) {
+ return 0L;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringImpossibleTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringImpossibleTest.java
new file mode 100644
index 0000000..88ede59
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringImpossibleTest.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugaring.interfacemethods;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DefaultInterfaceMethodDesugaringImpossibleTest extends TestBase {
+
+ private static final String EXPECTED = StringUtils.lines("I.m()");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DefaultInterfaceMethodDesugaringImpossibleTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private Collection<Class<?>> getProgramClasses() {
+ return ImmutableList.of(TestClass.class, I.class);
+ }
+
+ private Collection<byte[]> getProgramClassData() throws Exception {
+ return ImmutableList.of(
+ transformer(A.class)
+ .setAccessFlags(
+ A.class.getDeclaredMethod("m"),
+ flags -> {
+ assert flags.isPublic();
+ flags.unsetPublic();
+ flags.setPrivate();
+ flags.setStatic();
+ })
+ .transform());
+ }
+
+ @Test
+ public void testJVM() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassData())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ try {
+ testForD8()
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassData())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(this::checkDesugarError)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertTrue(parameters.canUseDefaultAndStaticInterfaceMethods());
+ } catch (CompilationFailedException e) {
+ assertFalse(parameters.canUseDefaultAndStaticInterfaceMethods());
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ try {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassData())
+ .addKeepAllClassesRule()
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(this::checkDesugarError)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertTrue(parameters.canUseDefaultAndStaticInterfaceMethods());
+ } catch (CompilationFailedException e) {
+ assertFalse(parameters.canUseDefaultAndStaticInterfaceMethods());
+ }
+ }
+
+ private void checkDesugarError(TestDiagnosticMessages diagnostics) {
+ if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ diagnostics.assertErrorMessageThatMatches(containsString("forwarding method that conflicts"));
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ I a = new A();
+ a.m();
+ }
+ }
+
+ interface I {
+
+ default void m() {
+ System.out.println("I.m()");
+ }
+ }
+
+ static class A implements I {
+
+ public /* will be: private static */ void m() {
+ System.out.println("A.m()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java
new file mode 100644
index 0000000..a8c5c6f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java
@@ -0,0 +1,174 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest
+ extends TestBase {
+
+ private static final String EXPECTED = StringUtils.lines("I.m()");
+
+ private final TestParameters parameters;
+ private final boolean invalidInvoke;
+
+ @Parameterized.Parameters(name = "{0}, invalid:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest(
+ TestParameters parameters, boolean invalidInvoke) {
+ this.parameters = parameters;
+ this.invalidInvoke = invalidInvoke;
+ }
+
+ private Collection<Class<?>> getProgramClasses() {
+ return ImmutableList.of(I.class, A.class, C.class);
+ }
+
+ private Collection<byte[]> getProgramClassData() throws Exception {
+ return ImmutableList.of(
+ transformer(B.class)
+ .setAccessFlags(
+ B.class.getDeclaredMethod("m"),
+ flags -> {
+ assert flags.isPublic();
+ flags.unsetPublic();
+ flags.setPrivate();
+ flags.setStatic();
+ })
+ .transform(),
+ transformer(TestClass.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ if (invalidInvoke && opcode == Opcodes.INVOKEVIRTUAL) {
+ assertEquals("m", name);
+ continuation.apply(
+ opcode,
+ DescriptorUtils.getBinaryNameFromJavaType(C.class.getTypeName()),
+ name,
+ descriptor,
+ isInterface);
+ } else {
+ continuation.apply(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform());
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ checkResult(
+ testForRuntime(parameters)
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassData())
+ .run(parameters.getRuntime(), TestClass.class));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ checkResult(
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassData())
+ .addKeepAllClassesRule()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class));
+ }
+
+ private void checkResult(TestRunResult<?> result) {
+ // Invalid invoke case is where the invoke-virtual targets C.m.
+ if (invalidInvoke) {
+ // Up to 4.4 the exception for targeting a private static was ICCE.
+ if (isDexOlderThanOrEqual(Version.V4_4_4)) {
+ result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ return;
+ }
+ // Then up to 6.0 the runtime just ignores privates leading to incorrectly hitting I.m
+ if (isDexOlderThanOrEqual(Version.V6_0_1)) {
+ result.assertSuccessWithOutput(EXPECTED);
+ return;
+ }
+ if (!unexpectedArtFailure() && !parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assert false : "Dead code until future ART behavior change. See b/152199517";
+ // Desugaring will insert a forwarding bridge which will hide the "invalid invoke" case.
+ // Thus, a future ART runtime that does not have the invalid IAE for the private override
+ // will end up calling the forward method to I.m.
+ result.assertSuccessWithOutput(EXPECTED);
+ }
+ // The expected behavior is IAE since the resolved method is private.
+ result.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ return;
+ }
+
+ // The non-invalid case is where the invoke-virtual targets A.m.
+
+ // In the successful case ART since 6.0 incorrectly throws IAE due to the private override.
+ if (unexpectedArtFailure()) {
+ result.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ return;
+ }
+
+ // The expected behavior is that the resolution of A.m will resolve and hit I.m.
+ result.assertSuccessWithOutput(EXPECTED);
+ }
+
+ private boolean isDexOlderThanOrEqual(Version version) {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().getVersion().isOlderThanOrEqual(version);
+ }
+
+ private boolean unexpectedArtFailure() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isNewerThan(DexVm.ART_6_0_1_HOST);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ // Same as DefaultInterfaceMethodDesugaringWithStaticResolutionTest, but targets a class A.
+ A /* or C */ a = new C();
+ a.m();
+ }
+ }
+
+ interface I {
+
+ default void m() {
+ System.out.println("I.m()");
+ }
+ }
+
+ static class A implements I {}
+
+ static class B extends A {
+
+ public /* will be: private static */ void m() {
+ System.out.println("B.m()");
+ }
+ }
+
+ static class C extends B implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java
index bdd7960..c649c8d 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionTest.java
@@ -1,13 +1,11 @@
package com.android.tools.r8.desugaring.interfacemethods;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -15,6 +13,8 @@
@RunWith(Parameterized.class)
public class DefaultInterfaceMethodDesugaringWithStaticResolutionTest extends TestBase {
+ private static final String EXPECTED = StringUtils.lines("I.m()");
+
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
@@ -32,43 +32,29 @@
testForJvm()
.addTestClasspath()
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("I.m()");
+ .assertSuccessWithOutput(EXPECTED);
}
@Test
public void testD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
- D8TestRunResult result =
- testForD8()
- .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class)
- .setMinApi(parameters.getApiLevel())
- .compile()
- .run(parameters.getRuntime(), TestClass.class);
- // TODO(b/152163087): Should always succeed with "I.m()".
- if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- result.assertSuccessWithOutputLines("I.m()");
- } else {
- result.assertFailureWithErrorThatMatches(
- containsString(AbstractMethodError.class.getTypeName()));
- }
+ testForD8()
+ .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
}
@Test
public void testR8() throws Exception {
- R8TestRunResult result =
- testForR8(parameters.getBackend())
- .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class)
- .addKeepAllClassesRule()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .run(parameters.getRuntime(), TestClass.class);
- // TODO(b/152163087): Should always succeed with "I.m()".
- if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- result.assertSuccessWithOutputLines("I.m()");
- } else {
- result.assertFailureWithErrorThatMatches(
- containsString(AbstractMethodError.class.getTypeName()));
- }
+ testForR8(parameters.getBackend())
+ .addInnerClasses(DefaultInterfaceMethodDesugaringWithStaticResolutionTest.class)
+ .addKeepAllClassesRule()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
new file mode 100644
index 0000000..b4e1ef6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfEnumUnboxingTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ValueOfEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private static final Class<?>[] SUCCESSES = {
+ EnumValueOf.class,
+ };
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final KeepRule enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public ValueOfEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ValueOfEnumUnboxingTest.class)
+ .addKeepMainRules(SUCCESSES)
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRule())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ for (Class<?> success : SUCCESSES) {
+ R8TestRunResult run =
+ compile
+ .inspectDiagnosticMessages(
+ m ->
+ assertEnumIsUnboxed(
+ success.getDeclaredClasses()[0], success.getSimpleName(), m))
+ .run(parameters.getRuntime(), success)
+ .assertSuccess();
+ assertLines2By2Correct(run.getStdOut());
+ }
+ }
+
+ static class EnumValueOf {
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B
+ }
+
+ public static void main(String[] args) {
+ System.out.println(Enum.valueOf(EnumValueOf.MyEnum.class, "A").ordinal());
+ System.out.println(0);
+ System.out.println(Enum.valueOf(EnumValueOf.MyEnum.class, "B").ordinal());
+ System.out.println(1);
+ try {
+ Enum.valueOf(EnumValueOf.MyEnum.class, "C");
+ } catch (IllegalArgumentException argException) {
+ System.out.println(argException.getMessage());
+ System.out.println(
+ "No enum constant"
+ + " com.android.tools.r8.enumunboxing.ValueOfEnumUnboxingTest.EnumValueOf.MyEnum.C");
+ }
+ try {
+ Enum.valueOf(EnumValueOf.MyEnum.class, null);
+ } catch (NullPointerException npe) {
+ System.out.println(npe.getMessage());
+ System.out.println("Name is null");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 33321eb..4d39642 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -8,16 +8,21 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignature.Parser;
import com.android.tools.r8.graph.GenericSignature.ReturnType;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.graph.GenericSignatureTestClassA.I;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -28,11 +33,24 @@
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
+import org.junit.Ignore;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class GenericSignatureTest extends TestBase {
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GenericSignatureTest(TestParameters parameters) {}
+
@Test
+ @Ignore("b/152709234")
public void test() throws Exception {
AndroidApp app =
testForD8()
@@ -62,6 +80,8 @@
assertThat(cy, isPresent());
ClassSubject cyy = inspector.clazz(GenericSignatureTestClassCYY.class);
assertThat(cyy, isPresent());
+ ClassSubject i = inspector.clazz(I.class);
+ assertThat(cyy, isPresent());
DexEncodedMethod method;
@@ -80,13 +100,22 @@
// Testing ClassSignature
//
- // class CYY<T extends A<T>.Y> extends CY<T>
+ // class <T:GenericSignatureTestClassA<T>.Y>CYY<T extends A<T>.Y> extends CY<T>
DexClass clazz = cyy.getDexClass();
assertNotNull(clazz);
classSignature = Parser.toClassSignature(clazz, appView);
assertNotNull(classSignature);
- // TODO(b/129925954): test formal type parameter of CYY
+ assertEquals(1, classSignature.formalTypeParameters.size());
+ FormalTypeParameter formalTypeParameter = classSignature.formalTypeParameters.get(0);
+ assertEquals("T", formalTypeParameter.name);
+ assertNull(formalTypeParameter.interfaceBounds);
+ assertTrue(formalTypeParameter.classBound.isClassTypeSignature());
+ ClassTypeSignature classBoundSignature = formalTypeParameter.classBound.asClassTypeSignature();
+ assertEquals(y.getDexClass().type, classBoundSignature.innerTypeSignature.type);
+ assertEquals(1, classBoundSignature.typeArguments.size());
+ assertEquals(
+ "T", classBoundSignature.typeArguments.get(0).asTypeVariableSignature().typeVariable);
assertTrue(classSignature.superInterfaceSignatures.isEmpty());
classTypeSignature = classSignature.superClassSignature;
@@ -126,6 +155,18 @@
methodTypeSignature = Parser.toMethodTypeSignature(method, appView);
assertNotNull(methodTypeSignature);
+ assertEquals(1, methodTypeSignature.formalTypeParameters.size());
+ FormalTypeParameter methodFormalParameter = methodTypeSignature.formalTypeParameters.get(0);
+ assertTrue(methodFormalParameter.classBound.isClassTypeSignature());
+ assertEquals(
+ y.getDexClass().getType(),
+ methodFormalParameter.classBound.asClassTypeSignature().innerTypeSignature.type);
+ assertNotNull(methodFormalParameter.interfaceBounds);
+ assertEquals(1, methodFormalParameter.interfaceBounds.size());
+ FieldTypeSignature interfaceBound = methodFormalParameter.interfaceBounds.get(0);
+ assertTrue(interfaceBound.isClassTypeSignature());
+ assertEquals(i.getDexClass().getType(), interfaceBound.asClassTypeSignature().type);
+
// return type: A$Y$YY
returnType = methodTypeSignature.returnType();
assertFalse(returnType.isVoidDescriptor());
@@ -257,6 +298,9 @@
//
class GenericSignatureTestClassA<T> {
+
+ interface I {}
+
class Y {
class YY {}
@@ -264,7 +308,7 @@
class ZZ<TT> extends YY {
public YY yy;
- YY newYY(GenericSignatureTestClassB... bs) {
+ <R extends I> YY newYY(GenericSignatureTestClassB... bs) {
return new YY();
}
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 07db6bd..2438ab2 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -83,12 +83,9 @@
DexEncodedMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x",
ImmutableList.of());
assertFalse(
- appInfo
- .resolveMethod(method.method.holder, method.method)
- .getSingleTarget()
- .isVirtualMethod());
- assertNull(appInfo.lookupDirectTarget(method.method, method.method.holder));
- assertNotNull(appInfo.lookupStaticTarget(method.method, method.method.holder));
+ appInfo.resolveMethod(method.holder(), method.method).getSingleTarget().isVirtualMethod());
+ assertNull(appInfo.lookupDirectTarget(method.method, method.holder()));
+ assertNotNull(appInfo.lookupStaticTarget(method.method, method.holder()));
if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(DexVm.Version.V4_4_4)) {
// Dalvik rejects at verification time instead of producing the
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 741be70..20ed6dc 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -94,7 +94,7 @@
.asLookupResultSuccess();
assertNotNull(lookupResult);
assertFalse(lookupResult.hasLambdaTargets());
- if (appInfo().subtypes(method.method.holder).stream()
+ if (appInfo().subtypes(method.holder()).stream()
.allMatch(t -> appInfo().definitionFor(t).isInterface())) {
Counter counter = new Counter();
lookupResult.forEach(
diff --git a/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java b/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
index 0d31dca..f191b3b 100644
--- a/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
@@ -153,7 +153,7 @@
IRCode code = simpleCode();
InstructionListIterator it = new LinearFlowInstructionListIterator(code, code.blocks.get(1));
Instruction current = it.previous();
- assertTrue(current.isConstNumber() && current.outValue().getType().isReferenceType());
+ assertTrue(current.isConstNumber() && current.getOutType().isReferenceType());
it.next();
current = it.next();
assertTrue(current.isArrayGet());
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
index a644df9..bdeda3c 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/ArrayTypeTest.java
@@ -104,7 +104,7 @@
code,
instruction ->
instruction.isConstNumber() && instruction.asConstNumber().getRawValue() != 0);
- assertEquals(getFloat(), constNumberInstruction.outValue().getType());
+ assertEquals(getFloat(), constNumberInstruction.getOutType());
}
};
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java
index e3b40cb..e421309 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/ConstrainedPrimitiveTypeTest.java
@@ -98,7 +98,7 @@
for (Instruction instruction : code.instructions()) {
if (instruction.isConstNumber()) {
ConstNumber constNumberInstruction = instruction.asConstNumber();
- assertEquals(expectedType, constNumberInstruction.outValue().getType());
+ assertEquals(expectedType, constNumberInstruction.getOutType());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java
index 0f40b4b..9fb2f9b 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeConstraintOnTrivialPhiTest.java
@@ -111,7 +111,7 @@
private static Consumer<IRCode> testInspector(TypeElement expectedType) {
return code -> {
ConstNumber constNumberInstruction = getMatchingInstruction(code, Instruction::isConstNumber);
- assertEquals(expectedType, constNumberInstruction.outValue().getType());
+ assertEquals(expectedType, constNumberInstruction.getOutType());
};
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java
index f6ad83a..961827a 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/UnconstrainedPrimitiveTypeTest.java
@@ -116,7 +116,7 @@
for (Instruction instruction : code.instructions()) {
if (instruction.isConstNumber()) {
ConstNumber constNumberInstruction = instruction.asConstNumber();
- assertEquals(expectedType, constNumberInstruction.outValue().getType());
+ assertEquals(expectedType, constNumberInstruction.getOutType());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java
index 2a2ad92..b5997bf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java
@@ -43,8 +43,8 @@
private void callSiteOptimizationInfoInspect(DexEncodedMethod encodedMethod) {
// TODO(b/139246447): should avoid visiting A#<init>, which is trivial, default init!
- assert encodedMethod.method.holder.toSourceString().endsWith("A")
- && encodedMethod.toSourceString().contains("<init>")
+ assert encodedMethod.holder().toSourceString().endsWith("A")
+ && encodedMethod.toSourceString().contains("<init>")
: "Unexpected revisit: " + encodedMethod.toSourceString();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
index 4890c6a..54a3b8e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
@@ -64,7 +64,7 @@
assert encodedMethod.method.name.toString().equals("m")
: "Unexpected revisit: " + encodedMethod.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo();
- if (encodedMethod.method.holder.toSourceString().endsWith("$C")) {
+ if (encodedMethod.holder().toSourceString().endsWith("$C")) {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
} else {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
index a0a436d..96b85ec 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -61,7 +61,7 @@
assert encodedMethod.method.name.toString().equals("m")
: "Unexpected revisit: " + encodedMethod.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo();
- if (encodedMethod.method.holder.toSourceString().endsWith("$C")) {
+ if (encodedMethod.holder().toSourceString().endsWith("$C")) {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
} else {
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
index ab5d763..14ff937 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -65,7 +65,7 @@
CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo();
assert callSiteOptimizationInfo.getDynamicUpperBoundType(1).isDefinitelyNotNull();
AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(1);
- if (encodedMethod.method.holder.toSourceString().endsWith("$A")) {
+ if (encodedMethod.holder().toSourceString().endsWith("$A")) {
assert abstractValue.isSingleStringValue()
&& abstractValue.asSingleStringValue().getDexString().toString().equals("nul");
} else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
index 8cc1e12..204f41b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -66,7 +66,7 @@
CallSiteOptimizationInfo callSiteOptimizationInfo = encodedMethod.getCallSiteOptimizationInfo();
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isDefinitelyNotNull();
- if (encodedMethod.method.holder.toSourceString().endsWith("$A")) {
+ if (encodedMethod.holder().toSourceString().endsWith("$A")) {
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$Sub1");
} else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
index ad7b3ae..4882c32 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -66,7 +66,7 @@
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isNullable();
assert upperBoundType.isClassType()
- && upperBoundType.asClassType().getClassType().equals(encodedMethod.method.holder);
+ && upperBoundType.asClassType().getClassType().equals(encodedMethod.holder());
} else {
assert methodName.equals("test");
assert callSiteOptimizationInfo.getDynamicUpperBoundType(0).isDefinitelyNotNull();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
index 0551286..0009024 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -64,10 +64,10 @@
TypeElement upperBoundType = callSiteOptimizationInfo.getDynamicUpperBoundType(1);
assert upperBoundType.isClassType()
&& upperBoundType.asClassType().getClassType().toSourceString().endsWith("$A");
- if (encodedMethod.method.holder.toSourceString().endsWith("$A")) {
+ if (encodedMethod.holder().toSourceString().endsWith("$A")) {
assert upperBoundType.isDefinitelyNotNull();
} else {
- assert encodedMethod.method.holder.toSourceString().endsWith("$B");
+ assert encodedMethod.holder().toSourceString().endsWith("$B");
assert upperBoundType.isNullable();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFieldLoadEliminationMeetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFieldLoadEliminationMeetTest.java
new file mode 100644
index 0000000..776df6b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFieldLoadEliminationMeetTest.java
@@ -0,0 +1,64 @@
+package com.android.tools.r8.ir.optimize.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.PrintStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RedundantFieldLoadEliminationMeetTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public RedundantFieldLoadEliminationMeetTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(RedundantFieldLoadEliminationMeetTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ MethodSubject mainMethodSubject = testClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertEquals(
+ 1, mainMethodSubject.streamInstructions().filter(InstructionSubject::isStaticGet).count());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ boolean unknown = System.currentTimeMillis() > 0;
+ PrintStream out = System.out;
+ String message = unknown ? "Hello world!" : "Unexpected";
+ System.out.println(message);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
new file mode 100644
index 0000000..329e574
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
@@ -0,0 +1,147 @@
+package com.android.tools.r8.ir.optimize.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.redundantfieldloadelimination.RedundantFinalStaticFieldLoadAfterStoreTest.A;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RedundantFinalInstanceFieldLoadAfterStoreTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public RedundantFinalInstanceFieldLoadAfterStoreTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(RedundantFinalInstanceFieldLoadAfterStoreTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("0", "42", "42", "42");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ FieldSubject fFieldSubject = aClassSubject.uniqueFieldWithName("f");
+ assertThat(fFieldSubject, isPresent());
+
+ MethodSubject initMethodSubject = aClassSubject.init();
+ assertThat(initMethodSubject, isPresent());
+ assertEquals(
+ 0,
+ countInstanceGetInstructions(
+ initMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
+
+ MethodSubject mMethodSubject = aClassSubject.uniqueMethodWithName("m");
+ assertThat(mMethodSubject, isPresent());
+ assertEquals(
+ 2,
+ countInstanceGetInstructions(
+ mMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
+ }
+
+ private long countInstanceGetInstructions(
+ FoundMethodSubject methodSubject, FoundFieldSubject fieldSubject) {
+ return methodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInstanceGet)
+ .map(InstructionSubject::getField)
+ .filter(fieldSubject.getField().field::equals)
+ .count();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new A();
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ @NeverPropagateValue final long f;
+
+ static volatile boolean read;
+ static volatile boolean initialized;
+
+ A() {
+ fork();
+ waitUntilRead();
+ f = System.currentTimeMillis() > 0 ? 42 : 0;
+ initialized = true;
+ killNonFinalActiveFields();
+ System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized.
+ killNonFinalActiveFields();
+ System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized.
+ }
+
+ @NeverInline
+ void m() {
+ System.out.println(f);
+ read = true;
+ waitUntilInitialized();
+ System.out.println(f); // Not redundant, since `f` is not guaranteed to be initialized.
+ }
+
+ @NeverInline
+ void fork() {
+ new Thread(this::m).start();
+ }
+
+ @NeverInline
+ void killNonFinalActiveFields() {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(this);
+ }
+ }
+
+ @NeverInline
+ void waitUntilInitialized() {
+ while (!initialized) {
+ Thread.yield();
+ }
+ }
+
+ @NeverInline
+ void waitUntilRead() {
+ while (!read) {
+ Thread.yield();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
new file mode 100644
index 0000000..fe3d79a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
@@ -0,0 +1,118 @@
+package com.android.tools.r8.ir.optimize.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RedundantFinalStaticFieldLoadAfterStoreTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public RedundantFinalStaticFieldLoadAfterStoreTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(RedundantFinalStaticFieldLoadAfterStoreTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42", "42", "42", "42");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ FieldSubject fFieldSubject = aClassSubject.uniqueFieldWithName("f");
+ assertThat(fFieldSubject, isPresent());
+
+ MethodSubject initMethodSubject = aClassSubject.clinit();
+ assertThat(initMethodSubject, isPresent());
+ assertEquals(
+ 0,
+ countStaticGetInstructions(
+ initMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
+
+ MethodSubject mMethodSubject = aClassSubject.uniqueMethodWithName("m");
+ assertThat(mMethodSubject, isPresent());
+ // TODO(b/152196923): Should be 0.
+ assertEquals(
+ 2,
+ countStaticGetInstructions(
+ mMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
+ }
+
+ private long countStaticGetInstructions(
+ FoundMethodSubject methodSubject, FoundFieldSubject fieldSubject) {
+ return methodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(InstructionSubject::getField)
+ .filter(fieldSubject.getField().field::equals)
+ .count();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ A.m();
+ }
+ }
+
+ static class A {
+
+ @NeverPropagateValue static final long f;
+
+ static {
+ f = System.currentTimeMillis() > 0 ? 42 : 0;
+ killNonFinalActiveFields();
+ System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized.
+ killNonFinalActiveFields();
+ System.out.println(f); // Redundant, since `f` is final and guaranteed to be initialized.
+ }
+
+ @NeverInline
+ static void m() {
+ System.out.println(A.f);
+ killNonFinalActiveFields();
+ System.out.println(A.f); // Redundant, since `f` is guaranteed to be initialized.
+ }
+
+ @NeverInline
+ static void killNonFinalActiveFields() {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(A.class);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java
new file mode 100644
index 0000000..74dc3f4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java
@@ -0,0 +1,85 @@
+package com.android.tools.r8.ir.optimize.staticizer;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CompanionClassWithNewInstanceUserTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public CompanionClassWithNewInstanceUserTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(CompanionClassWithNewInstanceUserTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ if (parameters.isCfRuntime()) {
+ // Class staticizer is disabled when generating class files.
+ assertThat(inspector.clazz(Companion.class), isPresent());
+ } else {
+ // The companion class has been removed.
+ assertThat(inspector.clazz(Companion.class), not(isPresent()));
+
+ // The companion method has been moved to the companion host class.
+ ClassSubject hostClassSubject = inspector.clazz(CompanionHost.class);
+ assertThat(hostClassSubject, isPresent());
+ assertThat(hostClassSubject.uniqueMethodWithName("method"), isPresent());
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Companion companion = CompanionHost.COMPANION;
+ }
+ }
+
+ static class CompanionHost {
+
+ static final Companion COMPANION;
+
+ static {
+ Companion companion = new Companion();
+ COMPANION = companion;
+ companion.method();
+ }
+ }
+
+ static class Companion {
+
+ @NeverInline
+ public void method() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderTest.java
new file mode 100644
index 0000000..0dcda2d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderTest.java
@@ -0,0 +1,60 @@
+package com.android.tools.r8.ir.optimize.string;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class UnusedStringBuilderTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public UnusedStringBuilderTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(UnusedStringBuilderTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ MethodSubject methodSubject = testClassSubject.mainMethod();
+ assertThat(methodSubject, isPresent());
+ assertTrue(methodSubject.streamInstructions().allMatch(InstructionSubject::isReturnVoid));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ String.valueOf(new StringBuilder().append("x=").append(42));
+ new StringBuilder().append("x=").append(42).toString();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index 6087795..6fb984f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -53,7 +53,6 @@
// The Util class is there, but its instance methods have been inlined.
ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
assertThat(utilClass, isPresent());
- AtomicInteger nonStaticMethodCount = new AtomicInteger();
assertTrue(
utilClass.allMethods().stream()
.filter(Predicates.not(FoundMethodSubject::isStatic))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index 7514ff5..9d75aaa 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -36,7 +36,12 @@
public class MetadataRewriteInCompanionTest extends KotlinMetadataTestBase {
private static final String EXPECTED =
StringUtils.lines(
- "B.Companion::foo", "B.Companion::foo", "B.Companion::foo", "B.Companion::foo");
+ "B.Companion::foo",
+ "B.Companion::foo",
+ "B.Companion::foo",
+ "B.Companion::foo",
+ "B.Companion::bar",
+ "Hello World!");
private final TestParameters parameters;
@@ -125,8 +130,8 @@
// Property in companion with @JvmField is defined in the host class, without accessors.
.addKeepRules("-keepclassmembers class **.B { *** elt2; }")
.addKeepRules("-keep class **.I { <methods>; }")
- // Keep getters for B$Companion.(eltN|foo) which will be referenced at the app.
- .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }")
+ // Keep getters/setters for B$Companion.(eltN|foo) which will be referenced at the app.
+ .addKeepRules("-keepclassmembers class **.B$* { *** get*(...); *** set*(...); }")
// Keep the companion instance in the B class
.addKeepRules("-keepclassmembers class **.B { *** Companion; }")
// Keep the name of companion class
@@ -239,5 +244,9 @@
MethodSubject fooGetter = companion.uniqueMethodWithName("getFoo");
assertThat(fooGetter, isPresent());
assertThat(fooGetter, not(isRenamed()));
+
+ MethodSubject barSetter = companion.uniqueMethodWithName("setBar");
+ assertThat(barSetter, isPresent());
+ assertThat(barSetter, not(isRenamed()));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index 07fbdd9..3d3d935 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -5,13 +5,11 @@
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.util.Collection;
@@ -25,7 +23,28 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInTypeArgumentsTest extends KotlinMetadataTestBase {
private static final String EXPECTED =
- StringUtils.lines("42", "1", "42", "42", "1", "42", "42", "42", "1", "42");
+ StringUtils.lines(
+ "Hello World!",
+ "42",
+ "1",
+ "42",
+ "42",
+ "1",
+ "42",
+ "42",
+ "42",
+ "1",
+ "42",
+ "1",
+ "42",
+ "42",
+ "42",
+ "42",
+ "42",
+ "1",
+ "2",
+ "7",
+ "42");
private final TestParameters parameters;
@@ -50,6 +69,7 @@
Path typeAliasLibJar =
kotlinc(KOTLINC, targetVersion)
.addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib"))
+ .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib_minified"))
.compile();
jarMap.put(targetVersion, typeAliasLibJar);
}
@@ -74,26 +94,37 @@
}
@Test
- public void testMetadataInTypeAlias_renamed() throws Exception {
+ public void testMetadataInTypeAlias_keepAll() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(jarMap.get(targetVersion))
.addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
- // TODO(b/151925520): Add inspections when program compiles
+ // TODO(b/151925520): Add inspections when program compiles correctly.
+ // TODO(mkroghj): Also inspect the renaming of lib_minified
+ // (not now, but when program compiles correctly).
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path mainJar =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
- .setOutputPath(temp.newFolder().toPath())
- // TODO(b/151925520): update to just .compile() once fixed.
- .compileRaw();
- // TODO(b/151925520): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(
- kotlinTestCompileResult.stderr,
- containsString("no type arguments expected for constructor Invariant()"));
+ .compile();
+
+ // TODO(b/152306391): Reified type-parameters are not flagged correctly.
+ testForJvm()
+ .addProgramFiles(mainJar)
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addRunClasspathFiles(libJar)
+ .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt")
+ .assertFailureWithErrorThatMatches(
+ containsString(
+ "This function has a reified type parameter and thus can only be inlined at"
+ + " compilation time, not called directly"));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt
index 403021e..ef4e23f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_app/main.kt
@@ -10,4 +10,7 @@
B.elt1.doStuff()
B.elt2.doStuff()
println(B.foo)
+ println(B.bar)
+ B.bar = "Hello World!";
+ println(B.bar)
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
index 8e69219..54db448 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
@@ -24,5 +24,10 @@
val elt2: Super = B()
val foo: String
get() = "B.Companion::foo"
+ var bar : String = "B.Companion::bar"
+ get() = field
+ set(value) {
+ field = value
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
index 7fc2b69..c8f4a72 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
@@ -8,7 +8,10 @@
import com.android.tools.r8.kotlin.metadata.typeargument_lib.ContraVariant
import com.android.tools.r8.kotlin.metadata.typeargument_lib.Invariant
import com.android.tools.r8.kotlin.metadata.typeargument_lib.SomeClass
+import com.android.tools.r8.kotlin.metadata.typeargument_lib.asList
+import com.android.tools.r8.kotlin.metadata.typeargument_lib.asObfuscatedClass
import com.android.tools.r8.kotlin.metadata.typeargument_lib.unBoxAndBox
+import com.android.tools.r8.kotlin.metadata.typeargument_lib.unboxAndPutInBox
import com.android.tools.r8.kotlin.metadata.typeargument_lib.update
class SomeSubClass(val x : Int) : SomeClass(), Comparable<SomeClass> {
@@ -24,7 +27,7 @@
fun testInvariant() {
val subtype1 = SomeSubClass(42)
val subtype2 = SomeSubClass(1)
- val inv = Invariant<SomeSubClass>()
+ val inv = Invariant<SomeSubClass, String>("Hello World!")
println(inv.classGenerics(subtype1).x)
println(inv.funGenerics(subtype2).x)
println(inv.funGenericsWithUpperBound(subtype1).x)
@@ -43,6 +46,12 @@
fun testExtension() {
println(CoVariant(42).unBoxAndBox().t)
println(CoVariant(1).update(42).t)
+ println(CoVariant(1).unboxAndPutInBox(CoVariant(42)).t)
+ println(CoVariant(42).asList().t.get(0))
+ val asList = CoVariant(42).asList(1, 2)
+ println(asList.t.get(0))
+ println(asList.t.get(1))
+ println(CoVariant(7).asObfuscatedClass().t.get(0).get(0).x)
}
fun main() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
index 7c8b6cb..38234b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
@@ -6,7 +6,11 @@
open class SomeClass
-class Invariant<T> {
+class Invariant<T, C> {
+
+ constructor(someValue : C) {
+ println(someValue)
+ }
fun classGenerics(t : T) : T {
return t
@@ -52,7 +56,6 @@
}
}
-
fun <T> CoVariant<T>.unBoxAndBox() : CoVariant<T> {
return CoVariant(this.t)
}
@@ -61,3 +64,24 @@
println(this.t)
return CoVariant(t)
}
+
+fun <T> CoVariant<T>.unboxAndPutInBox(box : CoVariant<T>) : CoVariant<T> {
+ println(this.t)
+ println(box.t)
+ return CoVariant(box.t)
+}
+
+inline fun <reified T> CoVariant<T>.asList() : CoVariant<Array<T>> {
+ println(this.t)
+ return CoVariant(arrayOf(this.t))
+}
+
+inline fun <reified T> CoVariant<T>.asList(vararg ts : T) : CoVariant<Array<out T>> {
+ println(this.t)
+ return CoVariant(ts)
+}
+
+fun <T> CoVariant<T>.asObfuscatedClass() : CoVariant<Array<Array<ClassThatWillBeObfuscated>>> {
+ println(this.t)
+ return CoVariant(arrayOf(arrayOf(ClassThatWillBeObfuscated(42))))
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt
new file mode 100644
index 0000000..78c3742
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt
@@ -0,0 +1,3 @@
+package com.android.tools.r8.kotlin.metadata.typeargument_lib
+
+class ClassThatWillBeObfuscated(val x : Int)
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java
index e081a44..b1ae543 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingConflictTest.java
@@ -4,11 +4,8 @@
package com.android.tools.r8.memberrebinding;
-import static org.hamcrest.core.StringContains.containsString;
-
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -35,36 +32,28 @@
@Test
public void test() throws Exception {
- R8TestRunResult result =
- testForR8(parameters.getBackend())
- .addProgramClasses(A.class, TestClass.class)
- .addProgramClassFileData(
- transformer(B.class)
- .removeMethods(
- (access, name, descriptor, signature, exceptions) -> {
- if (name.equals("foo")) {
- assert MethodAccessFlags.fromCfAccessFlags(access, false).isSynthetic();
- return true;
- }
- return false;
- })
- .transform())
- .addInnerClasses(MemberRebindingConflictTestClasses.class)
- .addKeepMainRule(TestClass.class)
- .enableInliningAnnotations()
- .enableMergeAnnotations()
- .enableNeverClassInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .run(parameters.getRuntime(), TestClass.class);
-
- if (parameters.isDexRuntime()
- && parameters.getRuntime().asDex().getVm().getVersion().isDalvik()) {
- result.assertSuccessWithOutputLines("foo", "bar", "foo", "baz");
- } else {
- result.assertFailureWithErrorThatMatches(
- containsString(IllegalAccessError.class.getTypeName()));
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, TestClass.class)
+ .addProgramClassFileData(
+ transformer(B.class)
+ .removeMethods(
+ (access, name, descriptor, signature, exceptions) -> {
+ if (name.equals("foo")) {
+ assert MethodAccessFlags.fromCfAccessFlags(access, false).isSynthetic();
+ return true;
+ }
+ return false;
+ })
+ .transform())
+ .addInnerClasses(MemberRebindingConflictTestClasses.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("foo", "bar", "foo", "baz");
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java b/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
index 32c754e..0556439 100644
--- a/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
+++ b/src/test/java/com/android/tools/r8/regress/b111250398/B111250398.java
@@ -276,12 +276,8 @@
// compilation (R8) will eliminate field loads on non-volatile fields.
assertEquals(1, countIget(mfOnA.getMethod().getCode().asDexCode(), fOnA.getField().field));
assertEquals(1, countSget(msfOnA.getMethod().getCode().asDexCode(), sfOnA.getField().field));
- // TODO(111380066). This could be 2 in stead of 4, but right now the optimization tracks the
- // combined set of fields for all successors, and for synchronized code all blocks have
- // exceptional edges for ensuring monitor exit causing the active load to be invalidated for
- // both normal and exceptional successors.
- assertEquals(4,
- countIget(mfWithMonitorOnA.getMethod().getCode().asDexCode(), fOnA.getField().field));
+ assertEquals(
+ 2, countIget(mfWithMonitorOnA.getMethod().getCode().asDexCode(), fOnA.getField().field));
// For fields on other class both separate compilation (D8) and whole program
// compilation (R8) will differ in the eliminated field loads of non-volatile fields.
diff --git a/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java b/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java
new file mode 100644
index 0000000..7c8aa34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.regress.b151964517;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConstStringWithMonitorTest extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().build();
+ }
+
+ public ConstStringWithMonitorTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void regress() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ConstStringWithMonitorTest.class)
+ .noMinification()
+ .allowAccessModification()
+ .addKeepMainRule(TestClass.class)
+ .compile()
+ .runDex2Oat(parameters.getRuntime());
+ // TODO(b/151964517): Should pass verification with assertNoVerificationErrors()
+ }
+
+ public static class TestClass {
+ public static void main(String[] args) {
+ try {
+ System.out.println(FooBar.tryIt());
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static class Value {
+ public final String value;
+
+ public Value(String value) {
+ this.value = value;
+ }
+ }
+
+ public static class FooBar {
+ private static Object mLock = new Object();
+
+ public static String tryIt() {
+ Value value = synchronizedMethod("foobar");
+ return value.value;
+ }
+
+ private static Value synchronizedMethod(String s) {
+ synchronized (mLock) {
+ if (System.currentTimeMillis() < 2) {
+ s.length();
+ throw new IllegalStateException("wrong" + s);
+ }
+ return new Value(s);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 0d89583..7bd9cc9 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -186,8 +186,7 @@
Assert.assertNull(singleVirtualTarget);
} else {
Assert.assertNotNull(singleVirtualTarget);
- Assert.assertEquals(
- toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.method.holder);
+ Assert.assertEquals(toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.holder());
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index a9dc70f..18cbb66 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -133,7 +133,7 @@
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
- assertEquals(definingClassDefinition.type, targetSpecial.method.holder);
+ assertEquals(definingClassDefinition.type, targetSpecial.holder());
assertEquals(targetSpecial, targetSuper);
} else {
assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 490b7dc..58575d8 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -109,7 +109,7 @@
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
- assertEquals(definingClassDefinition.type, targetSpecial.method.holder);
+ assertEquals(definingClassDefinition.type, targetSpecial.holder());
assertEquals(targetSpecial, targetSuper);
} else {
assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index 8e21be0..79e8f76 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -142,7 +142,7 @@
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest && symbolicReferenceIsDefiningType) {
- assertEquals(definingClassDefinition.type, targetSpecial.method.holder);
+ assertEquals(definingClassDefinition.type, targetSpecial.holder());
assertEquals(targetSpecial, targetSuper);
} else {
assertNull(targetSpecial);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index 49426c4..db75b6e 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -108,7 +108,7 @@
// Verify that looking up the dispatch target returns the defining method.
DexEncodedMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
- assertEquals(definingClassDefinition.type, targetSpecial.method.holder);
+ assertEquals(definingClassDefinition.type, targetSpecial.holder());
DexEncodedMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
index 005f292..b6023ce 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
@@ -48,7 +48,7 @@
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
// Currently R8 will resolve to L::f as that is the first in the topological search.
// Resolution may return any of the matches, so it is valid if this expectation changes.
- assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
index 0c3ed4c..2068ef1 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -51,7 +51,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
index 42f7600..64908c4 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -51,7 +51,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(R.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
index c29509d..4841f77 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -54,7 +54,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
index e7c5ab8..6059ea1 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -54,7 +54,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(R.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
index 3d7071d..433575f 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -55,8 +55,7 @@
Set<String> holders = new HashSet<>();
resolutionResult
.asFailedResolution()
- .forEachFailureDependency(
- target -> holders.add(target.method.holder.toSourceString()));
+ .forEachFailureDependency(target -> holders.add(target.holder().toSourceString()));
assertEquals(ImmutableSet.of(L.class.getTypeName(), R.class.getTypeName()), holders);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
index b5c2018..e04908b 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -45,7 +45,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(L.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(L.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
index 867cfe9..7472e89 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -45,7 +45,7 @@
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
- assertEquals(R.class.getTypeName(), resolutionTarget.method.holder.toSourceString());
+ assertEquals(R.class.getTypeName(), resolutionTarget.holder().toSourceString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
index b2a4ce2..5966b9d 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -56,8 +56,7 @@
Set<String> holders = new HashSet<>();
resolutionResult
.asFailedResolution()
- .forEachFailureDependency(
- m -> holders.add(m.method.holder.toSourceString()));
+ .forEachFailureDependency(m -> holders.add(m.holder().toSourceString()));
assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders);
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 91f0c4d..ae4947a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -85,6 +85,7 @@
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), 1);
+ assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), 1);
} else {
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -96,7 +97,6 @@
}
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
- assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi"));
}
@@ -129,6 +129,7 @@
assertNameReplacedWithConst(clazz.uniqueMethodWithName("inlined"), "TWO");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
+ assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), "TWO");
} else {
assertNameWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -141,7 +142,6 @@
// TODO(jakew) this should be allowed!
assertNameWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
- assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi"));
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
index 2ecc9eb..5dd5ec0 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
@@ -63,6 +63,7 @@
return Number.DOWN.name();
}
+ @AssumeMayHaveSideEffects
@NeverInline
private static String nonValueStaticField() {
return Number.DEFAULT.name();
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
index 534b16c..e1eee2c 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
@@ -73,6 +73,7 @@
return Number.DOWN.ordinal();
}
+ @AssumeMayHaveSideEffects
@NeverInline
private static long nonValueStaticField() {
return Number.DEFAULT.ordinal();
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java
new file mode 100644
index 0000000..fe72a94
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.assumenosideeffects;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B152492625 extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public B152492625(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void noCallToWait(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+ classSubject.forAllMethods(
+ foundMethodSubject ->
+ foundMethodSubject
+ .instructions(InstructionSubject::isInvokeVirtual)
+ .forEach(
+ instructionSubject -> {
+ Assert.assertNotEquals(
+ "wait", instructionSubject.getMethod().name.toString());
+ }));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(B152492625.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::noCallToWait)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
+ }
+
+ @Test
+ public void testProguardNotRemovingWait() throws Exception {
+ Assume.assumeTrue(parameters.isCfRuntime());
+
+ testForProguard()
+ .addInnerClasses(B152492625.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }")
+ .addKeepRules("-dontwarn " + B152492625.class.getTypeName())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(IllegalMonitorStateException.class);
+ }
+
+ @Test
+ public void testProguardRemovingWait() throws Exception {
+ Assume.assumeTrue(parameters.isCfRuntime());
+
+ testForProguard()
+ .addInnerClasses(B152492625.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-assumenosideeffects class java.lang.Object { void wait(); }")
+ .addKeepRules("-dontwarn " + B152492625.class.getTypeName())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::noCallToWait)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
+ }
+
+ static class TestClass {
+
+ public void m() throws Exception {
+ System.out.println("Hello, world");
+ // test fails if wait is not removed.
+ wait();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new TestClass().m();
+ }
+ }
+
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index 2387e7c..de762ac 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.StringUtils;
-import java.io.ByteArrayOutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
@@ -46,7 +45,6 @@
@Test
public void test() throws Throwable {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(Backend.CF)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 98f6b1e..3bbea86 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -102,8 +102,14 @@
@Override
public void describeMismatchSafely(final Subject subject, Description description) {
- description
- .appendText(type(subject) + " ").appendValue(name(subject)).appendText(" was not");
+ if (subject instanceof ClassSubject || subject instanceof MemberSubject) {
+ description
+ .appendText(type(subject) + " ")
+ .appendValue(name(subject))
+ .appendText(" was not");
+ } else {
+ description.appendText(type(subject) + " ").appendText(" was not found");
+ }
}
};
}
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 6d6f0fe..9182539 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -737,6 +737,8 @@
'-Pandroid.enableR8.fullMode=' + str(IsR8FullMode(shrinker)).lower()]
if app.has_lint_task:
args.extend(['-x', app_module + ':lintVital' + app_flavor])
+ if options.bot:
+ args.extend(['--console=plain', '--info'])
# Warm up gradle if pre_runs > 0. For posterity we generate the same sequence
# as the benchmarking at https://github.com/madsager/santa-tracker-android.
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index f3a0923..ee2b100 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -8,12 +8,8 @@
import os
from shutil import copyfile
import sys
-import tempfile
import utils
-import urllib
-
-BUILD_ROOT = "https://storage.googleapis.com/r8-releases/raw/"
-MASTER_BUILD_ROOT = "%smaster/" % BUILD_ROOT
+import archive
JAR_TARGETS_MAP = {
'full': [
@@ -66,8 +62,6 @@
print ('WARNING: Not copying ' + src + ' -> ' + dest +
', as' + dest + ' does not exist already')
-
-
def copy_jar_targets(root, target_root, jar_targets, maps):
srcs = map((lambda t: t[0] + '.jar'), jar_targets)
dests = map((lambda t: t[1] + '.jar'), jar_targets)
@@ -77,19 +71,19 @@
copy_targets(root, target_root, OTHER_TARGETS, OTHER_TARGETS)
def download_hash(root, commit_hash, target):
- url = MASTER_BUILD_ROOT + commit_hash + '/' + target
- download_target(root, url, target)
+ download_target(root, target, commit_hash, True)
def download_version(root, version, target):
- url = BUILD_ROOT + version + '/' + target
- download_target(root, url, target)
+ download_target(root, target, version, False)
-def download_target(root, url, target):
+def download_target(root, target, hash_or_version, is_hash):
download_path = os.path.join(root, target)
+ url = archive.GetUploadDestination(
+ hash_or_version,
+ target,
+ is_hash)
print 'Downloading: ' + url + ' -> ' + download_path
- result = urllib.urlretrieve(url, download_path)
- if 'X-GUploader-Request-Result: success' not in str(result[1]):
- raise IOError('Failed to download ' + url)
+ utils.download_file_from_cloud_storage(url, download_path)
def main_download(hash, maps, targets, target_root, version):
jar_targets = JAR_TARGETS_MAP[targets]