Introduce CfVersion to ensure correct comparisons.
Bug: 170796381
Bug: 169918924
Cherry picked commit: bdc905442f42e6d0107a4079d18f8d696335246c
Change-Id: I481afa535ca71147f056d0fb3d43f37e7ccb6a42
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 17b7485..e43f1da 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfThrow;
@@ -145,7 +146,7 @@
ParameterAnnotationsList.empty(),
code,
false,
- 50,
+ CfVersion.V1_6,
false);
if (method.isStatic() || method.isDirectMethod()) {
directMethods.add(throwingMethod);
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
new file mode 100644
index 0000000..84228b9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.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.cf;
+
+import java.util.Comparator;
+import org.objectweb.asm.Opcodes;
+
+public final class CfVersion implements Comparable<CfVersion> {
+
+ public static final CfVersion V1_1 = new CfVersion(Opcodes.V1_1);
+ public static final CfVersion V1_2 = new CfVersion(Opcodes.V1_2);
+ public static final CfVersion V1_3 = new CfVersion(Opcodes.V1_3);
+ public static final CfVersion V1_4 = new CfVersion(Opcodes.V1_4);
+ public static final CfVersion V1_5 = new CfVersion(Opcodes.V1_5);
+ public static final CfVersion V1_6 = new CfVersion(Opcodes.V1_6);
+ public static final CfVersion V1_7 = new CfVersion(Opcodes.V1_7);
+ public static final CfVersion V1_8 = new CfVersion(Opcodes.V1_8);
+ public static final CfVersion V9 = new CfVersion(Opcodes.V9);
+ public static final CfVersion V10 = new CfVersion(Opcodes.V10);
+ public static final CfVersion V11 = new CfVersion(Opcodes.V11);
+
+ private final int version;
+
+ // Private constructor in case we want to canonicalize versions.
+ private CfVersion(int version) {
+ this.version = version;
+ }
+
+ public static CfVersion fromRaw(int rawVersion) {
+ return new CfVersion(rawVersion);
+ }
+
+ public int major() {
+ return version & 0xFFFF;
+ }
+
+ public int minor() {
+ return version >> 16;
+ }
+
+ public int raw() {
+ return version;
+ }
+
+ public static CfVersion maxAllowNull(CfVersion v1, CfVersion v2) {
+ assert v1 != null || v2 != null;
+ if (v1 == null) {
+ return v2;
+ }
+ if (v2 == null) {
+ return v1;
+ }
+ return v1.max(v2);
+ }
+
+ public CfVersion max(CfVersion other) {
+ return isLessThan(other) ? other : this;
+ }
+
+ public boolean isEqual(CfVersion other) {
+ return version == other.version;
+ }
+
+ public boolean isLessThan(CfVersion other) {
+ return compareTo(other) < 0;
+ }
+
+ public boolean isLessThanOrEqual(CfVersion other) {
+ return compareTo(other) <= 0;
+ }
+
+ public boolean isGreaterThan(CfVersion other) {
+ return compareTo(other) > 0;
+ }
+
+ public boolean isGreaterThanOrEqual(CfVersion other) {
+ return compareTo(other) >= 0;
+ }
+
+ @Override
+ public int compareTo(CfVersion o) {
+ return Comparator.comparingInt(CfVersion::major)
+ .thenComparingInt(CfVersion::minor)
+ .compare(this, o);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CfVersion)) {
+ return false;
+ }
+ return isEqual((CfVersion) o);
+ }
+
+ @Override
+ public int hashCode() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ return minor() != 0 ? ("" + major() + "." + minor()) : ("" + major());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 77a82df..e3801e6 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.cf.CfVersion;
+
public class Constants {
public static final byte[] DEX_FILE_MAGIC_PREFIX = {'d', 'e', 'x', '\n'};
@@ -16,7 +18,7 @@
public static final int MAX_VDEX_VERSION = 11;
// We apply Java 6 class file constraints on DEX files.
- public static final int CORRESPONDING_CLASS_FILE_VERSION = 50;
+ public static final CfVersion CORRESPONDING_CLASS_FILE_VERSION = CfVersion.V1_6;
public static final int DEX_MAGIC_SIZE = 8;
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 de1c2b9..05dffcf 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -7,10 +7,9 @@
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
import static com.android.tools.r8.ir.conversion.CfSourceCode.canThrowHelper;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
-import static org.objectweb.asm.Opcodes.V1_5;
-import static org.objectweb.asm.Opcodes.V1_6;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfFrame.FrameType;
import com.android.tools.r8.cf.code.CfFrameVerificationHelper;
@@ -285,7 +284,7 @@
public void write(
ProgramMethod method,
- int classFileVersion,
+ CfVersion classFileVersion,
AppView<?> appView,
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
@@ -303,8 +302,9 @@
}
for (CfInstruction instruction : instructions) {
if (instruction instanceof CfFrame
- && (classFileVersion <= V1_5
- || (classFileVersion == V1_6 && !options.shouldKeepStackMapTable()))) {
+ && (classFileVersion.isLessThan(CfVersion.V1_6)
+ || (classFileVersion.isEqual(CfVersion.V1_6)
+ && !options.shouldKeepStackMapTable()))) {
continue;
}
instruction.write(
@@ -683,7 +683,7 @@
stackMapStatus = StackMapStatus.INVALID_OR_NOT_PRESENT;
return true;
}
- if (method.hasClassFileVersion() && method.getClassFileVersion() <= V1_6) {
+ if (method.hasClassFileVersion() && method.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
stackMapStatus = StackMapStatus.INVALID_OR_NOT_PRESENT;
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 36f43ba..e3584ec 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -105,16 +106,17 @@
* Checks whether the constraints from
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
*/
- public boolean areValid(int majorVersion, boolean isPackageInfo) {
+ public boolean areValid(CfVersion version, boolean isPackageInfo) {
if (isInterface()) {
// We ignore the super flags prior to JDK 9, as so did the VM.
- if ((majorVersion >= 53) && isSuper()) {
+ if (version.isGreaterThanOrEqual(CfVersion.V9) && isSuper()) {
return false;
}
// When not coming from DEX input we require interfaces to be abstract - except for
// package-info classes - as both old versions of javac and other tools can produce
// package-info classes that are interfaces but not abstract.
- if (((majorVersion > Constants.CORRESPONDING_CLASS_FILE_VERSION) && !isAbstract())
+ if (version.isGreaterThanOrEqual(Constants.CORRESPONDING_CLASS_FILE_VERSION)
+ && !isAbstract()
&& !isPackageInfo) {
return false;
}
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 c30f69c..1e628e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -12,6 +12,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
@@ -147,7 +148,7 @@
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
- private int classFileVersion;
+ private CfVersion classFileVersion = null;
private KotlinMethodLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
private DexEncodedMethod defaultInterfaceMethodImplementation = null;
@@ -228,7 +229,7 @@
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code) {
- this(method, accessFlags, annotations, parameterAnnotationsList, code, false, -1);
+ this(method, accessFlags, annotations, parameterAnnotationsList, code, false, null);
}
public DexEncodedMethod(
@@ -238,7 +239,7 @@
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized) {
- this(method, accessFlags, annotations, parameterAnnotationsList, code, d8R8Synthesized, -1);
+ this(method, accessFlags, annotations, parameterAnnotationsList, code, d8R8Synthesized, null);
}
public DexEncodedMethod(
@@ -248,7 +249,7 @@
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized,
- int classFileVersion) {
+ CfVersion classFileVersion) {
this(
method,
accessFlags,
@@ -267,7 +268,7 @@
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized,
- int classFileVersion,
+ CfVersion classFileVersion,
boolean deprecated) {
super(annotations);
this.method = method;
@@ -757,21 +758,21 @@
code = null;
}
- public int getClassFileVersion() {
+ public CfVersion getClassFileVersion() {
checkIfObsolete();
- assert classFileVersion >= 0;
+ assert classFileVersion != null;
return classFileVersion;
}
public boolean hasClassFileVersion() {
checkIfObsolete();
- return classFileVersion >= 0;
+ return classFileVersion != null;
}
- public void upgradeClassFileVersion(int version) {
+ public void upgradeClassFileVersion(CfVersion version) {
checkIfObsolete();
- assert version >= 0;
- classFileVersion = Math.max(classFileVersion, version);
+ assert version != null;
+ classFileVersion = CfVersion.maxAllowNull(classFileVersion, version);
}
public String qualifiedName() {
@@ -1394,7 +1395,7 @@
public void copyMetadata(DexEncodedMethod from) {
checkIfObsolete();
- if (from.classFileVersion > classFileVersion) {
+ if (from.hasClassFileVersion()) {
upgradeClassFileVersion(from.getClassFileVersion());
}
}
@@ -1418,7 +1419,7 @@
private CompilationState compilationState;
private MethodOptimizationInfo optimizationInfo;
private KotlinMethodLevelInfo kotlinMemberInfo;
- private final int classFileVersion;
+ private final CfVersion classFileVersion;
private boolean d8R8Synthesized;
private Builder(DexEncodedMethod from) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 13246eb..d94b060 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
@@ -47,7 +48,7 @@
private final ProgramResource.Kind originKind;
private final Collection<DexProgramClass> synthesizedFrom;
- private int initialClassFileVersion = -1;
+ private CfVersion initialClassFileVersion = null;
private boolean deprecated = false;
private KotlinClassLevelInfo kotlinInfo = NO_KOTLIN_INFO;
@@ -551,17 +552,18 @@
return this;
}
- public void setInitialClassFileVersion(int initialClassFileVersion) {
- assert this.initialClassFileVersion == -1 && initialClassFileVersion > 0;
+ public void setInitialClassFileVersion(CfVersion initialClassFileVersion) {
+ assert this.initialClassFileVersion == null;
+ assert initialClassFileVersion != null;
this.initialClassFileVersion = initialClassFileVersion;
}
public boolean hasClassFileVersion() {
- return initialClassFileVersion > -1;
+ return initialClassFileVersion != null;
}
- public int getInitialClassFileVersion() {
- assert initialClassFileVersion > -1;
+ public CfVersion getInitialClassFileVersion() {
+ assert initialClassFileVersion != null;
return initialClassFileVersion;
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index ea58f8d..fcc0870 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -8,12 +8,11 @@
import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
-import static org.objectweb.asm.Opcodes.V1_6;
-import static org.objectweb.asm.Opcodes.V9;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
@@ -195,7 +194,7 @@
private final ReparseContext context = new ReparseContext();
// DexClass data.
- private int version;
+ private CfVersion version;
private boolean deprecated;
private DexType type;
private ClassAccessFlags accessFlags;
@@ -234,7 +233,7 @@
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (outerName != null && innerName != null) {
String separator = DescriptorUtils.computeInnerClassSeparator(outerName, name, innerName);
- if (separator == null && getMajorVersion() < V9) {
+ if (separator == null && version.isLessThan(CfVersion.V9)) {
application.options.reporter.info(
new StringDiagnostic(
StringUtils.lines(
@@ -285,43 +284,54 @@
+ name;
}
- private String illegalClassFilePostfix(int version) {
+ private String illegalClassFilePostfix(CfVersion version) {
return "Class file version " + version;
}
private String illegalClassFileMessage(
- ClassAccessFlags accessFlags, String name, int version, String message) {
+ ClassAccessFlags accessFlags, String name, CfVersion version, String message) {
return illegalClassFilePrefix(accessFlags, name)
+ " " + message
+ ". " + illegalClassFilePostfix(version) + ".";
}
@Override
- public void visit(int version, int access, String name, String signature, String superName,
+ public void visit(
+ int rawVersion,
+ int access,
+ String name,
+ String signature,
+ String superName,
String[] interfaces) {
- this.version = version;
- if (InternalOptions.SUPPORTED_CF_MAJOR_VERSION < getMajorVersion()) {
- throw new CompilationError("Unsupported class file version: " + getMajorVersion(), origin);
+ version = CfVersion.fromRaw(rawVersion);
+ if (InternalOptions.SUPPORTED_CF_VERSION.isLessThan(version)) {
+ throw new CompilationError("Unsupported class file version: " + version, origin);
}
this.deprecated = AsmUtils.isDeprecated(access);
accessFlags = ClassAccessFlags.fromCfAccessFlags(cleanAccessFlags(access));
type = application.getTypeFromName(name);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
- if (!accessFlags.areValid(getMajorVersion(), name.endsWith("/package-info"))) {
+ if (!accessFlags.areValid(version, name.endsWith("/package-info"))) {
throw new CompilationError(
- illegalClassFileMessage(accessFlags, name, version,
- "has invalid access flags. Found: " + accessFlags.toString()), origin);
+ illegalClassFileMessage(
+ accessFlags,
+ name,
+ version,
+ "has invalid access flags. Found: " + accessFlags.toString()),
+ origin);
}
if (superName == null && !name.equals(Constants.JAVA_LANG_OBJECT_NAME)) {
throw new CompilationError(
- illegalClassFileMessage(accessFlags, name, version,
- "is missing a super type"), origin);
+ illegalClassFileMessage(accessFlags, name, version, "is missing a super type"), origin);
}
if (accessFlags.isInterface()
&& !Objects.equals(superName, Constants.JAVA_LANG_OBJECT_NAME)) {
throw new CompilationError(
- illegalClassFileMessage(accessFlags, name, version,
+ illegalClassFileMessage(
+ accessFlags,
+ name,
+ version,
"must extend class java.lang.Object. Found: " + Objects.toString(superName)),
origin);
}
@@ -443,7 +453,7 @@
}
if (enclosingMember == null
&& (clazz.isLocalClass() || clazz.isAnonymousClass())
- && getMajorVersion() > V1_6) {
+ && CfVersion.V1_6.isLessThan(version)) {
application.options.warningMissingEnclosingMember(clazz.type, clazz.origin, version);
}
if (!clazz.isLibraryClass()) {
@@ -521,14 +531,6 @@
return annotations;
}
- private int getMajorVersion() {
- return version & 0xFFFF;
- }
-
- private int getMinorVersion() {
- return ((version >> 16) & 0xFFFF);
- }
-
public boolean isInANest() {
return !nestMembers.isEmpty() || nestHost != null;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 9baf6ad..f9a5fbe 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -134,10 +135,11 @@
// Tree map as must be sorted.
Int2ReferenceSortedMap<DexMethod> typeConstructorClassMap = new Int2ReferenceAVLTreeMap<>();
- int classFileVersion = -1;
+ CfVersion classFileVersion = null;
for (DexEncodedMethod constructor : constructors) {
if (constructor.hasClassFileVersion()) {
- classFileVersion = Integer.max(classFileVersion, constructor.getClassFileVersion());
+ classFileVersion =
+ CfVersion.maxAllowNull(classFileVersion, constructor.getClassFileVersion());
}
DexMethod movedConstructor = moveConstructor(constructor);
lensBuilder.mapMethod(movedConstructor, movedConstructor);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 0f9fdc1..7501fe0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -141,11 +142,11 @@
Int2ReferenceSortedMap<DexMethod> classIdToMethodMap = new Int2ReferenceAVLTreeMap<>();
- int classFileVersion = -1;
+ CfVersion classFileVersion = null;
for (ProgramMethod method : methods) {
if (method.getDefinition().hasClassFileVersion()) {
- classFileVersion =
- Integer.max(classFileVersion, method.getDefinition().getClassFileVersion());
+ CfVersion methodVersion = method.getDefinition().getClassFileVersion();
+ classFileVersion = CfVersion.maxAllowNull(classFileVersion, methodVersion);
}
DexMethod newMethod = moveMethod(method);
lensBuilder.mapMethod(newMethod, newMethod);
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 6382b86..ced929d 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
@@ -14,9 +14,9 @@
import static com.android.tools.r8.ir.analysis.type.TypeElement.getNull;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getSingle;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getWide;
-import static org.objectweb.asm.Opcodes.V1_8;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.InvalidDebugInfoException;
@@ -2199,7 +2199,7 @@
local,
readType);
} else {
- assert method.getDefinition().getClassFileVersion() < V1_8;
+ assert method.getDefinition().getClassFileVersion().isLessThan(CfVersion.V1_8);
hasIncorrectStackMapTypes = true;
}
}
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 c937356..02b521f 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
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -67,7 +68,7 @@
public class EnumUnboxingRewriter {
public static final String ENUM_UNBOXING_UTILITY_METHOD_PREFIX = "$enumboxing$";
- private static final int REQUIRED_CLASS_FILE_VERSION = 52;
+ private static final CfVersion REQUIRED_CLASS_FILE_VERSION = CfVersion.V1_8;
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory factory;
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 e28734b..0021144 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -4,11 +4,10 @@
package com.android.tools.r8.jar;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
-import static org.objectweb.asm.Opcodes.V1_6;
-import static org.objectweb.asm.Opcodes.V1_8;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.CodeSizeOverflowDiagnostic;
@@ -82,6 +81,8 @@
public final ProguardMapSupplier proguardMapSupplier;
+ private static final CfVersion MIN_VERSION_FOR_COMPILER_GENERATED_CODE = CfVersion.V1_6;
+
public CfApplicationWriter(
AppView<?> appView,
Marker marker,
@@ -163,8 +164,8 @@
}
String sourceDebug = getSourceDebugExtension(clazz.annotations());
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
- int version = getClassFileVersion(clazz);
- if (version >= V1_8) {
+ CfVersion version = getClassFileVersion(clazz);
+ if (version.isGreaterThanOrEqual(CfVersion.V1_8)) {
// JDK8 and after ignore ACC_SUPER so unset it.
clazz.accessFlags.unsetSuper();
} else {
@@ -188,7 +189,7 @@
for (int i = 0; i < clazz.interfaces.values.length; i++) {
interfaces[i] = namingLens.lookupInternalName(clazz.interfaces.values[i]);
}
- writer.visit(version, access, name, signature, superName, interfaces);
+ writer.visit(version.raw(), access, name, signature, superName, interfaces);
writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
@@ -233,7 +234,7 @@
options.reporter, handler -> consumer.accept(ByteDataView.of(result), desc, handler));
}
- private int getClassFileVersion(DexEncodedMethod method) {
+ private CfVersion getClassFileVersion(DexEncodedMethod method) {
if (!method.hasClassFileVersion()) {
// In this case bridges have been introduced for the Cf back-end,
// which do not have class file version.
@@ -242,18 +243,22 @@
|| options.cfToCfDesugar;
// TODO(b/146424042): We may call static methods on interface classes so we have to go for
// Java 8.
- return options.cfToCfDesugar ? V1_8 : 0;
+ assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(CfVersion.V1_8);
+ return options.cfToCfDesugar ? CfVersion.V1_8 : MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
}
return method.getClassFileVersion();
}
- private int getClassFileVersion(DexProgramClass clazz) {
- int version = clazz.hasClassFileVersion() ? clazz.getInitialClassFileVersion() : V1_6;
+ private CfVersion getClassFileVersion(DexProgramClass clazz) {
+ CfVersion version =
+ clazz.hasClassFileVersion()
+ ? clazz.getInitialClassFileVersion()
+ : MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
for (DexEncodedMethod method : clazz.directMethods()) {
- version = Math.max(version, getClassFileVersion(method));
+ version = version.max(getClassFileVersion(method));
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
- version = Math.max(version, getClassFileVersion(method));
+ version = version.max(getClassFileVersion(method));
}
return version;
}
@@ -344,7 +349,7 @@
private void writeMethod(
ProgramMethod method,
- int classFileVersion,
+ CfVersion classFileVersion,
LensCodeRewriterUtils rewriter,
ClassWriter writer,
ImmutableMap<DexString, DexValue> defaults) {
@@ -505,7 +510,7 @@
private void writeCode(
ProgramMethod method,
- int classFileVersion,
+ CfVersion classFileVersion,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
CfCode code = method.getDefinition().getCode().asCfCode();
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 acb52c5..fb1eeeb 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexCallSite;
@@ -567,29 +568,30 @@
return definition;
}
- private int largestInputCfVersion = -1;
+ private CfVersion largestInputCfVersion = null;
public boolean canUseConstClassInstructions(InternalOptions options) {
if (!options.isGeneratingClassFiles()) {
return true;
}
- if (largestInputCfVersion == -1) {
+ if (largestInputCfVersion == null) {
computeLargestCfVersion();
}
return options.canUseConstClassInstructions(largestInputCfVersion);
}
private synchronized void computeLargestCfVersion() {
- if (largestInputCfVersion != -1) {
+ if (largestInputCfVersion != null) {
return;
}
for (DexProgramClass clazz : classes()) {
// Skip synthetic classes which may not have a specified version.
if (clazz.hasClassFileVersion()) {
- largestInputCfVersion = Math.max(largestInputCfVersion, clazz.getInitialClassFileVersion());
+ largestInputCfVersion =
+ CfVersion.maxAllowNull(largestInputCfVersion, clazz.getInitialClassFileVersion());
}
}
- assert largestInputCfVersion != -1;
+ assert largestInputCfVersion != null;
}
public boolean isLiveProgramClass(DexProgramClass clazz) {
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 0815577..0052577 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1252,7 +1252,7 @@
ParameterAnnotationsList.empty(),
code,
true,
- method.hasClassFileVersion() ? method.getClassFileVersion() : -1);
+ method.hasClassFileVersion() ? method.getClassFileVersion() : null);
bridge.setLibraryMethodOverride(method.isLibraryMethodOverride());
if (method.accessFlags.isPromotedToPublic()) {
// The bridge is now the public method serving the role of the original method, and should
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 af11559..6ae8c56 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
@@ -97,7 +98,7 @@
ON
}
- public static final int SUPPORTED_CF_MAJOR_VERSION = Opcodes.V11;
+ public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V11;
public static final int SUPPORTED_DEX_VERSION =
AndroidApiLevel.LATEST.getDexVersion().getIntValue();
@@ -696,10 +697,10 @@
private static class TypeVersionPair {
- final int version;
+ final CfVersion version;
final DexType type;
- public TypeVersionPair(int version, DexType type) {
+ public TypeVersionPair(CfVersion version, DexType type) {
this.version = version;
this.type = type;
}
@@ -945,7 +946,7 @@
}
}
- public void warningMissingEnclosingMember(DexType clazz, Origin origin, int version) {
+ public void warningMissingEnclosingMember(DexType clazz, Origin origin, CfVersion version) {
TypeVersionPair pair = new TypeVersionPair(version, clazz);
synchronized (missingEnclosingMembers) {
missingEnclosingMembers.computeIfAbsent(origin, k -> new ArrayList<>()).add(pair);
@@ -1059,7 +1060,7 @@
builder.append(", ");
}
builder.append(pair.type);
- printOutdatedToolchain |= pair.version < 49;
+ printOutdatedToolchain |= pair.version.isLessThan(CfVersion.V1_5);
}
reporter.info(new StringDiagnostic(builder.toString(), origin));
}
@@ -1365,14 +1366,14 @@
return result;
}
- public boolean canUseConstClassInstructions(int cfVersion) {
+ public boolean canUseConstClassInstructions(CfVersion cfVersion) {
assert isGeneratingClassFiles();
- return cfVersion >= requiredCfVersionForConstClassInstructions();
+ return cfVersion.isGreaterThanOrEqual(requiredCfVersionForConstClassInstructions());
}
- public int requiredCfVersionForConstClassInstructions() {
+ public CfVersion requiredCfVersionForConstClassInstructions() {
assert isGeneratingClassFiles();
- return Opcodes.V1_5;
+ return CfVersion.V1_5;
}
public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java b/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
index 6ea338c..c71ca8b 100644
--- a/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
+++ b/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
@@ -21,7 +21,7 @@
@RunWith(Parameterized.class)
public class FailCompilationOnFutureVersionsTest extends TestBase {
- static final int UNSUPPORTED_CF_VERSION = InternalOptions.SUPPORTED_CF_MAJOR_VERSION + 1;
+ static final int UNSUPPORTED_CF_VERSION = InternalOptions.SUPPORTED_CF_VERSION.major() + 1;
static final int UNSUPPORTED_DEX_VERSION = InternalOptions.SUPPORTED_DEX_VERSION + 1;
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/cf/CfVersionTest.java b/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
new file mode 100644
index 0000000..48871e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
@@ -0,0 +1,64 @@
+// 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.cf;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class CfVersionTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public CfVersionTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void test() throws Exception {
+ CfVersion v1_1 = CfVersion.V1_1;
+ assertEquals(Opcodes.V1_1, v1_1.raw());
+ assertEquals(45, v1_1.major());
+ assertEquals(3, v1_1.minor());
+
+ CfVersion v1_2 = CfVersion.V1_2;
+ assertEquals(Opcodes.V1_2, v1_2.raw());
+ assertEquals(46, v1_2.major());
+ assertEquals(0, v1_2.minor());
+
+ CfVersion v9 = CfVersion.V9;
+ assertEquals(Opcodes.V9, v9.raw());
+ assertEquals(53, v9.major());
+ assertEquals(0, v9.minor());
+
+ assertLessThan(v1_1, v1_2);
+ assertLessThan(v1_2, v9);
+ }
+
+ private static void assertLessThan(CfVersion less, CfVersion more) {
+ assertFalse(less.isEqual(more));
+ assertEquals(-1, less.compareTo(more));
+ assertEquals(1, more.compareTo(less));
+ assertTrue(less.isLessThan(more));
+ assertTrue(less.isLessThanOrEqual(more));
+ assertFalse(less.isGreaterThan(more));
+ assertFalse(less.isGreaterThanOrEqual(more));
+ assertFalse(more.isLessThan(less));
+ assertFalse(more.isLessThanOrEqual(less));
+ assertTrue(more.isGreaterThan(less));
+ assertTrue(more.isGreaterThanOrEqual(less));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
index c25dc40..da83514 100644
--- a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
+++ b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.cf;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
@@ -18,7 +17,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.objectweb.asm.Opcodes;
@RunWith(Parameterized.class)
public class GetClassLdcClassTest extends TestBase {
@@ -26,16 +24,16 @@
static final String EXPECTED = StringUtils.lines(Runner.class.getName());
private final TestParameters parameters;
- private final int version;
+ private final CfVersion version;
@Parameterized.Parameters(name = "{0}, cf:{1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- new Integer[] {Opcodes.V1_4, Opcodes.V1_5});
+ new CfVersion[] {CfVersion.V1_1, CfVersion.V1_4, CfVersion.V1_5});
}
- public GetClassLdcClassTest(TestParameters parameters, int version) {
+ public GetClassLdcClassTest(TestParameters parameters, CfVersion version) {
this.parameters = parameters;
this.version = version;
}
@@ -99,11 +97,11 @@
inspector -> {
if (parameters.isCfRuntime()) {
// We are assuming the runtimes we are testing are post CF SE 1.4 (version 48).
- int cfVersionForRuntime = getVersion(inspector, TestClass.class);
- assertNotEquals(Opcodes.V1_4, cfVersionForRuntime);
+ CfVersion cfVersionForRuntime = getVersion(inspector, TestClass.class);
+ assertTrue(CfVersion.V1_4.isLessThan(cfVersionForRuntime));
// Check that the downgraded class has been bumped to at least SE 1.5 (version 49).
- int cfVersionAfterUpgrade = getVersion(inspector, Runner.class);
- assertTrue(cfVersionAfterUpgrade >= Opcodes.V1_5);
+ CfVersion cfVersionAfterUpgrade = getVersion(inspector, Runner.class);
+ assertTrue(CfVersion.V1_4.isLessThan(cfVersionAfterUpgrade));
}
// Check that the method uses a const class instruction.
assertTrue(
@@ -115,11 +113,11 @@
});
}
- private static int getVersion(CodeInspector inspector, Class<?> clazz) {
+ private static CfVersion getVersion(CodeInspector inspector, Class<?> clazz) {
return inspector.clazz(clazz).getDexProgramClass().getInitialClassFileVersion();
}
- private static void checkVersion(CodeInspector inspector, Class<?> clazz, int version) {
+ private static void checkVersion(CodeInspector inspector, Class<?> clazz, CfVersion version) {
assertEquals(version, getVersion(inspector, clazz));
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 4779b8e..d87eafd 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.ClassAccessFlags;
@@ -241,6 +242,10 @@
}
public ClassFileTransformer setVersion(int newVersion) {
+ return setVersion(CfVersion.fromRaw(newVersion));
+ }
+
+ public ClassFileTransformer setVersion(CfVersion newVersion) {
return addClassTransformer(
new ClassTransformer() {
@Override
@@ -251,7 +256,7 @@
String signature,
String superName,
String[] interfaces) {
- super.visit(newVersion, access, name, signature, superName, interfaces);
+ super.visit(newVersion.raw(), access, name, signature, superName, interfaces);
}
});
}