diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index 8f737e6b5fb..62ddfaa88f1 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -240,7 +240,6 @@ public class AsmUtil { return NO_FLAG_PACKAGE_PRIVATE; } - public static int getVisibilityAccessFlagForAnonymous(@NotNull ClassDescriptor descriptor) { if (isDeclarationInsideInlineFunction(descriptor)) { return ACC_PUBLIC; @@ -248,7 +247,7 @@ public class AsmUtil { return NO_FLAG_PACKAGE_PRIVATE; } - public static boolean isDeclarationInsideInlineFunction(@NotNull ClassDescriptor descriptor) { + private static boolean isDeclarationInsideInlineFunction(@NotNull ClassDescriptor descriptor) { //NB: constructor context couldn't be inline DeclarationDescriptor parentDeclaration = descriptor.getContainingDeclaration(); if (parentDeclaration instanceof SimpleFunctionDescriptor && @@ -258,6 +257,31 @@ public class AsmUtil { return false; } + public static int calculateInnerClassAccessFlags(@NotNull ClassDescriptor innerClass) { + return getVisibilityAccessFlag(innerClass) | + innerAccessFlagsForModalityAndKind(innerClass) | + (innerClass.isInner() ? 0 : ACC_STATIC); + } + + private static int innerAccessFlagsForModalityAndKind(@NotNull ClassDescriptor innerClass) { + switch (innerClass.getKind()) { + case TRAIT: + return ACC_ABSTRACT | ACC_INTERFACE; + case ENUM_CLASS: + return ACC_FINAL | ACC_ENUM; + case ANNOTATION_CLASS: + return ACC_ABSTRACT | ACC_ANNOTATION | ACC_INTERFACE; + default: + if (innerClass.getModality() == Modality.FINAL) { + return ACC_FINAL; + } + else if (innerClass.getModality() == Modality.ABSTRACT) { + return ACC_ABSTRACT; + } + } + return 0; + } + public static int getDeprecatedAccessFlag(@NotNull MemberDescriptor descriptor) { if (descriptor instanceof PropertyAccessorDescriptor) { return KotlinBuiltIns.getInstance().isDeprecated(descriptor) diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 8354f20ad1a..7ac90062dff 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -285,27 +285,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } private void writeInnerClass(@NotNull ClassDescriptor innerClass) { - // TODO: proper access - int innerClassAccess = getVisibilityAccessFlag(innerClass); - if (innerClass.getModality() == Modality.FINAL) { - innerClassAccess |= ACC_FINAL; - } - else if (innerClass.getModality() == Modality.ABSTRACT) { - innerClassAccess |= ACC_ABSTRACT; - } - - if (innerClass.getKind() == ClassKind.TRAIT) { - innerClassAccess |= ACC_INTERFACE; - } - else if (innerClass.getKind() == ClassKind.ENUM_CLASS) { - innerClassAccess |= ACC_ENUM; - } - - if (!innerClass.isInner()) { - innerClassAccess |= ACC_STATIC; - } - - // TODO: cache internal names DeclarationDescriptor containing = innerClass.getContainingDeclaration(); String outerClassInternalName = containing instanceof ClassDescriptor ? getInternalNameForImpl((ClassDescriptor) containing) : null; @@ -321,7 +300,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { innerClassInternalName = getInternalNameForImpl(innerClass); } - v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, innerClassAccess); + v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, calculateInnerClassAccessFlags(innerClass)); } @NotNull diff --git a/compiler/testData/codegen/innerClassInfo/innerAccessFlags.kt b/compiler/testData/codegen/innerClassInfo/innerAccessFlags.kt new file mode 100644 index 00000000000..b902284e2b8 --- /dev/null +++ b/compiler/testData/codegen/innerClassInfo/innerAccessFlags.kt @@ -0,0 +1,21 @@ +class A { + // Kind + annotation class Annotation + enum class Enum + trait Trait + + // Modality + open class OpenStaticClass + class FinalStaticClass + abstract class AbstractStaticClass + + open inner class OpenInnerClass + inner class FinalInnerClass + abstract inner class AbstractInnerClass + + // Visibility + private open inner class PrivateClass + protected open inner class ProtectedClass + internal open inner class InternalClass + public open inner class PublicClass +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/InnerClassInfoGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/InnerClassInfoGenTest.java index 243aea0badc..bf01f1bb60d 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/InnerClassInfoGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/InnerClassInfoGenTest.java @@ -18,12 +18,15 @@ package org.jetbrains.jet.codegen; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.org.objectweb.asm.ClassReader; -import org.jetbrains.org.objectweb.asm.ClassVisitor; import org.jetbrains.jet.ConfigurationKind; import org.jetbrains.jet.OutputFile; import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.jet.utils.UtilsPackage; +import org.jetbrains.org.objectweb.asm.ClassReader; +import org.jetbrains.org.objectweb.asm.ClassVisitor; +import org.jetbrains.org.objectweb.asm.Opcodes; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -78,7 +81,31 @@ public class InnerClassInfoGenTest extends CodegenTestCase { extractAndCompareInnerClasses("E$E2", innerE2); } + public void testInnerAccessFlags() { + checkAccess("A", "Annotation", ACC_PUBLIC | ACC_STATIC | ACC_INTERFACE | ACC_ABSTRACT | ACC_ANNOTATION); + checkAccess("A", "Enum", ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_ENUM); + checkAccess("A", "Trait", ACC_PUBLIC | ACC_STATIC | ACC_INTERFACE | ACC_ABSTRACT); + checkAccess("A", "OpenStaticClass", ACC_PUBLIC | ACC_STATIC); + checkAccess("A", "FinalStaticClass", ACC_PUBLIC | ACC_STATIC | ACC_FINAL); + checkAccess("A", "AbstractStaticClass", ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT); + checkAccess("A", "OpenInnerClass", ACC_PUBLIC); + checkAccess("A", "FinalInnerClass", ACC_PUBLIC | ACC_FINAL); + checkAccess("A", "AbstractInnerClass", ACC_PUBLIC | ACC_ABSTRACT); + + checkAccess("A", "PrivateClass", ACC_PRIVATE); + checkAccess("A", "ProtectedClass", ACC_PROTECTED); + checkAccess("A", "InternalClass", ACC_PUBLIC); + checkAccess("A", "PublicClass", ACC_PUBLIC); + } + + + + private void checkAccess(@NotNull String outerName, @NotNull String innerName, int accessFlags) { + String name = outerName + "$" + innerName; + InnerClassAttribute attribute = new InnerClassAttribute(name, outerName, innerName, accessFlags); + extractAndCompareInnerClasses(name, attribute); + } private void extractAndCompareInnerClasses(@NotNull String className, @NotNull InnerClassAttribute... expectedInnerClasses) { assertSameElements(extractInnerClasses(className), expectedInnerClasses); @@ -94,7 +121,7 @@ public class InnerClassInfoGenTest extends CodegenTestCase { reader.accept(new ClassVisitor(ASM5) { @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { + public void visitInnerClass(@NotNull String name, String outerName, String innerName, int access) { result.add(new InnerClassAttribute(name, outerName, innerName, access)); } }, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES); @@ -104,8 +131,8 @@ public class InnerClassInfoGenTest extends CodegenTestCase { private static class InnerClassAttribute { private final String name; - @Nullable private final String outerName; - @Nullable private final String innerName; + private final String outerName; + private final String innerName; private final int access; private InnerClassAttribute(@NotNull String name, @Nullable String outerName, @Nullable String innerName, int access) { @@ -141,7 +168,27 @@ public class InnerClassInfoGenTest extends CodegenTestCase { @Override public String toString() { - return String.format("InnerClass(name=%s, outerName=%s, innerName=%s, access=%d)", name, outerName, innerName, access); + return String.format("InnerClass(name=%s, outerName=%s, innerName=%s, access=%s)", + name, outerName, innerName, renderAccess(access)); + } + + @NotNull + private static String renderAccess(int access) { + try { + StringBuilder sb = new StringBuilder(); + for (Field field : Opcodes.class.getDeclaredFields()) { + String name = field.getName(); + if (name.startsWith("ACC_") && (access & field.getInt(null)) != 0) { + sb.append("|"); + sb.append(name); + } + } + String result = sb.toString(); + return result.isEmpty() ? "" : result.substring(1); + } + catch (Exception e) { + throw UtilsPackage.rethrow(e); + } } } }