JVM KT-36984 SAM wrappers are anonymous inner classes

This commit is contained in:
Dmitry Petrov
2020-12-18 14:27:53 +03:00
parent 57dd9fc87a
commit 0dff583070
14 changed files with 107 additions and 14 deletions
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.codegen.inline.SourceMapper;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
import org.jetbrains.kotlin.config.JvmDefaultMode;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotatedImpl;
@@ -83,7 +84,9 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
private final MemberCodegen<?> parentCodegen;
private final ReifiedTypeParametersUsages reifiedTypeParametersUsages = new ReifiedTypeParametersUsages();
private final Collection<ClassDescriptor> innerClasses = new LinkedHashSet<>();
private final Collection<SyntheticInnerClassInfo> syntheticInnerClasses = new LinkedHashSet<>();
private ExpressionCodegen clInit;
private NameGenerator inlineNameGenerator;
@@ -316,6 +319,10 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
genClassOrObject(context, descriptor.getSyntheticDeclaration(), state, this, descriptor);
}
public void addSyntheticAnonymousInnerClass(SyntheticInnerClassInfo syntheticInnerClassInfo) {
syntheticInnerClasses.add(syntheticInnerClassInfo);
}
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
@@ -331,6 +338,9 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
for (ClassDescriptor innerClass : innerClasses) {
writeInnerClass(innerClass);
}
for (SyntheticInnerClassInfo syntheticInnerClass : syntheticInnerClasses) {
v.visitInnerClass(syntheticInnerClass.getInternalName(), null, null, syntheticInnerClass.getFlags());
}
}
protected void addParentsToInnerClassesIfNeeded(@NotNull Collection<ClassDescriptor> innerClasses) {
@@ -376,18 +386,12 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
}
protected void writeOuterClassAndEnclosingMethod() {
CodegenContext context = this.context.getParentContext();
while (context instanceof InlineLambdaContext) {
// If this is a lambda which will be inlined, skip its MethodContext and enclosing ClosureContext
//noinspection ConstantConditions
context = context.getParentContext().getParentContext();
}
CodegenContext<?> context = getNonInlineOuterContext(this.context.getParentContext());
assert context != null : "Outermost context can't be null: " + this.context;
Type enclosingAsmType = computeOuterClass(context);
Type enclosingAsmType = computeOuterClass(typeMapper, state.getJvmDefaultMode(), element, context);
if (enclosingAsmType != null) {
Method method = computeEnclosingMethod(context);
Method method = computeEnclosingMethod(typeMapper, context);
v.visitOuterClass(
enclosingAsmType.getInternalName(),
@@ -397,15 +401,31 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
}
}
public static CodegenContext<?> getNonInlineOuterContext(CodegenContext<?> parentContext) {
CodegenContext<?> context = parentContext;
while (context instanceof InlineLambdaContext) {
// If this is a lambda which will be inlined, skip its MethodContext and enclosing ClosureContext
//noinspection ConstantConditions
context = context.getParentContext().getParentContext();
}
return context;
}
@Nullable
private Type computeOuterClass(@NotNull CodegenContext<?> context) {
public static Type computeOuterClass(
@NotNull KotlinTypeMapper typeMapper,
@NotNull JvmDefaultMode jvmDefaultMode,
@NotNull KtPureElement element,
@NotNull CodegenContext<?> context
) {
CodegenContext<? extends ClassOrPackageFragmentDescriptor> outermost = context.getClassOrPackageParentContext();
if (outermost instanceof ClassContext) {
ClassDescriptor classDescriptor = ((ClassContext) outermost).getContextDescriptor();
if (context instanceof MethodContext) {
FunctionDescriptor functionDescriptor = ((MethodContext) context).getFunctionDescriptor();
if (isInterface(functionDescriptor.getContainingDeclaration()) && !JvmAnnotationUtilKt
.isCompiledToJvmDefault(functionDescriptor, state.getJvmDefaultMode())) {
if (isInterface(functionDescriptor.getContainingDeclaration()) &&
!JvmAnnotationUtilKt.isCompiledToJvmDefault(functionDescriptor, jvmDefaultMode)
) {
return typeMapper.mapDefaultImpls(classDescriptor);
}
}
@@ -425,7 +445,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
}
@Nullable
private Method computeEnclosingMethod(@NotNull CodegenContext context) {
public static Method computeEnclosingMethod(@NotNull KotlinTypeMapper typeMapper, @NotNull CodegenContext context) {
if (context instanceof MethodContext) {
FunctionDescriptor functionDescriptor = ((MethodContext) context).getFunctionDescriptor();
if ("<clinit>".equals(functionDescriptor.getName().asString())) {
@@ -20,6 +20,8 @@ import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.context.ClassContext;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
@@ -62,6 +64,7 @@ public class SamWrapperCodegen {
private final SamType samType;
private final MemberCodegen<?> parentCodegen;
private final int visibility;
private final int classFlags;
public static final String SAM_WRAPPER_SUFFIX = "$0";
public SamWrapperCodegen(
@@ -76,6 +79,7 @@ public class SamWrapperCodegen {
this.samType = samType;
this.parentCodegen = parentCodegen;
visibility = isInsideInline ? ACC_PUBLIC : NO_FLAG_PACKAGE_PRIVATE;
classFlags = visibility | ACC_FINAL | ACC_SUPER;
}
@NotNull
@@ -121,7 +125,7 @@ public class SamWrapperCodegen {
cv.defineClass(
file,
state.getClassFileVersion(),
ACC_FINAL | ACC_SUPER | visibility,
classFlags,
asmType.getInternalName(),
null,
OBJECT_TYPE.getInternalName(),
@@ -131,6 +135,8 @@ public class SamWrapperCodegen {
WriteAnnotationUtilKt.writeSyntheticClassMetadata(cv, state);
generateInnerClassInformation(file, asmType, cv);
// e.g. ASM type for Function2
Type functionAsmType = typeMapper.mapType(functionType);
@@ -160,6 +166,24 @@ public class SamWrapperCodegen {
return asmType;
}
private void generateInnerClassInformation(@NotNull KtFile file, Type asmType, ClassBuilder cv) {
parentCodegen.addSyntheticAnonymousInnerClass(new SyntheticInnerClassInfo(asmType.getInternalName(), classFlags));
FieldOwnerContext<?> parentContext = parentCodegen.context;
CodegenContext<?> outerContext = MemberCodegen.getNonInlineOuterContext(parentContext);
assert outerContext != null :
"Outer context for SAM wrapper " + asmType.getInternalName() + " is null, parentContext:" + parentContext;
Type outerClassType = MemberCodegen.computeOuterClass(state.getTypeMapper(), state.getJvmDefaultMode(), file, outerContext);
assert outerClassType != null :
"Outer class for SAM wrapper " + asmType.getInternalName() + " is null, parentContext:" + parentContext;
Method enclosingMethod = MemberCodegen.computeEnclosingMethod(state.getTypeMapper(), outerContext);
cv.visitOuterClass(
outerClassType.getInternalName(),
enclosingMethod == null ? null : enclosingMethod.getName(),
enclosingMethod == null ? null : enclosingMethod.getDescriptor()
);
cv.visitInnerClass(asmType.getInternalName(), null, null, classFlags);
}
private void generateConstructor(Type ownerType, Type functionType, ClassBuilder cv) {
MethodVisitor mv = cv.newMethod(JvmDeclarationOriginKt.OtherOrigin(samType.getClassDescriptor()),
visibility, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, functionType), null, null);
@@ -0,0 +1,8 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen
data class SyntheticInnerClassInfo(val internalName: String, val flags: Int)