Calculate inner class access flags accurately
This commit is contained in:
@@ -240,7 +240,6 @@ public class AsmUtil {
|
|||||||
return NO_FLAG_PACKAGE_PRIVATE;
|
return NO_FLAG_PACKAGE_PRIVATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static int getVisibilityAccessFlagForAnonymous(@NotNull ClassDescriptor descriptor) {
|
public static int getVisibilityAccessFlagForAnonymous(@NotNull ClassDescriptor descriptor) {
|
||||||
if (isDeclarationInsideInlineFunction(descriptor)) {
|
if (isDeclarationInsideInlineFunction(descriptor)) {
|
||||||
return ACC_PUBLIC;
|
return ACC_PUBLIC;
|
||||||
@@ -248,7 +247,7 @@ public class AsmUtil {
|
|||||||
return NO_FLAG_PACKAGE_PRIVATE;
|
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
|
//NB: constructor context couldn't be inline
|
||||||
DeclarationDescriptor parentDeclaration = descriptor.getContainingDeclaration();
|
DeclarationDescriptor parentDeclaration = descriptor.getContainingDeclaration();
|
||||||
if (parentDeclaration instanceof SimpleFunctionDescriptor &&
|
if (parentDeclaration instanceof SimpleFunctionDescriptor &&
|
||||||
@@ -258,6 +257,31 @@ public class AsmUtil {
|
|||||||
return false;
|
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) {
|
public static int getDeprecatedAccessFlag(@NotNull MemberDescriptor descriptor) {
|
||||||
if (descriptor instanceof PropertyAccessorDescriptor) {
|
if (descriptor instanceof PropertyAccessorDescriptor) {
|
||||||
return KotlinBuiltIns.getInstance().isDeprecated(descriptor)
|
return KotlinBuiltIns.getInstance().isDeprecated(descriptor)
|
||||||
|
|||||||
@@ -285,27 +285,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeInnerClass(@NotNull ClassDescriptor innerClass) {
|
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();
|
DeclarationDescriptor containing = innerClass.getContainingDeclaration();
|
||||||
String outerClassInternalName = containing instanceof ClassDescriptor ? getInternalNameForImpl((ClassDescriptor) containing) : null;
|
String outerClassInternalName = containing instanceof ClassDescriptor ? getInternalNameForImpl((ClassDescriptor) containing) : null;
|
||||||
|
|
||||||
@@ -321,7 +300,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
innerClassInternalName = getInternalNameForImpl(innerClass);
|
innerClassInternalName = getInternalNameForImpl(innerClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, innerClassAccess);
|
v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, calculateInnerClassAccessFlags(innerClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -18,12 +18,15 @@ package org.jetbrains.jet.codegen;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.ConfigurationKind;
|
||||||
import org.jetbrains.jet.OutputFile;
|
import org.jetbrains.jet.OutputFile;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -78,7 +81,31 @@ public class InnerClassInfoGenTest extends CodegenTestCase {
|
|||||||
extractAndCompareInnerClasses("E$E2", innerE2);
|
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) {
|
private void extractAndCompareInnerClasses(@NotNull String className, @NotNull InnerClassAttribute... expectedInnerClasses) {
|
||||||
assertSameElements(extractInnerClasses(className), expectedInnerClasses);
|
assertSameElements(extractInnerClasses(className), expectedInnerClasses);
|
||||||
@@ -94,7 +121,7 @@ public class InnerClassInfoGenTest extends CodegenTestCase {
|
|||||||
|
|
||||||
reader.accept(new ClassVisitor(ASM5) {
|
reader.accept(new ClassVisitor(ASM5) {
|
||||||
@Override
|
@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));
|
result.add(new InnerClassAttribute(name, outerName, innerName, access));
|
||||||
}
|
}
|
||||||
}, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
}, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
||||||
@@ -104,8 +131,8 @@ public class InnerClassInfoGenTest extends CodegenTestCase {
|
|||||||
|
|
||||||
private static class InnerClassAttribute {
|
private static class InnerClassAttribute {
|
||||||
private final String name;
|
private final String name;
|
||||||
@Nullable private final String outerName;
|
private final String outerName;
|
||||||
@Nullable private final String innerName;
|
private final String innerName;
|
||||||
private final int access;
|
private final int access;
|
||||||
|
|
||||||
private InnerClassAttribute(@NotNull String name, @Nullable String outerName, @Nullable String innerName, 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
|
@Override
|
||||||
public String toString() {
|
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() ? "<empty>" : result.substring(1);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw UtilsPackage.rethrow(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user