summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorkmb <kmb@google.com>2018-02-20 20:16:39 -0800
committerIvan Gavrilovic <gavra@google.com>2018-05-04 10:38:28 +0100
commit669a724b8244e89d40ffd2ea0390d05c078857a3 (patch)
tree69b29bce16835aabd8b4a63229c678e3d32ae555 /java
parentcfff73917d13d5629fc6d247921e4db46fb841c8 (diff)
downloaddesugar-669a724b8244e89d40ffd2ea0390d05c078857a3.tar.gz
Apply interface invocation desugaring to renamed core libraries. Fix invokespecial invocations for core interfaces.
RELNOTES: None. PiperOrigin-RevId: 186404206 GitOrigin-RevId: f4d2dad976907abea8a727a8360c2e4e087b893f Change-Id: Ic6ddd94802f83596c35999db68ad3b28bdc93c73
Diffstat (limited to 'java')
-rw-r--r--java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java10
-rw-r--r--java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java79
-rw-r--r--java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java2
3 files changed, 63 insertions, 28 deletions
diff --git a/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java b/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java
index 417248b..e83ae41 100644
--- a/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java
+++ b/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java
@@ -51,7 +51,8 @@ public class CoreLibraryInvocationRewriter extends ClassVisitor {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
Class<?> coreInterface =
- support.getEmulatedCoreLibraryInvocationTarget(opcode, owner, name, desc, itf);
+ support.getCoreInterfaceRewritingTarget(opcode, owner, name, desc, itf);
+
if (coreInterface != null) {
String coreInterfaceName = coreInterface.getName().replace('.', '/');
name =
@@ -60,18 +61,17 @@ public class CoreLibraryInvocationRewriter extends ClassVisitor {
if (opcode == Opcodes.INVOKESTATIC) {
checkState(owner.equals(coreInterfaceName));
} else {
- desc =
- InterfaceDesugaring.companionDefaultMethodDescriptor(
- opcode == Opcodes.INVOKESPECIAL ? owner : coreInterfaceName, desc);
+ desc = InterfaceDesugaring.companionDefaultMethodDescriptor(coreInterfaceName, desc);
}
if (opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL) {
checkArgument(itf, "Expected interface to rewrite %s.%s : %s", owner, name, desc);
- owner = InterfaceDesugaring.getCompanionClassName(owner);
+ owner = InterfaceDesugaring.getCompanionClassName(coreInterfaceName);
} else {
// TODO(kmb): Simulate dynamic dispatch instead of calling most general default method
owner = InterfaceDesugaring.getCompanionClassName(coreInterfaceName);
}
+
opcode = Opcodes.INVOKESTATIC;
itf = false;
}
diff --git a/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java b/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java
index 2437a19..9f01638 100644
--- a/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java
+++ b/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.android.desugar;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
@@ -37,8 +38,7 @@ class CoreLibrarySupport {
private final ImmutableList<Class<?>> emulatedInterfaces;
public CoreLibrarySupport(CoreLibraryRewriter rewriter, ClassLoader targetLoader,
- ImmutableList<String> renamedPrefixes, ImmutableList<String> emulatedInterfaces)
- throws ClassNotFoundException {
+ ImmutableList<String> renamedPrefixes, ImmutableList<String> emulatedInterfaces) {
this.rewriter = rewriter;
this.targetLoader = targetLoader;
checkArgument(
@@ -47,7 +47,7 @@ class CoreLibrarySupport {
ImmutableList.Builder<Class<?>> classBuilder = ImmutableList.builder();
for (String itf : emulatedInterfaces) {
checkArgument(itf.startsWith("java/util/"), itf);
- Class<?> clazz = targetLoader.loadClass((rewriter.getPrefix() + itf).replace('/', '.'));
+ Class<?> clazz = loadFromInternal(rewriter.getPrefix() + itf);
checkArgument(clazz.isInterface(), itf);
classBuilder.add(clazz);
}
@@ -56,7 +56,7 @@ class CoreLibrarySupport {
public boolean isRenamedCoreLibrary(String internalName) {
String unprefixedName = rewriter.unprefix(internalName);
- if (!unprefixedName.startsWith("java/")) {
+ if (!unprefixedName.startsWith("java/") || renamedPrefixes.isEmpty()) {
return false; // shortcut
}
// Rename any classes desugar might generate under java/ (for emulated interfaces) as well as
@@ -81,26 +81,60 @@ class CoreLibrarySupport {
return getEmulatedCoreClassOrInterface(internalName) != null;
}
- public boolean isEmulatedCoreLibraryInvocation(
- int opcode, String owner, String name, String desc, boolean itf) {
- return getEmulatedCoreLibraryInvocationTarget(opcode, owner, name, desc, itf) != null;
- }
-
+ /**
+ * If the given invocation needs to go through a companion class of an emulated or renamed
+ * core interface, this methods returns that interface. This is a helper method for
+ * {@link CoreLibraryInvocationRewriter}.
+ *
+ * <p>Always returns an interface (or {@code null}), even if {@code owner} is a class. Can only
+ * return non-{@code null} if {@code owner} is a core library type.
+ */
@Nullable
- public Class<?> getEmulatedCoreLibraryInvocationTarget(
+ public Class<?> getCoreInterfaceRewritingTarget(
int opcode, String owner, String name, String desc, boolean itf) {
- Class<?> clazz = getEmulatedCoreClassOrInterface(owner);
- if (clazz == null) {
+ if (owner.contains("$$Lambda$") || owner.endsWith("$$CC")) {
+ // Regular desugaring handles generated classes, no emulation is needed
+ return null;
+ }
+ if (!itf && (opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL)) {
+ // Ignore staticly dispatched invocations on classes--they never need rewriting
return null;
}
+ Class<?> clazz;
+ if (isRenamedCoreLibrary(owner)) {
+ // For renamed invocation targets we just need to do what InterfaceDesugaring does, that is,
+ // only worry about invokestatic and invokespecial interface invocations; nothing to do for
+ // invokevirtual and invokeinterface. InterfaceDesugaring ignores bootclasspath interfaces,
+ // so we have to do its work here for renamed interfaces.
+ if (itf
+ && (opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL)) {
+ clazz = loadFromInternal(owner);
+ } else {
+ return null;
+ }
+ } else {
+ // If not renamed, see if the owner needs emulation.
+ clazz = getEmulatedCoreClassOrInterface(owner);
+ if (clazz == null) {
+ return null;
+ }
+ }
+ checkArgument(itf == clazz.isInterface(), "%s expected to be interface: %s", owner, itf);
- if (itf && opcode == Opcodes.INVOKESTATIC) {
- return clazz; // static interface method
+ if (opcode == Opcodes.INVOKESTATIC) {
+ // Static interface invocation always goes to the given owner
+ checkState(itf); // we should've bailed out above.
+ return clazz;
}
+ // See if the invoked method is a default method, which will need rewriting. For invokespecial
+ // we can only get here if its a default method, and invokestatic we handled above.
Method callee = findInterfaceMethod(clazz, name, desc);
if (callee != null && callee.isDefault()) {
return callee.getDeclaringClass();
+ } else {
+ checkArgument(opcode != Opcodes.INVOKESPECIAL,
+ "Couldn't resolve interface super call %s.super.%s : %s", owner, name, desc);
}
return null;
}
@@ -117,19 +151,21 @@ class CoreLibrarySupport {
}
}
- Class<?> clazz;
- try {
- clazz = targetLoader.loadClass(internalName.replace('/', '.'));
- } catch (ClassNotFoundException e) {
- throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e);
- }
-
+ Class<?> clazz = loadFromInternal(internalName);
if (emulatedInterfaces.stream().anyMatch(itf -> itf.isAssignableFrom(clazz))) {
return clazz;
}
return null;
}
+ private Class<?> loadFromInternal(String internalName) {
+ try {
+ return targetLoader.loadClass(internalName.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e);
+ }
+ }
+
private static Method findInterfaceMethod(Class<?> clazz, String name, String desc) {
return collectImplementedInterfaces(clazz, new LinkedHashSet<>())
.stream()
@@ -141,7 +177,6 @@ class CoreLibrarySupport {
.orElse((Method) null);
}
-
private static Method findMethod(Class<?> clazz, String name, String desc) {
for (Method m : clazz.getMethods()) {
if (m.getName().equals(name) && Type.getMethodDescriptor(m).equals(desc)) {
diff --git a/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java b/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java
index 52964b7..6143940 100644
--- a/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java
+++ b/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java
@@ -494,7 +494,7 @@ public class DefaultMethodClassFixer extends ClassVisitor {
// If we're visiting a bootclasspath interface then we most likely don't have the code.
// That means we can't just copy the method bodies as we're trying to do below.
checkState(!isBootclasspathInterface,
- "TODO stub bridge methods for core interfaces if ever needed");
+ "TODO stub core interface %s bridge methods in %s", stubbedInterfaceName, internalName);
// For bridges we just copy their bodies instead of going through the companion class.
// Meanwhile, we also need to desugar the copied method bodies, so that any calls to
// interface methods are correctly handled.