aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-06-20 07:41:10 -0400
committerGravatar Kristina Chodorow <kchodorow@google.com>2017-06-20 14:36:06 -0400
commit2d008e824e4c5c39f00f21cfa879eed61d5b500a (patch)
tree762e250be38b5b2a2191cd911b8466037e7a6824 /src/tools/android/java/com/google/devtools/build/android
parent44833517b549af9a844e58a2c6978111bda06720 (diff)
Automated rollback of commit 317e0de2113f0361c9dbf98653ceda4efea3f3e2.
*** Original change description *** Access interface constants to explicitly trigger the execution of interface initializers. The problem is that when we desugar default methods, the static intializer of interface will not be executed. The JVM spec says that if an interface has default methods, then when it is loaded, it will also be initialized. After we desugar such an interface, its default methods are removed, and when we load the interface, the <clinit> will not be executed. This CL checks whether an interface has default me... *** ROLLBACK_OF=159496992 NO_SQ=PURE_ROLLBACK RELNOTES: None PiperOrigin-RevId: 159545752
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java209
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java7
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java79
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