/* * 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; import static org.junit.Assert.assertEquals; import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiTest; import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiTestRunner; import com.google.common.base.Joiner; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; import org.objectweb.asm.Opcodes; @RunWith(CompilerTreeApiTestRunner.class) public class AccessFlagsTest extends CompilerTreeApiTest { @Test public void testPublicFlagOnField() throws IOException { testFieldFlags("public", Opcodes.ACC_PUBLIC); } @Test public void testPublicFlagOnMethod() throws IOException { testMethodFlags("public", Opcodes.ACC_PUBLIC); } @Test public void testPublicFlagOnClass() throws IOException { testClassFlags("public", Opcodes.ACC_PUBLIC); } @Test public void testProtectedFlagOnField() throws IOException { testFieldFlags("protected", Opcodes.ACC_PROTECTED); } @Test public void testProtectedFlagOnMethod() throws IOException { testMethodFlags("protected", Opcodes.ACC_PROTECTED); } @Test public void testPrivateFlagOnField() throws IOException { testFieldFlags("private", Opcodes.ACC_PRIVATE); } @Test public void testPrivateFlagOnMethod() throws IOException { testMethodFlags("private", Opcodes.ACC_PRIVATE); } @Test public void testNoFlagForDefaultVisibilityOnField() throws IOException { testFieldFlags("", 0); } @Test public void testNoFlagForDefaultVisibilityOnMethod() throws IOException { testMethodFlags("", 0); } @Test public void testNoFlagForDefaultVisibilityOnClass() throws IOException { testClassFlags("", 0); } @Test public void testNoFlagForInterfaceDefaultMethod() throws IOException { compile(Joiner.on('\n').join("interface Foo {", " default void foo() { }", "}")); assertEquals( Opcodes.ACC_PUBLIC, AccessFlags.getAccessFlags(findMethod("foo", elements.getTypeElement("Foo")))); } @Test public void testStaticFlagOnField() throws IOException { testFieldFlags("static", Opcodes.ACC_STATIC); } @Test public void testStaticFlagOnMethod() throws IOException { testMethodFlags("static", Opcodes.ACC_STATIC); } @Test public void testStaticFlagOnClass() throws IOException { testTypeFlags( Joiner.on('\n').join("class Foo {", " static class Inner { }", "}"), "Foo.Inner", Opcodes.ACC_STATIC | Opcodes.ACC_SUPER); } @Test public void testFinalFlagOnField() throws IOException { testFieldFlags("final", Opcodes.ACC_FINAL); } @Test public void testFinalFlagOnMethod() throws IOException { testMethodFlags("final", Opcodes.ACC_FINAL); } @Test public void testFinalFlagOnClass() throws IOException { testClassFlags("final", Opcodes.ACC_FINAL); } @Test public void testVolatileFlag() throws IOException { testFieldFlags("volatile", Opcodes.ACC_VOLATILE); } @Test public void testTransientFlag() throws IOException { testFieldFlags("transient", Opcodes.ACC_TRANSIENT); } @Test public void testAbstractFlagOnClass() throws IOException { testClassFlags("abstract", Opcodes.ACC_ABSTRACT); } @Test public void testAbstractFlagOnMethod() throws IOException { compile(Joiner.on('\n').join("abstract class Foo {", " abstract void foo();", "}")); assertEquals( Opcodes.ACC_ABSTRACT, AccessFlags.getAccessFlags(findMethod("foo", elements.getTypeElement("Foo")))); } @Test public void testSynchronizedFlag() throws IOException { testMethodFlags("synchronized", Opcodes.ACC_SYNCHRONIZED); } @Test public void testFpStrictFlag() throws IOException { testMethodFlags("strictfp", Opcodes.ACC_STRICT); } @Test public void testNativeFlag() throws IOException { compile(Joiner.on('\n').join("class Foo {", " native void method();", "}")); assertEquals( Opcodes.ACC_NATIVE, AccessFlags.getAccessFlags(findMethod("method", elements.getTypeElement("Foo")))); } @Test public void testMultipleFlagsOnMethod() throws IOException { testMethodFlags("public static", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC); } @Test public void testMultipleFlagsOnField() throws IOException { testFieldFlags("public static", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC); } @Test public void testVarArgsFlag() throws IOException { compile(Joiner.on('\n').join("class Foo {", " void method(String... s) { }", "}")); assertEquals( Opcodes.ACC_VARARGS, AccessFlags.getAccessFlags(findMethod("method", elements.getTypeElement("Foo")))); } @Test public void testDeprecatedPseudoFlagOnField() throws IOException { testFieldFlags("@Deprecated", Opcodes.ACC_DEPRECATED); } @Test public void testDeprecatedPseudoFlagOnMethod() throws IOException { testMethodFlags("@Deprecated", Opcodes.ACC_DEPRECATED); } @Test public void testAnnotationTypeFlags() throws IOException { testTypeFlags( "@java.lang.annotation.Documented @interface Foo { }", "Foo", Opcodes.ACC_ANNOTATION | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT); } @Test public void testInterfaceTypeFlags() throws IOException { testTypeFlags("interface Foo { }", "Foo", Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT); } @Test public void testEnumTypeFlags() throws IOException { testTypeFlags( "enum Foo { Item }", "Foo", Opcodes.ACC_ENUM | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL); } /** * The {@link javax.lang.model.element.TypeElement} for an abstract Enum will *sometimes* report * that the enum is abstract, but not in all the cases where ACC_ABSTRACT needs to appear in the * class file. For this and other reasons we just never use the flag. */ @Test public void testEnumAbstractFlagIsRemoved() throws IOException { testTypeFlags( Joiner.on('\n') .join( "enum Foo {", " Value {", " int get() { return 3; }", " };", " abstract int get();", "}"), "Foo", Opcodes.ACC_ENUM | Opcodes.ACC_SUPER); } @Test public void testEnumVarFlags() throws IOException { compile("enum Foo { Item }"); assertEquals( Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_ENUM, AccessFlags.getAccessFlags(findField("Item", elements.getTypeElement("Foo")))); } private void testClassFlags(String modifiers, int expectedFlags) throws IOException { testTypeFlags( String.format("%s class Foo { }", modifiers), "Foo", expectedFlags | Opcodes.ACC_SUPER); } private void testTypeFlags(String content, String typeName, int expectedFlags) throws IOException { compile(content); assertNoErrors(); assertEquals(expectedFlags, AccessFlags.getAccessFlags(elements.getTypeElement(typeName))); } private void testMethodFlags(String modifiers, int expectedFlags) throws IOException { compile( Joiner.on('\n') .join("class Foo {", String.format(" %s void method() { }", modifiers), "}")); assertNoErrors(); assertEquals( expectedFlags, AccessFlags.getAccessFlags(findMethod("method", elements.getTypeElement("Foo")))); } private void testFieldFlags(String modifiers, int expectedFlags) throws IOException { compile( Joiner.on('\n').join("class Foo {", String.format(" %s int field = 0;", modifiers), "}")); assertNoErrors(); assertEquals( expectedFlags, AccessFlags.getAccessFlags(findField("field", elements.getTypeElement("Foo")))); } }