Fix generation of InnerClasses/EnclosingMethod attributes

Generate InnerClasses for each outer class and each direct inner class in all
inheritors of MemberCodegen, i.e. for classes, objects, lambdas, local classes,
etc.

Also skip lambdas which will be inlined when calculating outer class for
EnclosingMethod

 #KT-5447 Fixed
 #KT-5478 Fixed
This commit is contained in:
Alexander Udalov
2015-01-19 19:54:36 +03:00
parent 432bdcd0fc
commit 20dc3438a2
7 changed files with 92 additions and 57 deletions
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage;
@@ -61,6 +62,7 @@ import java.util.Map;
import java.util.Set;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isInterface;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.ABI_VERSION_FIELD_NAME;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
@@ -863,15 +865,16 @@ public class AsmUtil {
@NotNull DeclarationDescriptor originalDescriptor,
@NotNull JetTypeMapper typeMapper
) {
BindingContext bindingContext = typeMapper.getBindingContext();
DeclarationDescriptor container = classDescriptor.getContainingDeclaration();
while (container != null) {
if (container instanceof ClassDescriptor) {
return typeMapper.mapClass((ClassDescriptor) container).getInternalName();
}
else if (CodegenBinding.isLocalFunOrLambda(container)) {
ClassDescriptor descriptor =
CodegenBinding.anonymousClassForFunction(typeMapper.getBindingContext(), (FunctionDescriptor) container);
return typeMapper.mapClass(descriptor).getInternalName();
else if (CodegenBinding.isLocalFunOrLambda(container) &&
!JvmCodegenUtil.isLambdaWhichWillBeInlined(bindingContext, container)) {
return asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) container).getInternalName();
}
container = container.getContainingDeclaration();
@@ -128,4 +128,10 @@ public abstract class ClassBodyCodegen extends MemberCodegen<JetClassOrObject> {
}
return Collections.emptyList();
}
@Nullable
@Override
protected ClassDescriptor classForInnerClassRecord() {
return DescriptorUtils.isTopLevelDeclaration(descriptor) ? null : descriptor;
}
}
@@ -146,7 +146,12 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
superInterfaceAsmTypes
);
v.visitSource(element.getContainingFile().getName(), null);
}
@Nullable
@Override
protected ClassDescriptor classForInnerClassRecord() {
return JvmCodegenUtil.isLambdaWhichWillBeInlined(bindingContext, funDescriptor) ? null : classDescriptor;
}
@Override
@@ -204,7 +209,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
@Override
protected void done() {
AsmUtil.writeOuterClassAndEnclosingMethod(classDescriptor, funDescriptor, typeMapper, v);
writeOuterClassAndEnclosingMethod(classDescriptor, funDescriptor, typeMapper, v);
super.done();
}
@@ -297,7 +302,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
int k = 1;
for (FieldInfo fieldInfo : args) {
k = AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, k, iv);
k = genAssignInstanceFieldFromParam(fieldInfo, k, iv);
}
iv.load(0, superClassAsmType);
@@ -69,7 +69,8 @@ import java.util.*;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.isLocalNamedFun;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.classDescriptorToDeclaration;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
@@ -209,10 +210,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
writeEnclosingMethod();
writeOuterClasses();
writeInnerClasses();
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor, null);
generateReflectionObjectFieldIfNeeded();
@@ -265,43 +262,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
}
private void writeInnerClasses() {
Collection<ClassDescriptor> result = bindingContext.get(INNER_CLASSES, descriptor);
if (result != null) {
for (ClassDescriptor innerClass : result) {
writeInnerClass(innerClass);
}
}
}
private void writeOuterClasses() {
// JVMS7 (4.7.6): a nested class or interface member will have InnerClasses information
// for each enclosing class and for each immediate member
DeclarationDescriptor inner = descriptor;
while (true) {
if (inner == null || isTopLevelDeclaration(inner)) {
break;
}
if (inner instanceof ClassDescriptor) {
writeInnerClass((ClassDescriptor) inner);
}
inner = inner.getContainingDeclaration();
}
}
private void writeInnerClass(@NotNull ClassDescriptor innerClass) {
DeclarationDescriptor containing = innerClass.getContainingDeclaration();
String outerClassInternalName =
containing instanceof ClassDescriptor ? typeMapper.mapClass((ClassDescriptor) containing).getInternalName() : null;
String innerName = isClassObject(innerClass)
? JvmAbi.CLASS_OBJECT_CLASS_NAME
: innerClass.getName().isSpecial() ? null : innerClass.getName().asString();
String innerClassInternalName = typeMapper.mapClass(innerClass).getInternalName();
v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, calculateInnerClassAccessFlags(innerClass));
}
@NotNull
private JvmClassSignature signature() {
BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS);
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
@@ -34,9 +35,13 @@ import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider;
import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.psi.JetFunctionLiteral;
import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression;
import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilPackage;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.InlineDescriptorUtils;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import org.jetbrains.kotlin.types.JetType;
@@ -224,4 +229,11 @@ public class JvmCodegenUtil {
? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
: descriptor;
}
public static boolean isLambdaWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
return declaration instanceof JetFunctionLiteral &&
declaration.getParent() instanceof JetFunctionLiteralExpression &&
InlineDescriptorUtils.isInlineLambda((JetFunctionLiteralExpression) declaration.getParent(), bindingContext, false);
}
}
@@ -48,14 +48,14 @@ import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
import static org.jetbrains.kotlin.codegen.AsmUtil.calculateInnerClassAccessFlags;
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
import static org.jetbrains.kotlin.descriptors.SourceElement.NO_SOURCE;
import static org.jetbrains.kotlin.resolve.BindingContext.VARIABLE;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isClassObject;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.TraitImpl;
@@ -73,6 +73,7 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
protected final BindingContext bindingContext;
private final MemberCodegen<?> parentCodegen;
private final ReifiedTypeParametersUsages reifiedTypeParametersUsages = new ReifiedTypeParametersUsages();
protected final Collection<ClassDescriptor> innerClasses = new LinkedHashSet<ClassDescriptor>();
protected ExpressionCodegen clInit;
private NameGenerator inlineNameGenerator;
@@ -120,12 +121,19 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
protected abstract void generateKotlinAnnotation();
@Nullable
protected ClassDescriptor classForInnerClassRecord() {
return null;
}
protected void done() {
if (clInit != null) {
clInit.v.visitInsn(RETURN);
FunctionCodegen.endVisit(clInit.v, "static initializer", element);
}
writeInnerClasses();
v.done();
}
@@ -203,6 +211,41 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
genClassOrObject(context, aClass, state, this);
}
private void writeInnerClasses() {
// JVMS7 (4.7.6): a nested class or interface member will have InnerClasses information
// for each enclosing class and for each immediate member
ClassDescriptor classDescriptor = classForInnerClassRecord();
if (classDescriptor != null) {
if (parentCodegen != null) {
parentCodegen.innerClasses.add(classDescriptor);
}
for (MemberCodegen<?> codegen = this; codegen != null; codegen = codegen.getParentCodegen()) {
ClassDescriptor outerClass = codegen.classForInnerClassRecord();
if (outerClass != null) {
innerClasses.add(outerClass);
}
}
}
for (ClassDescriptor innerClass : innerClasses) {
writeInnerClass(innerClass);
}
}
private void writeInnerClass(@NotNull ClassDescriptor innerClass) {
DeclarationDescriptor containing = innerClass.getContainingDeclaration();
String outerClassInternalName =
containing instanceof ClassDescriptor ? typeMapper.mapClass((ClassDescriptor) containing).getInternalName() : null;
String innerName = isClassObject(innerClass)
? JvmAbi.CLASS_OBJECT_CLASS_NAME
: innerClass.getName().isSpecial() ? null : innerClass.getName().asString();
String innerClassInternalName = typeMapper.mapClass(innerClass).getInternalName();
v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, calculateInnerClassAccessFlags(innerClass));
}
@NotNull
public NameGenerator getInlineNameGenerator() {
if (inlineNameGenerator == null) {
@@ -403,4 +446,9 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
public MemberCodegen<?> getParentCodegen() {
return parentCodegen;
}
@Override
public String toString() {
return context.toString();
}
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
import org.jetbrains.kotlin.codegen.SamType;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
@@ -60,8 +61,8 @@ public class CodegenBinding {
public static final WritableSlice<JetExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
public static final WritableSlice<JetWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.
<JetWhenExpression, WhenByEnumsMapping>sliceBuilder().build();
public static final WritableSlice<JetWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM =
Slices.<JetWhenExpression, WhenByEnumsMapping>sliceBuilder().build();
public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
Slices.<String, List<WhenByEnumsMapping>>sliceBuilder().build();
@@ -169,9 +170,9 @@ public class CodegenBinding {
trace.record(ASM_TYPE, classDescriptor, asmType);
trace.record(CLOSURE, classDescriptor, closure);
//TEMPORARY EAT INNER CLASS INFO FOR FUNCTION LITERALS
//TODO: we should understand that lambda/closure would be inlined and don't generate inner class record
if (enclosing != null && !(element instanceof JetFunctionLiteral)) {
// Note: at the moment this is needed for light classes only
// TODO: refactor this out
if (enclosing != null && !JvmCodegenUtil.isLambdaWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
recordInnerClass(trace, enclosing, classDescriptor);
}
}
@@ -183,7 +184,7 @@ public class CodegenBinding {
) {
Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
if (innerClasses == null) {
innerClasses = new ArrayList<ClassDescriptor>();
innerClasses = new ArrayList<ClassDescriptor>(1);
bindingTrace.record(INNER_CLASSES, outer, innerClasses);
}
innerClasses.add(inner);