diff options
author | kmb <kmb@google.com> | 2018-02-20 20:16:39 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-02-20 20:17:59 -0800 |
commit | f4d2dad976907abea8a727a8360c2e4e087b893f (patch) | |
tree | 4062bf63f4292d476fcc4a19ba0078fd0899c130 /src/tools/android/java/com/google/devtools/build/android/desugar | |
parent | b6f8642049c58d3bfde92013dc52e66488162a01 (diff) |
Apply interface invocation desugaring to renamed core libraries. Fix invokespecial invocations for core interfaces.
RELNOTES: None.
PiperOrigin-RevId: 186404206
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/desugar')
3 files changed, 63 insertions, 28 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java index 417248b8eb..e83ae41189 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryInvocationRewriter.java +++ b/src/tools/android/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/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java index 2437a19979..9f0163814e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java +++ b/src/tools/android/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/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 52964b7f70..6143940391 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 @@ -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. |