/* * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.jvm.java.abi.source; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiParameterized; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.util.stream.Collectors; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(CompilerTreeApiParameterized.class) public class TreeBackedExecutableElementTest extends CompilerTreeApiParameterizedTest { @Test public void testGetReturnType() throws IOException { compile(Joiner.on('\n').join("abstract class Foo {", " public abstract String foo();", "}")); ExecutableElement element = findMethod("foo", elements.getTypeElement("Foo")); TypeMirror stringType = elements.getTypeElement("java.lang.String").asType(); assertSameType(stringType, element.getReturnType()); } @Test public void testGetVoidReturnType() throws IOException { compile(Joiner.on('\n').join("abstract class Foo {", " public void foo();", "}")); ExecutableElement element = findMethod("foo", elements.getTypeElement("Foo")); TypeMirror voidType = types.getNoType(TypeKind.VOID); assertSameType(voidType, element.getReturnType()); } @Test public void testGetConstructorReturnType() throws IOException { compile(Joiner.on('\n').join("class Foo {", "}")); ExecutableElement element = findDefaultConstructor(elements.getTypeElement("Foo")); TypeMirror voidType = types.getNoType(TypeKind.VOID); assertSameType(voidType, element.getReturnType()); } /** The docs claim this should be a NoType of TypeKind.NONE. The docs lie. */ @Test public void testGetReceiverTypeOfStaticIsNull() throws IOException { compile(Joiner.on('\n').join("class Foo {", " static void foo() { }", "}")); ExecutableElement element = findMethod("foo", elements.getTypeElement("Foo")); TypeMirror receiverType = element.getReceiverType(); assertNull(receiverType); } /** The docs claim this should be a NoType of TypeKind.NONE. The docs lie. */ @Test public void testGetReceiverTypeOfTopLevelConstructorIsNull() throws IOException { compile(Joiner.on('\n').join("class Foo {", " Foo () { }", "}")); ExecutableElement element = findDefaultConstructor(elements.getTypeElement("Foo")); TypeMirror receiverType = element.getReceiverType(); assertNull(receiverType); } @Test public void testGetReceiverTypeOfInnerConstructorIsEnclosingType() throws IOException { compile( Joiner.on('\n') .join("class Foo {", " class Bar {", " Bar (Foo Foo.this) { }", " }", "}")); ExecutableElement element = findDefaultConstructor(elements.getTypeElement("Foo.Bar")); TypeMirror receiverType = element.getReceiverType(); assertSameType(elements.getTypeElement("Foo").asType(), receiverType); } @Test public void testGetReceiverTypeOfInnerConstructorIsEnclosingTypeGeneric() throws IOException { compile( Joiner.on('\n') .join("class Foo<T> {", " class Bar {", " Bar (Foo<T> Foo.this) { }", " }", "}")); ExecutableElement element = findDefaultConstructor(elements.getTypeElement("Foo.Bar")); TypeMirror receiverType = element.getReceiverType(); assertSameType(elements.getTypeElement("Foo").asType(), receiverType); } @Test public void testGetReceiverTypeOfInstanceMethodIsEnclosingType() throws IOException { compile(Joiner.on('\n').join("class Foo {", " void foo(Foo this) { }", "}")); TypeElement fooClass = elements.getTypeElement("Foo"); ExecutableElement fooMethod = findMethod("foo", fooClass); TypeMirror receiverType = fooMethod.getReceiverType(); assertSameType(fooClass.asType(), receiverType); } @Test public void testGetReceiverTypeOfInstanceMethodIsEnclosingTypeGeneric() throws IOException { compile(Joiner.on('\n').join("class Foo<T> {", " void foo(Foo<T> this) { }", "}")); TypeElement fooClass = elements.getTypeElement("Foo"); ExecutableElement fooMethod = findMethod("foo", fooClass); TypeMirror receiverType = fooMethod.getReceiverType(); assertSameType(fooClass.asType(), receiverType); } @Test public void testGetParameters() throws IOException { compile( Joiner.on('\n') .join("abstract class Foo {", " public abstract void foo(String s, int i);", "}")); VariableElement sParameter = findParameter("s", findMethod("foo", elements.getTypeElement("Foo"))); VariableElement iParameter = findParameter("i", findMethod("foo", elements.getTypeElement("Foo"))); assertSameType(elements.getTypeElement("java.lang.String").asType(), sParameter.asType()); assertSameType(types.getPrimitiveType(TypeKind.INT), iParameter.asType()); } @Test public void testNotVarArgs() throws IOException { compile( Joiner.on('\n') .join("abstract class Foo {", " public abstract void foo(String[] strings);", "}")); TypeMirror stringArrayType = types.getArrayType(elements.getTypeElement("java.lang.String").asType()); ExecutableElement method = findMethod("foo", elements.getTypeElement("Foo")); assertFalse(method.isVarArgs()); assertSameType(stringArrayType, findParameter("strings", method).asType()); } @Test public void testVarArgs() throws IOException { compile( Joiner.on('\n') .join("abstract class Foo {", " public abstract void foo(String... strings);", "}")); TypeMirror stringArrayType = types.getArrayType(elements.getTypeElement("java.lang.String").asType()); ExecutableElement method = findMethod("foo", elements.getTypeElement("Foo")); assertTrue(method.isVarArgs()); assertSameType(stringArrayType, findParameter("strings", method).asType()); } @Test public void testNotDefault() throws IOException { compile(Joiner.on('\n').join("interface Foo {", " void foo();", "}")); assertFalse(findMethod("foo", elements.getTypeElement("Foo")).isDefault()); } @Test public void testDefault() throws IOException { compile(Joiner.on('\n').join("interface Foo {", " default void foo() {};", "}")); assertTrue(findMethod("foo", elements.getTypeElement("Foo")).isDefault()); } @Test public void testGetThrownTypes() throws IOException { compile( Joiner.on('\n') .join( "abstract class Foo {", " public abstract void foo() throws Exception, RuntimeException;", "}")); ExecutableElement method = findMethod("foo", elements.getTypeElement("Foo")); assertThat( method.getThrownTypes().stream().map(TypeMirror::toString).collect(Collectors.toList()), Matchers.contains( elements.getTypeElement("java.lang.Exception").asType().toString(), elements.getTypeElement("java.lang.RuntimeException").asType().toString())); } /** * See {@link com.facebook.buck.jvm.java.abi.source.TreeBackedAnnotationValueTest} for lots of * tests of methods with default values. */ @Test public void testGetDefaultValueNoDefault() throws IOException { compile(Joiner.on('\n').join("@interface Foo {", " int value();", "}")); assertThat( findMethod("value", elements.getTypeElement("Foo")).getDefaultValue(), Matchers.nullValue()); } /** * See {@link TreeBackedTypeParameterElementTest} for lots of tests of the type parameter elements * themselves. */ @Test public void testGetTypeParameters() throws IOException { compile(Joiner.on('\n').join("class Foo {", " public <T, U> void foo(T t, U u) { }", "}")); assertThat( findMethod("foo", elements.getTypeElement("Foo")) .getTypeParameters() .stream() .map(TypeParameterElement::getSimpleName) .map(Name::toString) .collect(Collectors.toList()), Matchers.contains("T", "U")); } @Test public void testAsType() throws IOException { compile( Joiner.on('\n') .join( "abstract class Foo {", "public abstract <E extends Exception> int foo(Foo this, String s, long l, E e) throws E;", "}")); TypeElement fooTypeElement = elements.getTypeElement("Foo"); ExecutableElement fooExecutableElement = findMethod("foo", fooTypeElement); TypeMirror typeMirror = fooExecutableElement.asType(); TypeMirror typeParam = fooExecutableElement.getTypeParameters().get(0).asType(); assertEquals(TypeKind.EXECUTABLE, typeMirror.getKind()); ExecutableType type = (ExecutableType) typeMirror; assertSameType(types.getPrimitiveType(TypeKind.INT), type.getReturnType()); assertSameType(fooTypeElement.asType(), type.getReceiverType()); assertThat( type.getParameterTypes(), Matchers.contains( ImmutableList.of( sameType(elements.getTypeElement("java.lang.String").asType()), sameType(types.getPrimitiveType(TypeKind.LONG)), sameType(typeParam)))); assertThat(type.getThrownTypes(), Matchers.contains(sameType(typeParam))); } }