diff options
Diffstat (limited to 'src')
3 files changed, 9 insertions, 286 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java b/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java index 68f9756735..2563c7a9ab 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.collect.ImmutableList; import java.util.Comparator; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import org.objectweb.asm.ClassReader; @@ -28,11 +27,6 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; /** * Fixer of classes that extend interfaces with default methods to declare any missing methods @@ -49,8 +43,6 @@ public class DefaultMethodClassFixer extends ClassVisitor { private String internalName; private ImmutableList<String> directInterfaces; private String superName; - /** This method node caches <clinit>, and flushes out in {@code visitEnd()}; */ - private MethodNode clInitMethodNode; public DefaultMethodClassFixer( ClassVisitor dest, @@ -90,88 +82,10 @@ public class DefaultMethodClassFixer extends ClassVisitor { // figure out what methods they declare before stubbing in any missing default methods. recordInheritedMethods(); stubMissingDefaultAndBridgeMethods(); - // Check whether there are interfaces with default methods and <clinit>. If yes, the following - // method call will return a list of interface fields to access in the <clinit> to trigger - // the initialization of these interfaces. - ImmutableList<String> companionsToTriggerInterfaceClinit = - computeOrderedCompanionsToTriggerInterfaceClinit(directInterfaces); - if (!companionsToTriggerInterfaceClinit.isEmpty()) { - if (clInitMethodNode == null) { - clInitMethodNode = new MethodNode(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null); - } - desugarClinitToTriggerInterfaceInitializers(companionsToTriggerInterfaceClinit); - } - } - if (clInitMethodNode != null && super.cv != null) { // Write <clinit> to the chained visitor. - clInitMethodNode.accept(super.cv); } super.visitEnd(); } - private boolean isClinitAlreadyDesugared( - ImmutableList<String> companionsToAccessToTriggerInterfaceClinit) { - InsnList instructions = clInitMethodNode.instructions; - if (instructions.size() <= companionsToAccessToTriggerInterfaceClinit.size()) { - // The <clinit> must end with RETURN, so if the instruction count is less than or equal to - // the companion class count, this <clinit> has not been desugared. - return false; - } - Iterator<AbstractInsnNode> iterator = instructions.iterator(); - for (String companion : companionsToAccessToTriggerInterfaceClinit) { - if (!iterator.hasNext()) { - return false; - } - AbstractInsnNode first = iterator.next(); - if (!(first instanceof MethodInsnNode)) { - return false; - } - MethodInsnNode methodInsnNode = (MethodInsnNode) first; - if (methodInsnNode.getOpcode() != Opcodes.INVOKESTATIC - || !methodInsnNode.owner.equals(companion) - || !methodInsnNode.name.equals( - InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME)) { - return false; - } - checkState( - methodInsnNode.desc.equals( - InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC), - "Inconsistent method desc: %s vs %s", - methodInsnNode.desc, - InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC); - - if (!iterator.hasNext()) { - return false; - } - AbstractInsnNode second = iterator.next(); - if (second.getOpcode() != Opcodes.POP) { - return false; - } - } - return true; - } - - private void desugarClinitToTriggerInterfaceInitializers( - ImmutableList<String> companionsToTriggerInterfaceClinit) { - if (isClinitAlreadyDesugared(companionsToTriggerInterfaceClinit)) { - return; - } - InsnList desugarInsts = new InsnList(); - for (String companionClass : companionsToTriggerInterfaceClinit) { - desugarInsts.add( - new MethodInsnNode( - Opcodes.INVOKESTATIC, - companionClass, - InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME, - InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC, - false)); - } - if (clInitMethodNode.instructions.size() == 0) { - clInitMethodNode.instructions.insert(new InsnNode(Opcodes.RETURN)); - } - clInitMethodNode.instructions.insertBefore( - clInitMethodNode.instructions.getFirst(), desugarInsts); - } - @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { @@ -179,11 +93,6 @@ public class DefaultMethodClassFixer extends ClassVisitor { if (!isInterface) { recordIfInstanceMethod(access, name, desc); } - if ("<clinit>".equals(name)) { - checkState(clInitMethodNode == null, "This class fixer has been used. "); - clInitMethodNode = new MethodNode(access, name, desc, signature, exceptions); - return clInitMethodNode; - } return super.visitMethod(access, name, desc, signature, exceptions); } @@ -256,55 +165,6 @@ public class DefaultMethodClassFixer extends ClassVisitor { } /** - * Starting from the given interfaces, this method scans the interface hierarchy, finds the - * interfaces that have default methods and <clinit>, and returns the companion class names of - * these interfaces. - * - * <p>Note that the returned companion classes are ordered in the order of the interface - * initialization, which is consistent with the JVM behavior. For example, "class A implements I1, - * I2", the returned list would be [I1$$CC, I2$$CC], not [I2$$CC, I1$$CC]. - */ - private ImmutableList<String> computeOrderedCompanionsToTriggerInterfaceClinit( - ImmutableList<String> interfaces) { - ImmutableList.Builder<String> companionCollector = ImmutableList.builder(); - HashSet<String> visitedInterfaces = new HashSet<>(); - for (String anInterface : interfaces) { - computeOrderedCompanionsToTriggerInterfaceClinit( - anInterface, visitedInterfaces, companionCollector); - } - return companionCollector.build(); - } - - private void computeOrderedCompanionsToTriggerInterfaceClinit( - String anInterface, - HashSet<String> visitedInterfaces, - ImmutableList.Builder<String> companionCollector) { - if (!visitedInterfaces.add(anInterface)) { - return; - } - ClassReader bytecode = classpath.readIfKnown(anInterface); - if (bytecode == null || bootclasspath.isKnown(anInterface)) { - return; - } - String[] parentInterfaces = bytecode.getInterfaces(); - if (parentInterfaces != null && parentInterfaces.length > 0) { - for (String parentInterface : parentInterfaces) { - computeOrderedCompanionsToTriggerInterfaceClinit( - parentInterface, visitedInterfaces, companionCollector); - } - } - InterfaceInitializationNecessityDetector necessityDetector = - new InterfaceInitializationNecessityDetector(bytecode.getClassName()); - bytecode.accept(necessityDetector, ClassReader.SKIP_DEBUG); - if (necessityDetector.needsToInitialize()) { - // If we need to initialize this interface, we initialize its companion class, and its - // companion class will initialize the interface then. This desigin decision is made to avoid - // access issue, e.g., package-private interfaces. - companionCollector.add(InterfaceDesugaring.getCompanionClassName(anInterface)); - } - } - - /** * Recursively searches the given interfaces for default methods not implemented by this class * directly. If this method returns true we need to think about stubbing missing default methods. */ @@ -338,13 +198,10 @@ public class DefaultMethodClassFixer extends ClassVisitor { // Note that an exception is that, if a bridge method is for a default interface method, javac // will NOT generate the bridge method in the implementing class. So we need extra logic to // handle these bridge methods. - return isNonBridgeDefaultMethod(access) && !instanceMethods.contains(name + ":" + desc); - } - - private static boolean isNonBridgeDefaultMethod(int access) { return BitFlags.noneSet( - access, - Opcodes.ACC_ABSTRACT | Opcodes.ACC_STATIC | Opcodes.ACC_BRIDGE | Opcodes.ACC_PRIVATE); + access, + Opcodes.ACC_ABSTRACT | Opcodes.ACC_STATIC | Opcodes.ACC_BRIDGE | Opcodes.ACC_PRIVATE) + && !instanceMethods.contains(name + ":" + desc); } /** @@ -522,66 +379,6 @@ public class DefaultMethodClassFixer extends ClassVisitor { } } - /** - * Detector to determine whether an interface needs to be initialized when it is loaded. - * - * <p>If the interface has a default method, and its <clinit> initializes any of its fields, then - * this interface needs to be initialized. - */ - private static class InterfaceInitializationNecessityDetector extends ClassVisitor { - - private final String internalName; - private boolean hasFieldInitializedInClinit; - private boolean hasDefaultMethods; - - public InterfaceInitializationNecessityDetector(String internalName) { - super(Opcodes.ASM5); - this.internalName = internalName; - } - - public boolean needsToInitialize() { - return hasDefaultMethods && hasFieldInitializedInClinit; - } - - @Override - public void visit( - int version, - int access, - String name, - String signature, - String superName, - String[] interfaces) { - super.visit(version, access, name, signature, superName, interfaces); - checkState( - internalName.equals(name), - "Inconsistent internal names: expected=%s, real=%s", - internalName, - name); - checkArgument( - BitFlags.isSet(access, Opcodes.ACC_INTERFACE), - "This class visitor is only used for interfaces."); - } - - @Override - public MethodVisitor visitMethod( - int access, String name, String desc, String signature, String[] exceptions) { - if (!hasDefaultMethods) { - hasDefaultMethods = isNonBridgeDefaultMethod(access); - } - if ("<clinit>".equals(name)) { - return new MethodVisitor(Opcodes.ASM5) { - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - if (opcode == Opcodes.PUTSTATIC && internalName.equals(owner)) { - hasFieldInitializedInClinit = true; - } - } - }; - } - return null; // Do not care about the code. - } - } - /** Comparator for interfaces that compares by whether interfaces extend one another. */ enum InterfaceComparator implements Comparator<Class<?>> { INSTANCE; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java index 74dfa176a1..bbba22e149 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java @@ -20,7 +20,6 @@ import static com.google.devtools.build.android.desugar.LambdaClassMaker.LAMBDA_ import static java.nio.charset.StandardCharsets.ISO_8859_1; import com.google.auto.value.AutoValue; -import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -626,8 +625,7 @@ class Desugar { return ioPairListbuilder.build(); } - @VisibleForTesting - static class ThrowingClassLoader extends ClassLoader { + private static class ThrowingClassLoader extends ClassLoader { @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith("java.")) { @@ -702,8 +700,7 @@ class Desugar { * closer. */ @SuppressWarnings("MustBeClosedChecker") - @VisibleForTesting - static ImmutableList<InputFileProvider> toRegisteredInputFileProvider( + private static ImmutableList<InputFileProvider> toRegisteredInputFileProvider( Closer closer, List<Path> paths) throws IOException { ImmutableList.Builder<InputFileProvider> builder = new ImmutableList.Builder<>(); for (Path path : paths) { diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java index c32ca9ab98..adcd1e04af 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java @@ -14,7 +14,6 @@ package com.google.devtools.build.android.desugar; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import javax.annotation.Nullable; @@ -38,9 +37,6 @@ import org.objectweb.asm.TypePath; */ class InterfaceDesugaring extends ClassVisitor { - static final String COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME = "$$triggerInterfaceInit"; - static final String COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC = "()V"; - static final String COMPANION_SUFFIX = "$$CC"; static final String INTERFACE_STATIC_COMPANION_METHOD_SUFFIX = "$$STATIC$$"; @@ -51,7 +47,6 @@ class InterfaceDesugaring extends ClassVisitor { private int bytecodeVersion; private int accessFlags; @Nullable private ClassVisitor companion; - @Nullable private FieldInfo interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit; public InterfaceDesugaring( ClassVisitor dest, ClassReaderFactory bootclasspath, GeneratedClassStore store) { @@ -78,50 +73,18 @@ class InterfaceDesugaring extends ClassVisitor { @Override public void visitEnd() { if (companion != null) { - // Emit a method to access the fields of the interfaces that need initialization. - emitInterfaceFieldAccessInCompanionMethodToTriggerInterfaceClinit(); companion.visitEnd(); } super.visitEnd(); } - private void emitInterfaceFieldAccessInCompanionMethodToTriggerInterfaceClinit() { - if (companion == null - || interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit == null) { - return; - } - - // Create a method to access the interface fields - MethodVisitor visitor = - checkNotNull( - companion.visitMethod( - Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, - COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME, - COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC, - null, - null), - "Cannot get a method visitor to write out %s to the companion class.", - COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME); - // Visit the interface field to triger <clinit> of the interface. - visitor.visitFieldInsn( - Opcodes.GETSTATIC, - interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit.owner(), - interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit.name(), - interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit.desc()); - visitor.visitInsn(Opcodes.POP); - visitor.visitInsn(Opcodes.RETURN); - } - @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor result; - if (isStaticInitializer(name)) { - result = - new InterfaceFieldWriteCollector( - super.visitMethod(access, name, desc, signature, exceptions)); - } else if (BitFlags.isSet(accessFlags, Opcodes.ACC_INTERFACE) - && BitFlags.noneSet(access, Opcodes.ACC_ABSTRACT | Opcodes.ACC_BRIDGE)) { + if (BitFlags.isSet(accessFlags, Opcodes.ACC_INTERFACE) + && BitFlags.noneSet(access, Opcodes.ACC_ABSTRACT | Opcodes.ACC_BRIDGE) + && !"<clinit>".equals(name)) { checkArgument(BitFlags.noneSet(access, Opcodes.ACC_NATIVE), "Forbidden per JLS ch 9.4"); boolean isLambdaBody = @@ -177,10 +140,6 @@ class InterfaceDesugaring extends ClassVisitor { : null; } - private static boolean isStaticInitializer(String methodName) { - return "<clinit>".equals(methodName); - } - private static String normalizeInterfaceMethodName( String name, boolean isLambda, boolean isStatic) { String suffix; @@ -197,10 +156,6 @@ class InterfaceDesugaring extends ClassVisitor { return name + suffix; } - static String getCompanionClassName(String interfaceName) { - return interfaceName + COMPANION_SUFFIX; - } - /** * Returns the descriptor of a static method for an instance method with the given receiver and * description, simply by pre-pending the given descriptor's parameter list with the given @@ -217,7 +172,7 @@ class InterfaceDesugaring extends ClassVisitor { private ClassVisitor companion() { if (companion == null) { checkState(BitFlags.isSet(accessFlags, Opcodes.ACC_INTERFACE)); - String companionName = getCompanionClassName(internalName); + String companionName = internalName + COMPANION_SUFFIX; companion = store.add(companionName); companion.visit( @@ -233,32 +188,6 @@ class InterfaceDesugaring extends ClassVisitor { } /** - * Interface field scanner to get the field of the current interface that is written in the - * initializer. - */ - private class InterfaceFieldWriteCollector extends MethodVisitor { - - public InterfaceFieldWriteCollector(MethodVisitor mv) { - super(Opcodes.ASM5, mv); - } - - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - if (interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit == null - && opcode == Opcodes.PUTSTATIC) { - checkState( - owner.equals(internalName), - "Expect only the fields in this interface to be initialized. owner=%s, expected=%s", - owner, - internalName); - interfaceFieldToAccessInCompanionMethodToTriggerInterfaceClinit = - FieldInfo.create(owner, name, desc); - } - super.visitFieldInsn(opcode, owner, name, desc); - } - } - - /** * Rewriter for calls to static interface methods and super calls to default methods, unless * they're part of the bootclasspath, as well as all lambda body methods. Keeps calls to interface * methods declared in the bootclasspath as-is (but note that these would presumably fail on |