1 // Copyright 2016 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package com.google.devtools.build.android.desugar; 15 16 import static com.google.common.truth.Truth.assertThat; 17 import static org.junit.Assert.fail; 18 19 import com.google.devtools.build.android.desugar.io.BitFlags; 20 import org.junit.Test; 21 import org.junit.runner.RunWith; 22 import org.junit.runners.JUnit4; 23 import org.objectweb.asm.ClassReader; 24 import org.objectweb.asm.ClassVisitor; 25 import org.objectweb.asm.MethodVisitor; 26 import org.objectweb.asm.Opcodes; 27 28 @RunWith(JUnit4.class) 29 public class Java7CompatibilityTest { 30 31 @Test testJava7CompatibleInterface()32 public void testJava7CompatibleInterface() throws Exception { 33 ClassReader reader = new ClassReader(ExtendsDefault.class.getName()); 34 ClassTester tester = new ClassTester(); 35 reader.accept(new Java7Compatibility(tester, null, null), 0); 36 assertThat(tester.version).isEqualTo(Opcodes.V1_7); 37 assertThat(tester.bridgeMethods).isEqualTo(0); // make sure we strip bridge methods 38 assertThat(tester.clinitMethods).isEqualTo(1); // make sure we don't strip <clinit> 39 } 40 41 @Test testDefaultMethodFails()42 public void testDefaultMethodFails() throws Exception { 43 ClassReader reader = new ClassReader(WithDefault.class.getName()); 44 try { 45 reader.accept(new Java7Compatibility(null, null, null), 0); 46 fail("IllegalArgumentException expected"); 47 } catch (IllegalArgumentException expected) { 48 assertThat(expected).hasMessageThat().contains("getVersion()I"); 49 } 50 } 51 52 /** 53 * Tests that a class implementing interfaces with bridge methods redeclares those bridges. 54 * This is behavior of javac that we rely on. 55 */ 56 @Test testConcreteClassRedeclaresBridges()57 public void testConcreteClassRedeclaresBridges() throws Exception { 58 ClassReader reader = new ClassReader(Impl.class.getName()); 59 ClassTester tester = new ClassTester(); 60 reader.accept(new Java7Compatibility(tester, null, null), 0); 61 assertThat(tester.version).isEqualTo(Opcodes.V1_7); 62 assertThat(tester.bridgeMethods).isEqualTo(2); 63 } 64 65 private static class ClassTester extends ClassVisitor { 66 67 int version; 68 int bridgeMethods; 69 int clinitMethods; 70 ClassTester()71 private ClassTester() { 72 super(Opcodes.ASM5, null); 73 } 74 75 @Override visit( int version, int access, String name, String signature, String superName, String[] interfaces)76 public void visit( 77 int version, 78 int access, 79 String name, 80 String signature, 81 String superName, 82 String[] interfaces) { 83 this.version = version; 84 super.visit(version, access, name, signature, superName, interfaces); 85 } 86 87 @Override visitMethod( int access, String name, String desc, String signature, String[] exceptions)88 public MethodVisitor visitMethod( 89 int access, String name, String desc, String signature, String[] exceptions) { 90 if (BitFlags.isSet(access, Opcodes.ACC_BRIDGE)) { 91 ++bridgeMethods; 92 } 93 if ("<clinit>".equals(name)) { 94 ++clinitMethods; 95 } 96 return super.visitMethod(access, name, desc, signature, exceptions); 97 } 98 } 99 100 interface WithDefault<T> { getVersion()101 default int getVersion() { 102 return 18; 103 } get()104 T get(); 105 } 106 107 // Javac will generate a default bridge method "Object get()" that Java7Compatibility will remove 108 interface ExtendsDefault<T extends Number> extends WithDefault<T> { 109 public static final Integer X = Integer.valueOf(37); name()110 String name(); get()111 @Override T get(); 112 } 113 114 // Javac will generate 2 bridge methods that we *don't* want to remove 115 static class Impl implements ExtendsDefault<Integer> { get()116 @Override public Integer get() { 117 return X; 118 } name()119 @Override public String name() { 120 return "test"; 121 } 122 } 123 } 124