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 org.junit.Test; 20 import org.junit.runner.RunWith; 21 import org.junit.runners.JUnit4; 22 import org.objectweb.asm.ClassReader; 23 import org.objectweb.asm.ClassVisitor; 24 import org.objectweb.asm.MethodVisitor; 25 import org.objectweb.asm.Opcodes; 26 27 @RunWith(JUnit4.class) 28 public class Java7CompatibilityTest { 29 30 @Test testJava7CompatibleInterface()31 public void testJava7CompatibleInterface() throws Exception { 32 ClassReader reader = new ClassReader(ExtendsDefault.class.getName()); 33 ClassTester tester = new ClassTester(); 34 reader.accept(new Java7Compatibility(tester, null, null), 0); 35 assertThat(tester.version).isEqualTo(Opcodes.V1_7); 36 assertThat(tester.bridgeMethods).isEqualTo(0); // make sure we strip bridge methods 37 assertThat(tester.clinitMethods).isEqualTo(1); // make sure we don't strip <clinit> 38 } 39 40 @Test testDefaultMethodFails()41 public void testDefaultMethodFails() throws Exception { 42 ClassReader reader = new ClassReader(WithDefault.class.getName()); 43 try { 44 reader.accept(new Java7Compatibility(null, null, null), 0); 45 fail("IllegalArgumentException expected"); 46 } catch (IllegalArgumentException expected) { 47 assertThat(expected).hasMessageThat().contains("getVersion()I"); 48 } 49 } 50 51 /** 52 * Tests that a class implementing interfaces with bridge methods redeclares those bridges. 53 * This is behavior of javac that we rely on. 54 */ 55 @Test testConcreteClassRedeclaresBridges()56 public void testConcreteClassRedeclaresBridges() throws Exception { 57 ClassReader reader = new ClassReader(Impl.class.getName()); 58 ClassTester tester = new ClassTester(); 59 reader.accept(new Java7Compatibility(tester, null, null), 0); 60 assertThat(tester.version).isEqualTo(Opcodes.V1_7); 61 assertThat(tester.bridgeMethods).isEqualTo(2); 62 } 63 64 private static class ClassTester extends ClassVisitor { 65 66 int version; 67 int bridgeMethods; 68 int clinitMethods; 69 ClassTester()70 private ClassTester() { 71 super(Opcodes.ASM5, null); 72 } 73 74 @Override visit( int version, int access, String name, String signature, String superName, String[] interfaces)75 public void visit( 76 int version, 77 int access, 78 String name, 79 String signature, 80 String superName, 81 String[] interfaces) { 82 this.version = version; 83 super.visit(version, access, name, signature, superName, interfaces); 84 } 85 86 @Override visitMethod( int access, String name, String desc, String signature, String[] exceptions)87 public MethodVisitor visitMethod( 88 int access, String name, String desc, String signature, String[] exceptions) { 89 if (BitFlags.isSet(access, Opcodes.ACC_BRIDGE)) { 90 ++bridgeMethods; 91 } 92 if ("<clinit>".equals(name)) { 93 ++clinitMethods; 94 } 95 return super.visitMethod(access, name, desc, signature, exceptions); 96 } 97 } 98 99 interface WithDefault<T> { getVersion()100 default int getVersion() { 101 return 18; 102 } get()103 T get(); 104 } 105 106 // Javac will generate a default bridge method "Object get()" that Java7Compatibility will remove 107 interface ExtendsDefault<T extends Number> extends WithDefault<T> { 108 public static final Integer X = Integer.valueOf(37); name()109 String name(); get()110 @Override T get(); 111 } 112 113 // Javac will generate 2 bridge methods that we *don't* want to remove 114 static class Impl implements ExtendsDefault<Integer> { get()115 @Override public Integer get() { 116 return X; 117 } name()118 @Override public String name() { 119 return "test"; 120 } 121 } 122 } 123