From dbf9ff29a94aa31407b82b56351faaf7f3a14763 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 3 Dec 2018 17:56:18 +0300 Subject: [PATCH] Support data classes in ultra-light classes --- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 13 +- .../kotlin/codegen/JvmCodegenUtil.java | 2 +- .../kotlin/asJava/classes/ultraLightMethod.kt | 35 ++++ .../kotlin/asJava/classes/ultraLightPsi.kt | 42 +++++ .../kotlin/asJava/classes/ultraLightUtils.kt | 166 ++++++++++++++++-- .../asJava/ultraLightClasses/dataClasses.kt | 2 - .../resolve/IDELightClassGenerationSupport.kt | 6 +- 7 files changed, 248 insertions(+), 18 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 1bd7d0afc34..1fc9df399b9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver; import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt; import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker; +import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.jvm.*; import org.jetbrains.kotlin.resolve.jvm.checkers.DalvikIdentifierUtils; @@ -311,7 +312,11 @@ public class AsmUtil { } public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind, GenerationState state) { - int flags = getCommonCallableFlags(functionDescriptor, kind, state); + return getMethodAsmFlags(functionDescriptor, kind, state.getDeprecationProvider()); + } + + public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind, DeprecationResolver deprecationResolver) { + int flags = getCommonCallableFlags(functionDescriptor, kind, deprecationResolver); for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.METHOD_FLAGS) { flags |= flagAnnotation.getJvmFlag(functionDescriptor.getOriginal()); @@ -361,18 +366,18 @@ public class AsmUtil { } public static int getCommonCallableFlags(FunctionDescriptor functionDescriptor, @NotNull GenerationState state) { - return getCommonCallableFlags(functionDescriptor, null, state); + return getCommonCallableFlags(functionDescriptor, null, state.getDeprecationProvider()); } private static int getCommonCallableFlags( FunctionDescriptor functionDescriptor, @Nullable OwnerKind kind, - @NotNull GenerationState state + @NotNull DeprecationResolver deprecationResolver ) { int flags = getVisibilityAccessFlag(functionDescriptor, kind); flags |= getVarargsFlag(functionDescriptor); flags |= getDeprecatedAccessFlag(functionDescriptor); - if (state.getDeprecationProvider().isDeprecatedHidden(functionDescriptor) || + if (deprecationResolver.isDeprecatedHidden(functionDescriptor) || (functionDescriptor.isSuspend()) && functionDescriptor.getVisibility().equals(Visibilities.PRIVATE)) { flags |= ACC_SYNTHETIC; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index b4af23286cb..34d0d744d35 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -74,7 +74,7 @@ public class JvmCodegenUtil { return !hasJvmDefaultAnnotation(descriptor); } - public static boolean isJvmInterface(DeclarationDescriptor descriptor) { + public static boolean isJvmInterface(@Nullable DeclarationDescriptor descriptor) { if (descriptor instanceof ClassDescriptor) { ClassKind kind = ((ClassDescriptor) descriptor).getKind(); return kind == INTERFACE || kind == ANNOTATION_CLASS; diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt index 455a4c98a18..cf13aacd87a 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethod.kt @@ -91,6 +91,24 @@ internal class KtUltraLightMethodForSourceDeclaration( override fun computeDescriptor() = kotlinOrigin?.resolve() as? FunctionDescriptor } + +internal class KtUltraLightMethodForDescriptor( + private val descriptor: FunctionDescriptor, + delegate: LightMethodBuilder, + closestDeclarationForOrigin: KtDeclaration?, + support: UltraLightSupport, + containingClass: KtUltraLightClass +) : KtUltraLightMethod( + delegate, + closestDeclarationForOrigin, + support, + containingClass +) { + override fun buildTypeParameterList() = buildTypeParameterList(descriptor, this, support) + override fun computeDescriptor() = descriptor + + override val kotlinTypeForNullabilityAnnotation: KotlinType? + get() = descriptor.returnType } internal abstract class KtUltraLightParameter( @@ -212,3 +230,20 @@ internal class KtUltraLightReceiverParameter( computeContainingDescriptor()?.extensionReceiverParameter?.type } } + +internal class KtUltraLightParameterForDescriptor( + private val descriptor: ParameterDescriptor, + kotlinOrigin: KtDeclaration?, + support: UltraLightSupport, + method: KtLightMethod +) : KtUltraLightParameter( + if (descriptor.name.isSpecial) "\$self" else descriptor.name.identifier, + kotlinOrigin, support, method +) { + override val kotlinType: KotlinType? + get() = descriptor.type + + override fun computeContainingDescriptor() = descriptor.containingDeclaration as? CallableMemberDescriptor + + override fun isVarArgs() = (descriptor as? ValueParameterDescriptor)?.varargElementType != null +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt index bff02de024f..ddbd637d324 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightPsi.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.asJava.builder.LightClassData import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper @@ -32,10 +33,12 @@ import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClass import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.constants.EnumValue +import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi import org.jetbrains.kotlin.resolve.inline.isInlineOnly import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME @@ -288,9 +291,47 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor } } } + + addMethodsFromDataClass(result) + result } + private fun addMethodsFromDataClass(result: MutableList) { + if (!classOrObject.hasModifier(DATA_KEYWORD)) return + val descriptor = classOrObject.resolve() as? ClassDescriptor ?: return + val bindingContext = classOrObject.analyze() + + // Force resolving data class members set + descriptor.unsubstitutedMemberScope.getContributedDescriptors() + + object : DataClassMethodGenerator(classOrObject, bindingContext) { + private fun addFunction(descriptor: FunctionDescriptor, declarationForOrigin: KtDeclaration? = null) { + result.add(createGeneratedMethodFromDescriptor(descriptor, declarationForOrigin)) + } + + override fun generateComponentFunction(function: FunctionDescriptor, parameter: ValueParameterDescriptor) { + addFunction(function, DescriptorToSourceUtils.descriptorToDeclaration(parameter) as? KtDeclaration) + } + + override fun generateCopyFunction(function: FunctionDescriptor, constructorParameters: List) { + addFunction(function) + } + + override fun generateToStringMethod(function: FunctionDescriptor, properties: List) { + addFunction(function) + } + + override fun generateHashCodeMethod(function: FunctionDescriptor, properties: List) { + addFunction(function) + } + + override fun generateEqualsMethod(function: FunctionDescriptor, properties: List) { + addFunction(function) + } + }.generate() + } + private fun createConstructors(): List { val result = arrayListOf() val constructors = classOrObject.allConstructors @@ -601,6 +642,7 @@ interface UltraLightSupport { val moduleName: String fun findAnnotation(owner: KtAnnotated, fqName: FqName): Pair? fun isTooComplexForUltraLightGeneration(element: KtClassOrObject): Boolean + val deprecationResolver: DeprecationResolver } interface KtUltraLightElementWithNullabilityAnnotation : KtLightDeclaration, diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index 787be706572..f390b23d1a1 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -6,48 +6,102 @@ package org.jetbrains.kotlin.asJava.classes import com.intellij.psi.* +import com.intellij.psi.impl.cache.ModifierFlags import com.intellij.psi.impl.cache.TypeInfo import com.intellij.psi.impl.compiled.ClsTypeElementImpl import com.intellij.psi.impl.compiled.SignatureParsing import com.intellij.psi.impl.compiled.StubBuildingVisitor -import com.intellij.psi.impl.light.LightReferenceListBuilder -import com.intellij.psi.impl.light.LightTypeParameterBuilder +import com.intellij.psi.impl.light.* +import com.intellij.util.BitUtil.isSet import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.elements.KotlinLightTypeParameterListBuilder +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.ClassBuilderMode +import org.jetbrains.kotlin.codegen.JvmCodegenUtil +import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.config.JvmTarget -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.ValueDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterListOwner import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeProjectionImpl import org.jetbrains.kotlin.types.replace import org.jetbrains.kotlin.types.typeUtil.supertypes +import org.jetbrains.org.objectweb.asm.Opcodes import java.text.StringCharacterIterator +internal fun buildTypeParameterList( + declaration: CallableMemberDescriptor, + owner: PsiTypeParameterListOwner, + support: UltraLightSupport +): PsiTypeParameterList = buildTypeParameterList( + declaration, owner, support, + object : TypeParametersSupport { + override fun parameters(declaration: CallableMemberDescriptor) = declaration.typeParameters + + override fun name(typeParameter: TypeParameterDescriptor) = typeParameter.name.asString() + + override fun hasNonTrivialBounds( + declaration: CallableMemberDescriptor, + typeParameter: TypeParameterDescriptor + ) = typeParameter.upperBounds.any { !KotlinBuiltIns.isDefaultBound(it) } + + override fun asDescriptor(typeParameter: TypeParameterDescriptor) = typeParameter + } +) + internal fun buildTypeParameterList( declaration: KtTypeParameterListOwner, owner: PsiTypeParameterListOwner, support: UltraLightSupport +): PsiTypeParameterList = buildTypeParameterList( + declaration, owner, support, + object : TypeParametersSupport { + override fun parameters(declaration: KtTypeParameterListOwner) = declaration.typeParameters + override fun name(typeParameter: KtTypeParameter) = typeParameter.name + + override fun hasNonTrivialBounds( + declaration: KtTypeParameterListOwner, + typeParameter: KtTypeParameter + ) = typeParameter.extendsBound != null || declaration.typeConstraints.isNotEmpty() + + override fun asDescriptor(typeParameter: KtTypeParameter) = typeParameter.resolve() as? TypeParameterDescriptor + } +) + +interface TypeParametersSupport { + fun parameters(declaration: D): List + fun name(typeParameter: T): String? + fun hasNonTrivialBounds(declaration: D, typeParameter: T): Boolean + fun asDescriptor(typeParameter: T): TypeParameterDescriptor? +} + +internal fun buildTypeParameterList( + declaration: D, + owner: PsiTypeParameterListOwner, + support: UltraLightSupport, + typeParametersSupport: TypeParametersSupport ): PsiTypeParameterList { val tpList = KotlinLightTypeParameterListBuilder(owner) - for ((i, ktParam) in declaration.typeParameters.withIndex()) { - tpList.addParameter(object : LightTypeParameterBuilder(ktParam.name.orEmpty(), owner, i) { + for ((i, param) in typeParametersSupport.parameters(declaration).withIndex()) { + tpList.addParameter(object : LightTypeParameterBuilder(typeParametersSupport.name(param).orEmpty(), owner, i) { private val superList: LightReferenceListBuilder by lazyPub { val boundList = KotlinLightReferenceListBuilder(manager, PsiReferenceList.Role.EXTENDS_BOUNDS_LIST) - if (ktParam.extendsBound != null || declaration.typeConstraints.isNotEmpty()) { - val boundTypes = (ktParam.resolve() as? TypeParameterDescriptor)?.upperBounds.orEmpty() + + if (typeParametersSupport.hasNonTrivialBounds(declaration, param)) { + val boundTypes = typeParametersSupport.asDescriptor(param)?.upperBounds.orEmpty() .mapNotNull { it.asPsiType(support, TypeMappingMode.DEFAULT, this) as? PsiClassType } val hasDefaultBound = boundTypes.size == 1 && boundTypes[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT) if (!hasDefaultBound) { @@ -76,6 +130,7 @@ internal fun KtDeclaration.getKotlinType(): KotlinType? { } internal fun KtDeclaration.resolve() = LightClassGenerationSupport.getInstance(project).resolveToDescriptor(this) +internal fun KtDeclaration.analyze() = LightClassGenerationSupport.getInstance(project).analyze(this) // copy-pasted from kotlinInternalUastUtils.kt and post-processed internal fun KotlinType.asPsiType( @@ -145,3 +200,94 @@ fun KotlinType.cleanFromAnonymousTypes(): KotlinType? { return replace(newArguments = newArguments) } + +fun KtUltraLightClass.createGeneratedMethodFromDescriptor( + descriptor: FunctionDescriptor, + declarationForOrigin: KtDeclaration? = null +): KtLightMethod { + val lightMethod = lightMethod(descriptor) + val kotlinOrigin = + declarationForOrigin + ?: DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? KtDeclaration + ?: kotlinOrigin + + val wrapper = KtUltraLightMethodForDescriptor(descriptor, lightMethod, kotlinOrigin, support, this) + + descriptor.extensionReceiverParameter?.let { receiver -> + lightMethod.addParameter(KtUltraLightParameterForDescriptor(receiver, kotlinOrigin, support, wrapper)) + } + + for (valueParameter in descriptor.valueParameters) { + lightMethod.addParameter(KtUltraLightParameterForDescriptor(valueParameter, kotlinOrigin, support, wrapper)) + } + + lightMethod.setMethodReturnType { + support.mapType(wrapper) { typeMapper, signatureWriter -> + typeMapper.mapReturnType(descriptor, signatureWriter) + } + } + + return wrapper +} + +private fun KtUltraLightClass.lightMethod( + descriptor: FunctionDescriptor +): LightMethodBuilder { + val name = descriptor.name.asString() + + val accessFlags: Int by lazyPub { + val asmFlags = AsmUtil.getMethodAsmFlags(descriptor, OwnerKind.IMPLEMENTATION, support.deprecationResolver) + packMethodFlags(asmFlags, JvmCodegenUtil.isJvmInterface(kotlinOrigin.resolve() as? ClassDescriptor)) + } + + return LightMethodBuilder( + manager, language, name, + LightParameterListBuilder(manager, language), + object : LightModifierList(manager, language) { + override fun hasModifierProperty(name: String) = ModifierFlags.hasModifierProperty(name, accessFlags) + } + ) +} + +private fun packCommonFlags(access: Int): Int { + var flags = when { + isSet(access, Opcodes.ACC_PRIVATE) -> ModifierFlags.PRIVATE_MASK + isSet(access, Opcodes.ACC_PROTECTED) -> ModifierFlags.PROTECTED_MASK + isSet(access, Opcodes.ACC_PUBLIC) -> ModifierFlags.PUBLIC_MASK + else -> ModifierFlags.PACKAGE_LOCAL_MASK + } + + if (isSet(access, Opcodes.ACC_STATIC)) { + flags = flags or ModifierFlags.STATIC_MASK + } + + if (isSet(access, Opcodes.ACC_FINAL)) { + flags = flags or ModifierFlags.FINAL_MASK + } + + return flags +} + +private fun packMethodFlags(access: Int, isInterface: Boolean): Int { + var flags = packCommonFlags(access) + + if (isSet(access, Opcodes.ACC_SYNCHRONIZED)) { + flags = flags or ModifierFlags.SYNCHRONIZED_MASK + } + + if (isSet(access, Opcodes.ACC_NATIVE)) { + flags = flags or ModifierFlags.NATIVE_MASK + } + + if (isSet(access, Opcodes.ACC_STRICT)) { + flags = flags or ModifierFlags.STRICTFP_MASK + } + + if (isSet(access, Opcodes.ACC_ABSTRACT)) { + flags = flags or ModifierFlags.ABSTRACT_MASK + } else if (isInterface && !isSet(access, Opcodes.ACC_STATIC)) { + flags = flags or ModifierFlags.DEFAULT_MASK + } + + return flags +} diff --git a/compiler/testData/asJava/ultraLightClasses/dataClasses.kt b/compiler/testData/asJava/ultraLightClasses/dataClasses.kt index 365f25885c5..ea9648d622a 100644 --- a/compiler/testData/asJava/ultraLightClasses/dataClasses.kt +++ b/compiler/testData/asJava/ultraLightClasses/dataClasses.kt @@ -1,7 +1,5 @@ -/** should load cls */ data class User(val name: String = "", val age: Int = 0) -/** should load cls */ data class Person(val name: String) { var age: Int = 0 } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 5f49cb6d55b..4b75ef963cc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -103,6 +104,10 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG } return null } + + override val deprecationResolver: DeprecationResolver by lazyPub { + element.getResolutionFacade().getFrontendService(DeprecationResolver::class.java) + } }) } @@ -110,7 +115,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG if (declaration.hasExpectModifier() || declaration.hasModifier(KtTokens.ANNOTATION_KEYWORD) || declaration.hasModifier(KtTokens.INLINE_KEYWORD) && declaration is KtClassOrObject || - declaration.hasModifier(KtTokens.DATA_KEYWORD) || declaration.hasModifier(KtTokens.SUSPEND_KEYWORD) ) { return declaration