1 /* 2 * Copyright 2018 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.binder.bytecode; 18 19 import static com.google.common.collect.Iterables.getLast; 20 import static com.google.common.collect.MoreCollectors.onlyElement; 21 import static com.google.common.truth.Truth.assertThat; 22 import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; 23 import static java.util.Objects.requireNonNull; 24 25 import com.google.common.collect.ImmutableMap; 26 import com.google.common.io.ByteStreams; 27 import com.google.turbine.binder.bound.TurbineClassValue; 28 import com.google.turbine.binder.bound.TypeBoundClass; 29 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; 30 import com.google.turbine.binder.env.CompoundEnv; 31 import com.google.turbine.binder.env.Env; 32 import com.google.turbine.binder.env.SimpleEnv; 33 import com.google.turbine.binder.sym.ClassSymbol; 34 import com.google.turbine.type.Type; 35 import com.google.turbine.type.Type.ClassTy; 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.io.Serializable; 39 import java.io.UncheckedIOException; 40 import java.util.List; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.junit.runners.JUnit4; 44 45 @RunWith(JUnit4.class) 46 public class BytecodeBoundClassTest { 47 48 static class NoInterfaces {} 49 50 abstract static class RawInterfaces implements Serializable {} 51 52 abstract static class GenericInterfaces implements List<String> {} 53 54 @Test interfaceTypes()55 public void interfaceTypes() { 56 TypeBoundClass noInterfaces = getBytecodeBoundClass(NoInterfaces.class); 57 TypeBoundClass rawInterfaces = getBytecodeBoundClass(RawInterfaces.class); 58 TypeBoundClass genericInterfaces = getBytecodeBoundClass(GenericInterfaces.class); 59 60 assertThat(noInterfaces.interfaceTypes()).isEmpty(); 61 62 assertThat(rawInterfaces.interfaceTypes()).hasSize(1); 63 assertThat(((ClassTy) rawInterfaces.interfaceTypes().get(0)).sym()) 64 .isEqualTo(new ClassSymbol("java/io/Serializable")); 65 assertThat(getLast(((ClassTy) rawInterfaces.interfaceTypes().get(0)).classes()).targs()) 66 .isEmpty(); 67 68 assertThat(genericInterfaces.interfaceTypes()).hasSize(1); 69 assertThat(((ClassTy) genericInterfaces.interfaceTypes().get(0)).sym()) 70 .isEqualTo(new ClassSymbol("java/util/List")); 71 assertThat(getLast(((ClassTy) genericInterfaces.interfaceTypes().get(0)).classes()).targs()) 72 .hasSize(1); 73 assertThat( 74 ((ClassTy) 75 getLast(((ClassTy) genericInterfaces.interfaceTypes().get(0)).classes()) 76 .targs() 77 .get(0)) 78 .sym()) 79 .isEqualTo(new ClassSymbol("java/lang/String")); 80 } 81 82 static class HasMethod { 83 @Deprecated foo(@eprecated X bar, Y baz)84 <X, Y extends X, Z extends Throwable> X foo(@Deprecated X bar, Y baz) throws IOException, Z { 85 return null; 86 } 87 } 88 89 @Test methodTypes()90 public void methodTypes() { 91 MethodInfo m = 92 getBytecodeBoundClass(HasMethod.class).methods().stream() 93 .filter(x -> x.name().equals("foo")) 94 .collect(onlyElement()); 95 96 assertThat(m.tyParams()).hasSize(3); 97 assertThat(m.parameters().get(0).annotations()).hasSize(1); 98 assertThat(m.parameters().get(0).name()).isEqualTo("bar"); 99 assertThat(m.exceptions()).hasSize(2); 100 } 101 102 @interface VoidAnno { a()103 Class<?> a() default void.class; 104 b()105 Class<?> b() default int[].class; 106 } 107 108 @Test voidAnno()109 public void voidAnno() { 110 BytecodeBoundClass c = getBytecodeBoundClass(VoidAnno.class); 111 112 assertThat(c.methods()).hasSize(2); 113 assertThat(((TurbineClassValue) c.methods().get(0).defaultValue()).type().tyKind()) 114 .isEqualTo(Type.TyKind.VOID_TY); 115 assertThat(((TurbineClassValue) c.methods().get(1).defaultValue()).type().tyKind()) 116 .isEqualTo(Type.TyKind.ARRAY_TY); 117 } 118 toByteArrayOrDie(InputStream is)119 private static byte[] toByteArrayOrDie(InputStream is) { 120 try { 121 return ByteStreams.toByteArray(is); 122 } catch (IOException e) { 123 throw new UncheckedIOException(e); 124 } 125 } 126 getBytecodeBoundClass( Env<ClassSymbol, BytecodeBoundClass> env, Class<?> clazz)127 private BytecodeBoundClass getBytecodeBoundClass( 128 Env<ClassSymbol, BytecodeBoundClass> env, Class<?> clazz) { 129 String name = clazz.getName().replace('.', '/'); 130 String path = "/" + name + ".class"; 131 return new BytecodeBoundClass( 132 new ClassSymbol(name), 133 () -> toByteArrayOrDie(requireNonNull(getClass().getResourceAsStream(path), path)), 134 env, 135 "test.jar"); 136 } 137 getBytecodeBoundClass(Class<?> clazz)138 private BytecodeBoundClass getBytecodeBoundClass(Class<?> clazz) { 139 Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env(); 140 env = 141 CompoundEnv.of(env) 142 .append( 143 new SimpleEnv<>( 144 ImmutableMap.of( 145 new ClassSymbol(BytecodeBoundClass.class.getName().replace('.', '/')), 146 getBytecodeBoundClass(env, BytecodeBoundClassTest.class)))); 147 return getBytecodeBoundClass(env, clazz); 148 } 149 } 150