Light class builder: do not generate methods delegating to DefaultImpls in kotlin classes

Class APIs from java point of view stays the same so we can avoid generating those methods
Otherwise we have to calculate all supertypes when getMethods() is called,
    which imposes severe performance penalties
We have to pretend these methods are not 'abstract' (also we consider them 'default' for safety)
    so java highlighting does not report "class should be abstract" for all inheritors
We have to manually report "class should be abstract" on some of the java inheritors,
    specifically those that are implementing interfaces directly
	    as opposed to extending kotlin classes implementing those interfaces
This commit is contained in:
Pavel V. Talanov
2017-03-30 17:44:19 +03:00
parent db294da24d
commit 4f701285b1
38 changed files with 510 additions and 45 deletions
@@ -83,6 +83,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL;
import static org.jetbrains.kotlin.types.Variance.INVARIANT;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isLocalFunction;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
@@ -1282,14 +1283,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
CodegenUtilKt.reportTarget6InheritanceErrorIfNeeded(descriptor, myClass.getPsiOrParent(), restrictedInheritance, state);
}
private void generateDelegationToDefaultImpl(@NotNull FunctionDescriptor traitFun, @NotNull FunctionDescriptor inheritedFun) {
private void generateDelegationToDefaultImpl(@NotNull FunctionDescriptor interfaceFun, @NotNull FunctionDescriptor inheritedFun) {
functionCodegen.generateMethod(
JvmDeclarationOriginKt.DelegationToDefaultImpls(descriptorToDeclaration(traitFun), traitFun),
new JvmDeclarationOrigin(CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL, descriptorToDeclaration(interfaceFun), interfaceFun),
inheritedFun,
new FunctionGenerationStrategy.CodegenBased(state) {
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
DeclarationDescriptor containingDeclaration = traitFun.getContainingDeclaration();
DeclarationDescriptor containingDeclaration = interfaceFun.getContainingDeclaration();
if (!DescriptorUtils.isInterface(containingDeclaration)) return;
DeclarationDescriptor declarationInheritedFun = inheritedFun.getContainingDeclaration();
@@ -1301,12 +1303,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
ClassDescriptor containingTrait = (ClassDescriptor) containingDeclaration;
Type traitImplType = typeMapper.mapDefaultImpls(containingTrait);
Method traitMethod = typeMapper.mapAsmMethod(traitFun.getOriginal(), OwnerKind.DEFAULT_IMPLS);
Method traitMethod = typeMapper.mapAsmMethod(interfaceFun.getOriginal(), OwnerKind.DEFAULT_IMPLS);
Type[] argTypes = signature.getAsmMethod().getArgumentTypes();
Type[] originalArgTypes = traitMethod.getArgumentTypes();
assert originalArgTypes.length == argTypes.length + 1 :
"Invalid trait implementation signature: " + signature + " vs " + traitMethod + " for " + traitFun;
"Invalid trait implementation signature: " + signature + " vs " + traitMethod + " for " + interfaceFun;
InstructionAdapter iv = codegen.v;
iv.load(0, OBJECT_TYPE);
@@ -29,8 +29,8 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.diagnostics.DelegationToDefaultImpls
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.org.objectweb.asm.MethodVisitor
@@ -113,7 +113,10 @@ class InterfaceImplBodyCodegen(
if (delegateTo is JavaMethodDescriptor) return
functionCodegen.generateMethod(
DelegationToDefaultImpls(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor),
JvmDeclarationOrigin(
JvmDeclarationOriginKind.DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL,
DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor
),
descriptor,
object : FunctionGenerationStrategy.CodegenBased(state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
@@ -27,7 +27,7 @@ import org.jetbrains.org.objectweb.asm.FieldVisitor
import org.jetbrains.org.objectweb.asm.MethodVisitor
abstract class SignatureCollectingClassBuilderFactory(
delegate: ClassBuilderFactory
delegate: ClassBuilderFactory, val shouldGenerate: (JvmDeclarationOrigin) -> Boolean
) : DelegatingClassBuilderFactory(delegate) {
protected abstract fun handleClashingSignatures(data: ConflictingJvmDeclarationsData)
@@ -57,11 +57,17 @@ abstract class SignatureCollectingClassBuilderFactory(
override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor {
signatures.putValue(RawSignature(name, desc, MemberKind.FIELD), origin)
if (!shouldGenerate(origin)) {
return AbstractClassBuilder.EMPTY_FIELD_VISITOR
}
return super.newField(origin, access, name, desc, signature, value)
}
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor {
signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin)
if (!shouldGenerate(origin)) {
return AbstractClassBuilder.EMPTY_METHOD_VISITOR
}
return super.newMethod(origin, access, name, desc, signature, exceptions)
}
@@ -37,7 +37,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
private val EXTERNAL_SOURCES_KINDS = arrayOf(
JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS,
JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL,
JvmDeclarationOriginKind.DELEGATION,
JvmDeclarationOriginKind.BRIDGE
)
@@ -58,8 +58,9 @@ class BuilderFactoryForDuplicateSignatureDiagnostics(
bindingContext: BindingContext,
private val diagnostics: DiagnosticSink,
fileClassesProvider: JvmFileClassesProvider,
moduleName: String
) : SignatureCollectingClassBuilderFactory(builderFactory) {
moduleName: String,
shouldGenerate: (JvmDeclarationOrigin) -> Boolean
) : SignatureCollectingClassBuilderFactory(builderFactory, shouldGenerate) {
// Avoid errors when some classes are not loaded for some reason
private val typeMapper = KotlinTypeMapper(
@@ -44,6 +44,8 @@ import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
import java.io.File
@@ -177,8 +179,10 @@ class GenerationState @JvmOverloads constructor(
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
::CoroutineTransformerClassBuilderFactory,
{ BuilderFactoryForDuplicateSignatureDiagnostics(
it, this.bindingContext, diagnostics, fileClassesProvider, this.moduleName
).apply { duplicateSignatureFactory = this } },
it, this.bindingContext, diagnostics,
fileClassesProvider, this.moduleName,
shouldGenerate = { !shouldOnlyCollectSignatures(it) }
).apply { duplicateSignatureFactory = this } },
{ BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) },
{ configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH)
?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it }
@@ -209,6 +213,9 @@ class GenerationState @JvmOverloads constructor(
fun destroy() {
interceptedBuilderFactory.close()
}
private fun shouldOnlyCollectSignatures(origin: JvmDeclarationOrigin)
= classBuilderMode == ClassBuilderMode.LIGHT_CLASSES && origin.originKind == CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL
}
private class LazyJvmDiagnostics(compute: () -> Diagnostics): Diagnostics {