Enum unboxing: fix accessibility
Bug: 160939354
Change-Id: I2821fc2c505905709f51051f5bd8a0678f510c8d
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 019d295..cab8542 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -180,6 +180,10 @@
obsolete = true;
}
+ public CompilationState getCompilationState() {
+ return compilationState;
+ }
+
public DexEncodedMethod getDefaultInterfaceMethodImplementation() {
return defaultInterfaceMethodImplementation;
}
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 3f74b74..6cbf350 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
@@ -739,7 +739,11 @@
}
if (enumUnboxer != null) {
enumUnboxer.finishAnalysis();
- enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
+ enumUnboxer.unboxEnums(
+ postMethodProcessorBuilder,
+ executorService,
+ feedback,
+ classStaticizer == null ? Collections.emptySet() : classStaticizer.getCandidates());
}
if (!options.debug) {
new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
@@ -1763,6 +1767,11 @@
|| definition.getOptimizationInfo().isReachabilitySensitive()) {
return false;
}
+ if (appView.options().enableEnumUnboxing && method.getHolder().isEnum()) {
+ // Although the method is pinned, we compute the inlining constraint for enum unboxing,
+ // but the inliner won't be able to inline the method (marked as pinned).
+ return true;
+ }
if (appView.appInfo().hasLiveness()
&& appView.appInfo().withLiveness().isPinned(method.getReference())) {
return false;
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 e98ecfa..a74b4b6 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
@@ -51,6 +51,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.conversion.PostOptimization;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
@@ -68,6 +69,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -83,6 +87,7 @@
// Map the enum candidates with their dependencies, i.e., the methods to reprocess for the given
// enum if the optimization eventually decides to unbox it.
private final Map<DexType, ProgramMethodSet> enumsUnboxingCandidates;
+ private final Set<DexType> enumsToUnboxWithPackageRequirement = Sets.newIdentityHashSet();
private EnumUnboxingRewriter enumUnboxerRewriter;
@@ -328,7 +333,8 @@
public void unboxEnums(
PostMethodProcessor.Builder postBuilder,
ExecutorService executorService,
- OptimizationFeedbackDelayed feedback)
+ OptimizationFeedbackDelayed feedback,
+ Set<DexType> hostsToAvoidIfPossible)
throws ExecutionException {
// At this point the enumsToUnbox are no longer candidates, they will all be unboxed.
if (enumsUnboxingCandidates.isEmpty()) {
@@ -338,7 +344,10 @@
// Update keep info on any of the enum methods of the removed classes.
updatePinnedItems(enumsToUnbox);
enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumsToUnbox);
- NestedGraphLens enumUnboxingLens = new TreeFixer(enumsToUnbox).fixupTypeReferences();
+ Map<DexType, DexType> newMethodLocation =
+ findNewMethodLocationOfUnboxableEnums(enumsToUnbox, hostsToAvoidIfPossible);
+ NestedGraphLens enumUnboxingLens =
+ new TreeFixer(enumsToUnbox).fixupTypeReferences(newMethodLocation);
appView.setUnboxedEnums(enumUnboxerRewriter.getEnumsToUnbox());
GraphLens previousLens = appView.graphLens();
appView.rewriteWithLens(enumUnboxingLens);
@@ -378,6 +387,84 @@
postBuilder.rewrittenWithLens(appView, previousLens);
}
+ // Some enums may have methods which require to stay in the current package for accessibility,
+ // in this case we find another class than the enum unboxing utility class to host these methods.
+ private Map<DexType, DexType> findNewMethodLocationOfUnboxableEnums(
+ Set<DexType> enumsToUnbox, Set<DexType> hostsToAvoidIfPossible) {
+ if (enumsToUnboxWithPackageRequirement.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<DexType, DexType> newMethodLocationMap = new IdentityHashMap<>();
+ Map<String, DexProgramClass> packageToClassMap =
+ getPackageToClassMapExcluding(enumsToUnbox, hostsToAvoidIfPossible);
+ for (DexType toUnbox : enumsToUnboxWithPackageRequirement) {
+ DexProgramClass packageClass = packageToClassMap.get(toUnbox.getPackageDescriptor());
+ if (packageClass != null) {
+ newMethodLocationMap.put(toUnbox, packageClass.type);
+ }
+ }
+ enumsToUnboxWithPackageRequirement.clear();
+ return newMethodLocationMap;
+ }
+
+ // We are looking for another class in the same package as the unboxed enum to host the unboxed
+ // enum methods. We go through all classes, and for each package where a host is needed, we
+ // select a class.
+ private Map<String, DexProgramClass> getPackageToClassMapExcluding(
+ Set<DexType> enumsToUnbox, Set<DexType> hostsToAvoidIfPossible) {
+ HashSet<String> relevantPackages = new HashSet<>();
+ for (DexType toUnbox : enumsToUnbox) {
+ relevantPackages.add(toUnbox.getPackageDescriptor());
+ }
+
+ Map<String, DexProgramClass> packageToClassMap = new HashMap<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ String packageDescriptor = clazz.type.getPackageDescriptor();
+ if (relevantPackages.contains(packageDescriptor) && !enumsToUnbox.contains(clazz.type)) {
+ DexProgramClass previousClass = packageToClassMap.get(packageDescriptor);
+ if (previousClass == null) {
+ packageToClassMap.put(packageDescriptor, clazz);
+ } else {
+ packageToClassMap.put(
+ packageDescriptor, selectHost(clazz, previousClass, hostsToAvoidIfPossible));
+ }
+ }
+ }
+
+ return packageToClassMap;
+ }
+
+ // We are trying to select a host for the enum unboxing methods, but multiple candidates are
+ // available. We need to pick one of the two classes and the result has to be deterministic.
+ // We follow the heuristics, in order:
+ // 1. don't pick a class from hostToAvoidIfPossible if possible
+ // 2. pick the class with the least number of methods
+ // 3. pick the first class name in alphabetical order for determinism.
+ private DexProgramClass selectHost(
+ DexProgramClass class1, DexProgramClass class2, Set<DexType> hostsToAvoidIfPossible) {
+ boolean avoid1 = hostsToAvoidIfPossible.contains(class1.type);
+ boolean avoid2 = hostsToAvoidIfPossible.contains(class2.type);
+ if (avoid1 && !avoid2) {
+ return class2;
+ }
+ if (avoid2 && !avoid1) {
+ return class1;
+ }
+ int size1 = class1.getMethodCollection().size();
+ int size2 = class2.getMethodCollection().size();
+ if (size1 < size2) {
+ return class1;
+ }
+ if (size2 < size1) {
+ return class2;
+ }
+ assert class1.type != class2.type;
+ if (class1.type.slowCompareTo(class2.type) < 0) {
+ return class1;
+ }
+ return class2;
+ }
+
private void updatePinnedItems(Set<DexType> enumsToUnbox) {
appView
.appInfo()
@@ -394,6 +481,85 @@
}
public void finishAnalysis() {
+ analyzeInitializers();
+ analyzeAccessibility();
+ if (debugLogEnabled) {
+ reportEnumsAnalysis();
+ }
+ }
+
+ private void analyzeAccessibility() {
+ // Unboxing an enum will require to move its methods to a different class, which may impact
+ // accessibility. For a quick analysis we simply reuse the inliner analysis.
+ for (DexType toUnbox : enumsUnboxingCandidates.keySet()) {
+ DexProgramClass enumClass = appView.definitionFor(toUnbox).asProgramClass();
+ Constraint classConstraint = analyzeAccessibilityInClass(enumClass);
+ if (classConstraint == Constraint.NEVER) {
+ markEnumAsUnboxable(Reason.ACCESSIBILITY, enumClass);
+ } else if (classConstraint == Constraint.PACKAGE) {
+ enumsToUnboxWithPackageRequirement.add(toUnbox);
+ }
+ }
+ }
+
+ private Constraint analyzeAccessibilityInClass(DexProgramClass enumClass) {
+ Constraint classConstraint = Constraint.ALWAYS;
+ for (DexEncodedMethod method : enumClass.methods()) {
+ // Enum initializer are analyzed in analyzeInitializers instead.
+ if (!method.isInitializer()) {
+ Constraint methodConstraint = constraintForEnumUnboxing(method);
+ classConstraint = meet(classConstraint, methodConstraint);
+ if (classConstraint == Constraint.NEVER) {
+ return classConstraint;
+ }
+ }
+ }
+ return classConstraint;
+ }
+
+ private Constraint meet(Constraint constraint1, Constraint constraint2) {
+ if (constraint1 == Constraint.NEVER || constraint2 == Constraint.NEVER) {
+ return Constraint.NEVER;
+ }
+ if (constraint1 == Constraint.ALWAYS) {
+ return constraint2;
+ }
+ if (constraint2 == Constraint.ALWAYS) {
+ return constraint1;
+ }
+ assert constraint1 == Constraint.PACKAGE && constraint2 == Constraint.PACKAGE;
+ return Constraint.PACKAGE;
+ }
+
+ public Constraint constraintForEnumUnboxing(DexEncodedMethod method) {
+ // TODO(b/160939354): Use a UseRegistry instead of inlining constraints.
+ assert appView.definitionForProgramType(method.holder()) != null;
+ assert appView.definitionForProgramType(method.holder()).isEnum();
+ assert appView.definitionForProgramType(method.holder()).isEffectivelyFinal(appView);
+ assert appView.definitionForProgramType(method.holder()).superType
+ == appView.dexItemFactory().enumType;
+ assert !appView.definitionForProgramType(method.holder()).isInANest()
+ : "Unboxable enum is in a nest, this should not happen in cf to dex compilation, R8 needs"
+ + " to take care of nest access control when relocating unboxable enums methods";
+ switch (method.getCompilationState()) {
+ case PROCESSED_INLINING_CANDIDATE_ANY:
+ return Constraint.ALWAYS;
+ case PROCESSED_INLINING_CANDIDATE_SAME_NEST:
+ assert false;
+ // fall through
+ case PROCESSED_INLINING_CANDIDATE_SUBCLASS:
+ // There is no such thing as subclass access in this context, enums analyzed here have no
+ // subclasses, inherit directly from java.lang.Enum and are not analyzed if they call
+ // the protected methods in java.lang.Enum and java.lang.Object.
+ case PROCESSED_INLINING_CANDIDATE_SAME_CLASS:
+ case PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE:
+ return Constraint.PACKAGE;
+ default:
+ return Constraint.NEVER;
+ }
+ }
+
+ private void analyzeInitializers() {
for (DexType toUnbox : enumsUnboxingCandidates.keySet()) {
DexProgramClass enumClass = appView.definitionForProgramType(toUnbox);
assert enumClass != null;
@@ -417,9 +583,6 @@
markEnumAsUnboxable(Reason.INVALID_CLINIT, enumClass);
}
}
- if (debugLogEnabled) {
- reportEnumsAnalysis();
- }
}
private Reason instructionAllowEnumUnboxing(
@@ -674,6 +837,7 @@
public enum Reason {
ELIGIBLE,
+ ACCESSIBILITY,
ANNOTATION,
PINNED,
DOWN_CAST,
@@ -709,7 +873,8 @@
private class TreeFixer {
- private final List<DexEncodedMethod> unboxedEnumsMethods = new ArrayList<>();
+ private final Map<DexType, List<DexEncodedMethod>> unboxedEnumsMethods =
+ new IdentityHashMap<>();
private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.builder();
private final Set<DexType> enumsToUnbox;
@@ -717,13 +882,13 @@
this.enumsToUnbox = enumsToUnbox;
}
- private NestedGraphLens fixupTypeReferences() {
+ private NestedGraphLens fixupTypeReferences(Map<DexType, DexType> newMethodLocation) {
assert enumUnboxerRewriter != null;
// Fix all methods and fields using enums to unbox.
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (enumsToUnbox.contains(clazz.type)) {
assert clazz.instanceFields().size() == 0;
- // Clear the initializers and move the static methods to the utility class.
+ // Clear the initializers and move the static methods to the new location.
Set<DexEncodedMethod> methodsToRemove = Sets.newIdentityHashSet();
clazz
.methods()
@@ -732,8 +897,12 @@
if (m.isInitializer()) {
clearEnumToUnboxMethod(m);
} else {
- unboxedEnumsMethods.add(
- fixupEncodedMethodToUtility(m, factory.enumUnboxingUtilityType));
+ DexType newHolder =
+ newMethodLocation.getOrDefault(
+ clazz.type, factory.enumUnboxingUtilityType);
+ List<DexEncodedMethod> movedMethods =
+ unboxedEnumsMethods.computeIfAbsent(newHolder, k -> new ArrayList<>());
+ movedMethods.add(fixupEncodedMethodToUtility(m, newHolder));
methodsToRemove.add(m);
}
});
@@ -754,7 +923,11 @@
DexProgramClass utilityClass =
appView.definitionForProgramType(factory.enumUnboxingUtilityType);
assert utilityClass != null : "Should have been synthesized upfront";
- utilityClass.addDirectMethods(unboxedEnumsMethods);
+ unboxedEnumsMethods.forEach(
+ (newHolderType, movedMethods) -> {
+ DexProgramClass newHolderClass = appView.definitionFor(newHolderType).asProgramClass();
+ newHolderClass.addDirectMethods(movedMethods);
+ });
return lensBuilder.build(factory, appView.graphLens(), enumsToUnbox);
}
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 617a12e..c4466cd 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
@@ -113,6 +113,11 @@
this.converter = converter;
}
+ // This set is an approximation and can be used only for heuristics.
+ public final Set<DexType> getCandidates() {
+ return candidates.keySet();
+ }
+
// Before doing any usage-based analysis we collect a set of classes that can be
// candidates for staticizing. This analysis is very simple, but minimizes the
// set of eligible classes staticizer tracks and thus time and memory it needs.
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
new file mode 100644
index 0000000..52d8688
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
@@ -0,0 +1,101 @@
+// 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 static org.hamcrest.CoreMatchers.containsString;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.transformers.ClassTransformer;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.MethodVisitor;
+
+@RunWith(Parameterized.class)
+public class VirtualMethodsAccessibilityErrorEnumUnboxingTest extends EnumUnboxingTestBase {
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public VirtualMethodsAccessibilityErrorEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ byte[] testClassData =
+ transformer(TestClass.class)
+ .addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access,
+ String name,
+ String descriptor,
+ String signature,
+ String[] exceptions) {
+ if (name.equals("privateMethod")) {
+ return super.visitMethod(
+ ACC_STATIC | ACC_PRIVATE, name, descriptor, signature, exceptions);
+ } else {
+ return super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ }
+ })
+ .transform();
+ testForR8(parameters.getBackend())
+ .noMinification()
+ .addProgramClasses(MyEnum.class)
+ .addProgramClassFileData(testClassData)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatMatches(containsString("java.lang.IllegalAccessError"));
+ }
+
+ public static class TestClass {
+ // The method privateMethod will be transformed into a private method.
+ @NeverInline
+ protected static int privateMethod() {
+ return System.currentTimeMillis() > 0 ? 4 : 3;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(MyEnum.A.ordinal());
+ MyEnum.print();
+ }
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B;
+
+ @NeverInline
+ static void print() {
+ // This should raise an accessibility error, weither the enum is unboxed or not.
+ System.out.println(TestClass.privateMethod());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
index 2e3466f..3a1fbab 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsEnumUnboxingTest.java
@@ -1,11 +1,8 @@
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-
package com.android.tools.r8.enumunboxing;
-import static org.hamcrest.CoreMatchers.containsString;
-
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
@@ -19,7 +16,6 @@
@RunWith(Parameterized.class)
public class VirtualMethodsEnumUnboxingTest extends EnumUnboxingTestBase {
-
private final TestParameters parameters;
private final boolean enumValueOptimization;
private final EnumKeepRules enumKeepRules;
@@ -43,7 +39,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(VirtualMethodsEnumUnboxingTest.class)
.addKeepMainRule(classToTest)
- .addKeepMainRule(VirtualMethodsFail.class)
.addKeepRules(enumKeepRules.getKeepRules())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -58,13 +53,15 @@
assertEnumIsUnboxed(MyEnumWithCollisions.class, classToTest.getSimpleName(), m);
assertEnumIsUnboxed(
MyEnumWithPackagePrivateCall.class, classToTest.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumWithProtectedCall.class, classToTest.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumWithPackagePrivateFieldAccess.class, classToTest.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumWithPackagePrivateAndPrivateCall.class, classToTest.getSimpleName(), m);
});
R8TestRunResult run = compile.run(parameters.getRuntime(), classToTest).assertSuccess();
assertLines2By2Correct(run.getStdOut());
- // TODO(b/160854837): This test should actually be successful.
- compile
- .run(parameters.getRuntime(), VirtualMethodsFail.class)
- .assertFailureWithErrorThatMatches(containsString("IllegalAccessError"));
}
@SuppressWarnings("SameParameterValue")
@@ -110,7 +107,6 @@
printPrivate();
}
}
-
// Use two enums to test collision.
@NeverClassInline
enum MyEnum2 {
@@ -160,10 +156,30 @@
}
@NeverClassInline
- static class PackagePrivateClass {
+ static class PackagePrivateClassWithPublicMembers {
+ @NeverInline
+ public static void print() {
+ System.out.println("print1");
+ }
+
+ public static int item = System.currentTimeMillis() > 0 ? 42 : 41;
+ }
+
+ @NeverClassInline
+ public static class PublicClassWithPackagePrivateMembers {
@NeverInline
static void print() {
- System.out.println("print");
+ System.out.println("print2");
+ }
+
+ static int item = System.currentTimeMillis() > 0 ? 4 : 1;
+ }
+
+ @NeverClassInline
+ public static class PublicClassWithProtectedMembers {
+ @NeverInline
+ protected static void print() {
+ System.out.println("print3");
}
}
@@ -175,41 +191,75 @@
@NeverInline
public static void callPackagePrivate() {
- PackagePrivateClass.print();
+ PackagePrivateClassWithPublicMembers.print();
+ System.out.println("print1");
+ PublicClassWithPackagePrivateMembers.print();
+ System.out.println("print2");
}
}
- static class VirtualMethodsFail {
- public static void main(String[] args) {
- testCollisions();
- testPackagePrivate();
+ @NeverClassInline
+ enum MyEnumWithPackagePrivateAndPrivateCall {
+ A,
+ B,
+ C;
+
+ @NeverInline
+ public static void privateMethod() {
+ System.out.println("private");
}
@NeverInline
- private static void testPackagePrivate() {
- System.out.println(MyEnumWithPackagePrivateCall.A.ordinal());
- System.out.println(0);
- MyEnumWithPackagePrivateCall.callPackagePrivate();
- System.out.println("print");
+ public static void callPackagePrivateAndPrivate() {
+ // This method has "SAME_CLASS" as compilation state, yet,
+ // it also has a package-private call.
+ privateMethod();
+ System.out.println("private");
+ PackagePrivateClassWithPublicMembers.print();
+ System.out.println("print1");
+ PublicClassWithPackagePrivateMembers.print();
+ System.out.println("print2");
}
+ }
+
+ @NeverClassInline
+ enum MyEnumWithProtectedCall {
+ A,
+ B,
+ C;
@NeverInline
- private static void testCollisions() {
- System.out.println(MyEnumWithCollisions.A.get());
- System.out.println(5);
- System.out.println(MyEnumWithCollisions.B.get());
- System.out.println(2);
- System.out.println(MyEnumWithCollisions.C.get());
- System.out.println(1);
+ public static void callProtected() {
+ PublicClassWithProtectedMembers.print();
+ System.out.println("print3");
+ }
+ }
+
+ @NeverClassInline
+ enum MyEnumWithPackagePrivateFieldAccess {
+ A,
+ B,
+ C;
+
+ @NeverInline
+ public static void accessPackagePrivate() {
+ System.out.println(PackagePrivateClassWithPublicMembers.item);
+ System.out.println("42");
+ System.out.println(PublicClassWithPackagePrivateMembers.item);
+ System.out.println("4");
}
}
static class VirtualMethods {
-
public static void main(String[] args) {
testCustomMethods();
testCustomMethods2();
testNonPublicMethods();
+ testCollisions();
+ testPackagePrivateMethod();
+ testProtectedAccess();
+ testPackagePrivateAndPrivateMethod();
+ testPackagePrivateAccess();
}
@NeverInline
@@ -243,5 +293,43 @@
System.out.println((MyEnum2.A.returnEnum(true).ordinal()));
System.out.println(1);
}
+
+ @NeverInline
+ private static void testPackagePrivateMethod() {
+ System.out.println(MyEnumWithPackagePrivateCall.A.ordinal());
+ System.out.println(0);
+ MyEnumWithPackagePrivateCall.callPackagePrivate();
+ }
+
+ @NeverInline
+ private static void testPackagePrivateAndPrivateMethod() {
+ System.out.println(MyEnumWithPackagePrivateAndPrivateCall.A.ordinal());
+ System.out.println(0);
+ MyEnumWithPackagePrivateAndPrivateCall.callPackagePrivateAndPrivate();
+ }
+
+ @NeverInline
+ private static void testPackagePrivateAccess() {
+ System.out.println(MyEnumWithPackagePrivateFieldAccess.A.ordinal());
+ System.out.println(0);
+ MyEnumWithPackagePrivateFieldAccess.accessPackagePrivate();
+ }
+
+ @NeverInline
+ private static void testProtectedAccess() {
+ System.out.println(MyEnumWithProtectedCall.A.ordinal());
+ System.out.println(0);
+ MyEnumWithProtectedCall.callProtected();
+ }
+
+ @NeverInline
+ private static void testCollisions() {
+ System.out.println(MyEnumWithCollisions.A.get());
+ System.out.println(5);
+ System.out.println(MyEnumWithCollisions.B.get());
+ System.out.println(2);
+ System.out.println(MyEnumWithCollisions.C.get());
+ System.out.println(1);
+ }
}
}