diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/stubs/elements/JetClassElementType.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/stubs/elements/JetClassElementType.java index d5da66b856b..b8470c7f1d0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/stubs/elements/JetClassElementType.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/stubs/elements/JetClassElementType.java @@ -106,7 +106,7 @@ public class JetClassElementType extends JetStubElementType()) - override fun buildFileStub(content: FileContent): PsiFileStub<*>? { - val file = content.getFile() - - if (isKotlinInternalCompiledFile(file)) { - return null - } - - return ClsFileImpl.buildFileStub(file, content.getContent()) - } -} diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/CallableClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/CallableClsStubBuilder.kt new file mode 100644 index 00000000000..0a03a800fdd --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/CallableClsStubBuilder.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.decompiler.stubBuilder + +import com.intellij.psi.stubs.StubElement +import com.intellij.psi.PsiElement +import org.jetbrains.jet.descriptors.serialization.ProtoBuf +import org.jetbrains.jet.descriptors.serialization.Flags +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Modality +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinFunctionStubImpl +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinPropertyStubImpl +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable.CallableKind +import org.jetbrains.jet.descriptors.serialization.descriptors.ProtoContainer +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable.MemberKind +import org.jetbrains.jet.lang.resolve.dataClassUtils.isComponentLike +import org.jetbrains.jet.descriptors.serialization.NameResolver +import org.jetbrains.jet.plugin.decompiler.stubBuilder.FlagsToModifiers.* + +fun createCallableStub( + parentStub: StubElement, + callableProto: ProtoBuf.Callable, + outerContext: ClsStubBuilderContext, + protoContainer: ProtoContainer +) { + if (!shouldSkip(callableProto, outerContext.nameResolver)) { + CallableClsStubBuilder(parentStub, callableProto, outerContext, protoContainer).build() + } +} + +private fun shouldSkip(callableProto: ProtoBuf.Callable, nameResolver: NameResolver): Boolean { + val memberKind = Flags.MEMBER_KIND[callableProto.getFlags()] + return when (memberKind) { + MemberKind.FAKE_OVERRIDE, MemberKind.DELEGATION -> true + //TODO: fix decompiler to use sane criteria + MemberKind.SYNTHESIZED -> !isComponentLike(nameResolver.getName(callableProto.getName())) + else -> false + } +} + +private class CallableClsStubBuilder( + private val parent: StubElement, + private val callableProto: ProtoBuf.Callable, + outerContext: ClsStubBuilderContext, + private val protoContainer: ProtoContainer +) { + private val c = outerContext.child(callableProto.getTypeParameterList()) + private val typeStubBuilder = TypeClsStubBuilder(c) + private val isTopLevel: Boolean get() = protoContainer.packageFqName != null + private val callableStub = doCreateCallableStub() + + fun build() { + createModifierListStub() + val typeConstraintListData = typeStubBuilder.createTypeParameterListStub(callableStub, callableProto.getTypeParameterList()) + createReceiverTypeReferenceStub() + createValueParameterList() + createReturnTypeStub() + typeStubBuilder.createTypeConstraintListStub(callableStub, typeConstraintListData) + } + + private fun createValueParameterList() { + typeStubBuilder.createValueParameterListStub(callableStub, callableProto, protoContainer) + } + + private fun createReceiverTypeReferenceStub() { + if (callableProto.hasReceiverType()) { + typeStubBuilder.createTypeReferenceStub(callableStub, callableProto.getReceiverType()) + } + } + + private fun createReturnTypeStub() { + typeStubBuilder.createTypeReferenceStub(callableStub, callableProto.getReturnType()) + } + + private fun createModifierListStub() { + val relevantModifiers = if (isTopLevel) listOf(VISIBILITY) else listOf(VISIBILITY, MODALITY) + + val modifierListStubImpl = createModifierListStubForDeclaration(callableStub, callableProto.getFlags(), relevantModifiers) + val annotationIds = c.components.annotationLoader.loadCallableAnnotations( + protoContainer, callableProto, c.nameResolver, callableProto.annotatedCallableKind + ) + createAnnotationStubs(annotationIds, modifierListStubImpl) + } + + private fun doCreateCallableStub(): StubElement { + val callableKind = Flags.CALLABLE_KIND[callableProto.getFlags()] + val callableName = c.nameResolver.getName(callableProto.getName()) + val callableFqName = c.memberFqNameProvider.getMemberFqName(callableName) + + return when (callableKind) { + ProtoBuf.Callable.CallableKind.FUN -> { + KotlinFunctionStubImpl( + parent, + callableName.ref(), + isTopLevel, + callableFqName, + isExtension = callableProto.hasReceiverType(), + hasBlockBody = true, + hasBody = Flags.MODALITY[callableProto.getFlags()] != Modality.ABSTRACT, + hasTypeParameterListBeforeFunctionName = callableProto.getTypeParameterList().isNotEmpty(), + isProbablyNothingType = isProbablyNothing(callableProto) + ) + } + ProtoBuf.Callable.CallableKind.VAL, ProtoBuf.Callable.CallableKind.VAR -> { + KotlinPropertyStubImpl( + parent, + callableName.ref(), + isVar = callableKind == CallableKind.VAR, + isTopLevel = isTopLevel, + hasDelegate = false, + hasDelegateExpression = false, + hasInitializer = false, + hasReceiverTypeRef = callableProto.hasReceiverType(), + hasReturnTypeRef = true, + fqName = callableFqName, + isProbablyNothingType = isProbablyNothing(callableProto) + ) + } + ProtoBuf.Callable.CallableKind.CONSTRUCTOR -> throw IllegalStateException("Should not be called for constructor!") + else -> throw IllegalStateException("Unknown callable kind $callableKind") + } + } + + //TODO: remove isProbablyNothing from stubs + private fun isProbablyNothing(callableProto: ProtoBuf.Callable): Boolean { + val constructor = callableProto.getReturnType().getConstructor() + return constructor.getKind() == ProtoBuf.Type.Constructor.Kind.CLASS && + c.nameResolver.getClassId(constructor.getId()).getRelativeClassName().shortName().asString() == "Nothing" + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/ClassClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/ClassClsStubBuilder.kt new file mode 100644 index 00000000000..7d0471966d0 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/ClassClsStubBuilder.kt @@ -0,0 +1,233 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.decompiler.stubBuilder + +import com.intellij.psi.stubs.StubElement +import org.jetbrains.jet.descriptors.serialization.Flags +import org.jetbrains.jet.descriptors.serialization.ProtoBuf +import org.jetbrains.jet.lang.psi.stubs.elements.JetClassElementType +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinClassStubImpl +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinObjectStubImpl +import com.intellij.psi.PsiElement +import org.jetbrains.jet.lang.psi.JetClassBody +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinPlaceHolderStubImpl +import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes +import org.jetbrains.jet.lang.psi.JetParameterList +import org.jetbrains.jet.lang.resolve.name.ClassId +import org.jetbrains.jet.lang.psi.JetDelegationSpecifierList +import org.jetbrains.jet.lang.psi.JetDelegatorToSuperClass +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.resolve.name.SpecialNames.getClassObjectName +import org.jetbrains.jet.lang.psi.JetClassObject +import org.jetbrains.jet.descriptors.serialization.descriptors.ProtoContainer +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinModifierListStubImpl +import org.jetbrains.jet.lexer.JetModifierKeywordToken +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Type +import org.jetbrains.jet.plugin.decompiler.stubBuilder.FlagsToModifiers.* +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns + + +fun createClassStub(parent: StubElement, classProto: ProtoBuf.Class, classId: ClassId, context: ClsStubBuilderContext) { + ClassClsStubBuilder(parent, classProto, classId, context).build() +} + +private class ClassClsStubBuilder( + private val parentStub: StubElement, + private val classProto: ProtoBuf.Class, + private val classId: ClassId, + private val outerContext: ClsStubBuilderContext +) { + private val c = outerContext.child(classProto.getTypeParameterList(), classId.getRelativeClassName().shortName()) + private val typeStubBuilder = TypeClsStubBuilder(c) + private val classKind = Flags.CLASS_KIND[classProto.getFlags()] + private val supertypeIds = classProto.getSupertypeList().map { + type -> + assert(type.getConstructor().getKind() == Type.Constructor.Kind.CLASS) + c.nameResolver.getClassId(type.getConstructor().getId()) + }.let { + supertypeIds -> + //empty supertype list if single supertype is Any + if (supertypeIds.singleOrNull()?.let { KotlinBuiltIns.isAny(it.asSingleFqName()) } ?: false) { + listOf() + } + else { + supertypeIds + } + } + + private val classOrObjectStub = createClassOrObjectStubAndModifierListStub() + + fun build() { + val typeConstraintListData = typeStubBuilder.createTypeParameterListStub(classOrObjectStub, classProto.getTypeParameterList()) + createConstructorStub() + createDelegationSpecifierList() + typeStubBuilder.createTypeConstraintListStub(classOrObjectStub, typeConstraintListData) + createClassBodyAndMemberStubs() + } + + private fun createClassOrObjectStubAndModifierListStub(): StubElement { + val isClassObject = classKind == ProtoBuf.Class.Kind.CLASS_OBJECT + if (isClassObject) { + val classObjectStub = KotlinPlaceHolderStubImpl(parentStub, JetStubElementTypes.CLASS_OBJECT) + val modifierList = createModifierListForClass(classObjectStub) + val objectDeclarationStub = doCreateClassOrObjectStub(classObjectStub) + createAnnotationStubs(c.components.annotationLoader.loadClassAnnotations(classProto, c.nameResolver), modifierList) + return objectDeclarationStub + } + else { + val classOrObjectStub = doCreateClassOrObjectStub(parentStub) + val modifierList = createModifierListForClass(classOrObjectStub) + createAnnotationStubs(c.components.annotationLoader.loadClassAnnotations(classProto, c.nameResolver), modifierList) + return classOrObjectStub + } + } + + private fun createModifierListForClass(parent: StubElement): KotlinModifierListStubImpl { + val relevantFlags = arrayListOf(VISIBILITY) + if (isClass()) { + relevantFlags.add(INNER) + relevantFlags.add(MODALITY) + } + val additionalModifiers = when (classKind) { + ProtoBuf.Class.Kind.ENUM_CLASS -> listOf(JetTokens.ENUM_KEYWORD) + ProtoBuf.Class.Kind.ANNOTATION_CLASS -> listOf(JetTokens.ANNOTATION_KEYWORD) + else -> listOf() + } + return createModifierListStubForDeclaration(parent, classProto.getFlags(), relevantFlags, additionalModifiers) + } + + private fun doCreateClassOrObjectStub(parent: StubElement): StubElement { + val isClassObject = classKind == ProtoBuf.Class.Kind.CLASS_OBJECT + val fqName = if (!isClassObject) outerContext.memberFqNameProvider.getMemberFqName(classId.getRelativeClassName().shortName()) else null + val shortName = fqName?.shortName()?.ref() + val superTypeRefs = supertypeIds.filter { + //TODO: filtering function types should go away + !KotlinBuiltIns.isExactFunctionType(it.asSingleFqName()) && !KotlinBuiltIns.isExactExtensionFunctionType(it.asSingleFqName()) + }.map { it.getRelativeClassName().shortName().ref() }.copyToArray() + return when (classKind) { + ProtoBuf.Class.Kind.OBJECT, ProtoBuf.Class.Kind.CLASS_OBJECT -> { + KotlinObjectStubImpl( + parent, shortName, fqName, superTypeRefs, + isTopLevel = classId.isTopLevelClass(), + isClassObject = isClassObject, + isLocal = false, + isObjectLiteral = false + ) + } + else -> { + KotlinClassStubImpl( + JetClassElementType.getStubType(classKind == ProtoBuf.Class.Kind.ENUM_ENTRY), + parent, + fqName?.ref(), + shortName, + superTypeRefs, + isTrait = classKind == ProtoBuf.Class.Kind.TRAIT, + isEnumEntry = classKind == ProtoBuf.Class.Kind.ENUM_ENTRY, + isLocal = false, + isTopLevel = classId.isTopLevelClass() + ) + } + } + } + + private fun createConstructorStub() { + if (!isClass()) return + + val primaryConstructorProto = classProto.getPrimaryConstructor() + if (primaryConstructorProto.hasData()) { + typeStubBuilder.createValueParameterListStub(classOrObjectStub, primaryConstructorProto.getData(), ProtoContainer(classProto, null)) + } + else { + //default empty constructor + KotlinPlaceHolderStubImpl(classOrObjectStub, JetStubElementTypes.VALUE_PARAMETER_LIST) + } + } + + private fun createDelegationSpecifierList() { + // if single supertype is any then no delegation specifier list is needed + if (supertypeIds.isEmpty()) return + + val delegationSpecifierListStub = + KotlinPlaceHolderStubImpl(classOrObjectStub, JetStubElementTypes.DELEGATION_SPECIFIER_LIST) + + classProto.getSupertypeList().forEach { type -> + val superClassStub = KotlinPlaceHolderStubImpl( + delegationSpecifierListStub, JetStubElementTypes.DELEGATOR_SUPER_CLASS + ) + typeStubBuilder.createTypeReferenceStub(superClassStub, type) + } + } + + private fun createClassBodyAndMemberStubs() { + val classBody = KotlinPlaceHolderStubImpl(classOrObjectStub, JetStubElementTypes.CLASS_BODY) + createClassObjectStub(classBody) + createEnumEntryStubs(classBody) + createCallableMemberStubs(classBody) + createInnerAndNestedClasses(classBody) + } + + private fun createClassObjectStub(classBody: KotlinPlaceHolderStubImpl) { + if (!classProto.hasClassObject() || classKind == ProtoBuf.Class.Kind.OBJECT) { + return + } + + val classObjectId = classId.createNestedClassId(getClassObjectName(classId.getRelativeClassName().shortName())) + createNestedClassStub(classBody, classObjectId) + } + + private fun createEnumEntryStubs(classBody: KotlinPlaceHolderStubImpl) { + classProto.getEnumEntryList().forEach { id -> + val name = c.nameResolver.getName(id) + KotlinClassStubImpl( + JetStubElementTypes.ENUM_ENTRY, + classBody, + qualifiedName = c.memberFqNameProvider.getMemberFqName(name).ref(), + name = name.ref(), + superNames = array(), + isTrait = false, + isEnumEntry = true, + isLocal = false, + isTopLevel = false + ) + } + } + + private fun createCallableMemberStubs(classBody: KotlinPlaceHolderStubImpl) { + val container = ProtoContainer(classProto, null) + for (callableProto in classProto.getMemberList()) { + createCallableStub(classBody, callableProto, c, container) + } + } + + private fun isClass(): Boolean { + return classKind == ProtoBuf.Class.Kind.CLASS || + classKind == ProtoBuf.Class.Kind.ENUM_CLASS || + classKind == ProtoBuf.Class.Kind.ANNOTATION_CLASS + } + + private fun createInnerAndNestedClasses(classBody: KotlinPlaceHolderStubImpl) { + classProto.getNestedClassNameList().forEach { id -> + val nestedClassId = classId.createNestedClassId(c.nameResolver.getName(id)) + createNestedClassStub(classBody, nestedClassId) + } + } + + private fun createNestedClassStub(classBody: StubElement, nestedClassId: ClassId) { + val classData = c.components.classDataFinder.findClassData(nestedClassId)!! + createClassStub(classBody, classData.getClassProto(), nestedClassId, c.child(classData.getNameResolver())) + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/ClsStubBuilderContext.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/ClsStubBuilderContext.kt new file mode 100644 index 00000000000..1989d6234aa --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/ClsStubBuilderContext.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.decompiler.stubBuilder + +import org.jetbrains.jet.descriptors.serialization.NameResolver +import org.jetbrains.jet.lang.resolve.name.Name +import org.jetbrains.jet.lang.resolve.name.FqName +import org.jetbrains.jet.descriptors.serialization.ClassDataFinder +import org.jetbrains.jet.descriptors.serialization.ProtoBuf +import org.jetbrains.jet.lang.resolve.kotlin.KotlinClassFinder +import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter +import org.jetbrains.jet.lang.resolve.kotlin.AbstractBinaryClassAnnotationAndConstantLoader +import org.jetbrains.jet.lang.resolve.name.ClassId +import org.jetbrains.jet.storage.LockBasedStorageManager +import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass +import org.jetbrains.jet.lang.resolve.name.SpecialNames + + +class ClsStubBuilderComponents( + val classDataFinder: ClassDataFinder, + val annotationLoader: AnnotationLoaderForStubBuilder +) { + fun createContext( + nameResolver: NameResolver, + packageFqName: FqName + ): ClsStubBuilderContext { + return ClsStubBuilderContext(this, nameResolver, MemberFqNameProvider(packageFqName), EmptyTypeParameters) + } +} + +class MemberFqNameProvider(val fqName: FqName) { + fun getMemberFqName(name: Name): FqName = fqName.child(name) + + fun child(name: Name?): MemberFqNameProvider = + if (name == null || SpecialNames.isClassObjectName(name)) this else MemberFqNameProvider(fqName.child(name)) +} + +trait TypeParameters { + fun get(id: Int): Name + + fun child(nameResolver: NameResolver, innerTypeParameters: List) + = TypeParametersImpl(nameResolver, innerTypeParameters, parent = this) +} + +object EmptyTypeParameters : TypeParameters { + override fun get(id: Int): Name = throw IllegalStateException("Unknown type parameter with id = $id") +} + +class TypeParametersImpl( + nameResolver: NameResolver, + typeParameterProtos: Collection, + private val parent: TypeParameters +) : TypeParameters { + private val typeParametersById = typeParameterProtos.map { Pair(it.getId(), nameResolver.getName(it.getName())) }.toMap() + + override fun get(id: Int): Name = typeParametersById[id] ?: parent[id] +} + +class ClsStubBuilderContext( + val components: ClsStubBuilderComponents, + val nameResolver: NameResolver, + val memberFqNameProvider: MemberFqNameProvider, + val typeParameters: TypeParameters +) + +private fun ClsStubBuilderContext.child(typeParameterList: List, name: Name? = null): ClsStubBuilderContext { + return ClsStubBuilderContext( + this.components, + this.nameResolver, + this.memberFqNameProvider.child(name), + this.typeParameters.child(nameResolver, typeParameterList) + ) +} + +private fun ClsStubBuilderContext.child(nameResolver: NameResolver): ClsStubBuilderContext { + return ClsStubBuilderContext( + this.components, + nameResolver, + this.memberFqNameProvider, + this.typeParameters + ) +} + +class AnnotationLoaderForStubBuilder( + kotlinClassFinder: KotlinClassFinder, + errorReporter: ErrorReporter +) : AbstractBinaryClassAnnotationAndConstantLoader( + LockBasedStorageManager.NO_LOCKS, kotlinClassFinder, errorReporter) { + override fun loadConstant(desc: String, initializer: Any) = null + + override fun loadAnnotation(annotationClassId: ClassId, result: MutableList): KotlinJvmBinaryClass.AnnotationArgumentVisitor? { + result.add(annotationClassId) + return null + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/KotlinClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/KotlinClsStubBuilder.kt new file mode 100644 index 00000000000..c6237248897 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/KotlinClsStubBuilder.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.decompiler.stubBuilder + +import com.intellij.psi.compiled.ClsStubBuilder +import com.intellij.util.cls.ClsFormatException +import com.intellij.util.indexing.FileContent +import com.intellij.psi.stubs.PsiFileStub +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.impl.compiled.ClassFileStubBuilder +import org.jetbrains.jet.lang.resolve.kotlin.KotlinBinaryClassCache +import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader +import org.jetbrains.jet.descriptors.serialization.JavaProtoBufUtil +import org.jetbrains.jet.lang.psi.JetFile +import org.jetbrains.jet.plugin.decompiler.textBuilder.LocalClassFinder +import org.jetbrains.jet.plugin.decompiler.textBuilder.LocalClassDataFinder +import com.intellij.openapi.diagnostic.Logger +import org.jetbrains.jet.plugin.decompiler.textBuilder.LoggingErrorReporter +import org.jetbrains.jet.plugin.decompiler.isKotlinInternalCompiledFile +import org.jetbrains.jet.lang.resolve.name.FqName + +public class KotlinClsStubBuilder : ClsStubBuilder() { + override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + 1 + + override fun buildFileStub(content: FileContent): PsiFileStub<*>? { + val file = content.getFile() + + if (isKotlinInternalCompiledFile(file)) { + return null + } + + return doBuildFileStub(file) + } + + throws(javaClass()) + fun doBuildFileStub(file: VirtualFile): PsiFileStub? { + val kotlinBinaryClass = KotlinBinaryClassCache.getKotlinBinaryClass(file) + val header = kotlinBinaryClass.getClassHeader() + val classId = kotlinBinaryClass.getClassId() + val packageFqName = classId.getPackageFqName() + if (!header.isCompatibleAbiVersion) { + return createIncompatibleAbiVersionFileStub(packageFqName) + } + + val components = createStubBuilderComponents(file, packageFqName) + val annotationData = header.annotationData + if (annotationData == null) { + LOG.error("Corrupted kotlin header for file ${file.getName()}") + return null + } + return when (header.kind) { + KotlinClassHeader.Kind.PACKAGE_FACADE -> { + val packageData = JavaProtoBufUtil.readPackageDataFrom(annotationData) + val context = components.createContext(packageData.getNameResolver(), packageFqName) + createPackageFacadeFileStub(packageData.getPackageProto(), packageFqName, context) + } + + KotlinClassHeader.Kind.CLASS -> { + val classData = JavaProtoBufUtil.readClassDataFrom(annotationData) + val context = components.createContext(classData.getNameResolver(), packageFqName) + createTopLevelClassStub(classId, classData.getClassProto(), context) + } + else -> throw IllegalStateException("Should have processed " + file.getPath() + " with ${header.kind}") + } + } + + private fun createStubBuilderComponents(file: VirtualFile, packageFqName: FqName): ClsStubBuilderComponents { + val localClassFinder = LocalClassFinder(file.getParent()!!, packageFqName) + val localClassDataFinder = LocalClassDataFinder(localClassFinder, LOG) + val annotationLoader = AnnotationLoaderForStubBuilder(localClassFinder, LoggingErrorReporter(LOG)) + return ClsStubBuilderComponents(localClassDataFinder, annotationLoader) + } + + class object { + val LOG = Logger.getInstance(javaClass()) + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/TypeClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/TypeClsStubBuilder.kt new file mode 100644 index 00000000000..ee23706ccd7 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/TypeClsStubBuilder.kt @@ -0,0 +1,239 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.decompiler.stubBuilder + +import com.intellij.psi.stubs.StubElement +import org.jetbrains.jet.descriptors.serialization.ProtoBuf +import org.jetbrains.jet.lang.resolve.name.FqName +import com.intellij.psi.PsiElement +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinPlaceHolderStubImpl +import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinNameReferenceExpressionStubImpl +import org.jetbrains.jet.lang.psi.JetTypeReference +import org.jetbrains.jet.lexer.JetModifierKeywordToken +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.psi.JetParameterList +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinParameterStubImpl +import org.jetbrains.jet.lang.resolve.name.Name +import org.jetbrains.jet.lang.psi.JetNullableType +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.lang.psi.JetTypeArgumentList +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinTypeProjectionStubImpl +import org.jetbrains.jet.lang.psi.JetFunctionType +import org.jetbrains.jet.lang.psi.JetFunctionTypeReceiver +import org.jetbrains.jet.utils.addToStdlib.singletonOrEmptyList +import org.jetbrains.jet.lang.psi.JetTypeParameterList +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Type +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinTypeParameterStubImpl +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.TypeParameter.Variance +import org.jetbrains.jet.lang.psi.JetTypeConstraintList +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinTypeConstraintStubImpl +import org.jetbrains.jet.lang.resolve.name.ClassId +import java.util.ArrayList +import org.jetbrains.jet.descriptors.serialization.Flags +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable.CallableKind +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinModifierListStubImpl +import org.jetbrains.jet.lang.psi.stubs.impl.ModifierMaskUtils +import org.jetbrains.jet.descriptors.serialization.descriptors.ProtoContainer +import org.jetbrains.jet.lang.psi.stubs.KotlinUserTypeStub +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Type.Argument.Projection +import org.jetbrains.jet.lang.psi.JetProjectionKind + +class TypeClsStubBuilder(private val c: ClsStubBuilderContext) { + + fun createTypeReferenceStub(parent: StubElement, typeProto: Type) { + val typeReference = KotlinPlaceHolderStubImpl(parent, JetStubElementTypes.TYPE_REFERENCE) + createTypeStub(typeReference, typeProto) + } + + private fun createTypeStub(parent: StubElement, type: Type) { + val isNullable = type.getNullable() + val effectiveParent = if (isNullable) KotlinPlaceHolderStubImpl(parent, JetStubElementTypes.NULLABLE_TYPE) else parent + when (type.getConstructor().getKind()) { + Type.Constructor.Kind.CLASS -> { + createClassReferenceTypeStub(effectiveParent, type) + } + Type.Constructor.Kind.TYPE_PARAMETER -> { + val typeParameterName = c.typeParameters[type.getConstructor().getId()] + createStubForTypeName(ClassId.topLevel(FqName.topLevel(typeParameterName)), effectiveParent) + } + } + } + + private fun createClassReferenceTypeStub(parent: StubElement, type: Type) { + val classId = c.nameResolver.getClassId(type.getConstructor().getId()) + val fqName = classId.asSingleFqName() + val isFunctionType = KotlinBuiltIns.isExactFunctionType(fqName) + val isExtensionFunctionType = KotlinBuiltIns.isExactExtensionFunctionType(fqName) + if (isFunctionType || isExtensionFunctionType) { + createFunctionTypeStub(parent, type, isExtensionFunctionType) + return + } + val typeStub = createStubForTypeName(classId, parent) + val typeArgumentProtoList = type.getArgumentList() + createTypeArgumentListStub(typeStub, typeArgumentProtoList) + return + } + + private fun createTypeArgumentListStub(typeStub: KotlinUserTypeStub, typeArgumentProtoList: List) { + if (typeArgumentProtoList.isEmpty()) { + return + } + val typeArgumentsListStub = KotlinPlaceHolderStubImpl(typeStub, JetStubElementTypes.TYPE_ARGUMENT_LIST) + typeArgumentProtoList.forEach { typeArgumentProto -> + val projectionKind = typeArgumentProto.getProjection().toProjectionKind() + val typeProjection = KotlinTypeProjectionStubImpl(typeArgumentsListStub, projectionKind.ordinal()) + val modifierKeywordToken = projectionKind.getToken() as? JetModifierKeywordToken + createModifierListStub(typeProjection, modifierKeywordToken.singletonOrEmptyList()) + createTypeReferenceStub(typeProjection, typeArgumentProto.getType()) + } + } + + private fun Projection.toProjectionKind() = when (this) { + Projection.IN -> JetProjectionKind.IN + Projection.OUT -> JetProjectionKind.OUT + Projection.INV -> JetProjectionKind.NONE + } + + private fun createFunctionTypeStub(parent: StubElement, type: Type, isExtensionFunctionType: Boolean) { + val typeArgumentList = type.getArgumentList() + val functionType = KotlinPlaceHolderStubImpl(parent, JetStubElementTypes.FUNCTION_TYPE) + if (isExtensionFunctionType) { + val functionTypeReceiverStub + = KotlinPlaceHolderStubImpl(functionType, JetStubElementTypes.FUNCTION_TYPE_RECEIVER) + val receiverTypeProto = typeArgumentList.first().getType() + createTypeReferenceStub(functionTypeReceiverStub, receiverTypeProto) + } + + val parameterList = KotlinPlaceHolderStubImpl(functionType, JetStubElementTypes.VALUE_PARAMETER_LIST) + val typeArgumentsWithoutReceiverAndReturnType + = typeArgumentList.subList(if (isExtensionFunctionType) 1 else 0, typeArgumentList.size - 1) + typeArgumentsWithoutReceiverAndReturnType.forEach { argument -> + val parameter = KotlinParameterStubImpl(parameterList, fqName = null, name = null, isMutable = false, hasValOrVarNode = false, hasDefaultValue = false) + createTypeReferenceStub(parameter, argument.getType()) + } + + val returnType = typeArgumentList.last().getType() + createTypeReferenceStub(functionType, returnType) + } + + fun createValueParameterListStub(parent: StubElement, callableProto: ProtoBuf.Callable, container: ProtoContainer) { + val callableKind = Flags.CALLABLE_KIND[callableProto.getFlags()] + if (callableKind == CallableKind.VAL || callableKind == CallableKind.VAR) { + return + } + val parameterListStub = KotlinPlaceHolderStubImpl(parent, JetStubElementTypes.VALUE_PARAMETER_LIST) + for (valueParameterProto in callableProto.getValueParameterList()) { + val name = c.nameResolver.getName(valueParameterProto.getName()) + val parameterStub = KotlinParameterStubImpl( + parameterListStub, + name = name.ref(), + fqName = null, + hasDefaultValue = false, + hasValOrVarNode = false, + isMutable = false + ) + val isVararg = valueParameterProto.hasVarargElementType() + val modifierList = if (isVararg) createModifierListStub(parameterStub, listOf(JetTokens.VARARG_KEYWORD)) else null + val parameterAnnotations = c.components.annotationLoader.loadValueParameterAnnotations( + container, callableProto, c.nameResolver, callableProto.annotatedCallableKind, valueParameterProto + ) + if (parameterAnnotations.isNotEmpty()) { + createAnnotationStubs(parameterAnnotations, modifierList ?: createEmptyModifierList(parameterStub)) + } + + val typeProto = if (isVararg) valueParameterProto.getVarargElementType() else valueParameterProto.getType() + createTypeReferenceStub(parameterStub, typeProto) + } + } + + private fun createEmptyModifierList(parameterStub: KotlinParameterStubImpl): KotlinModifierListStubImpl { + return KotlinModifierListStubImpl( + parameterStub, + ModifierMaskUtils.computeMask { false }, + JetStubElementTypes.MODIFIER_LIST + ) + } + + fun createTypeParameterListStub( + parent: StubElement, + typeParameterProtoList: List + ): List> { + if (typeParameterProtoList.isEmpty()) return listOf() + + val typeParameterListStub = KotlinPlaceHolderStubImpl(parent, JetStubElementTypes.TYPE_PARAMETER_LIST) + val protosForTypeConstraintList = arrayListOf>() + for (proto in typeParameterProtoList) { + val name = c.nameResolver.getName(proto.getName()) + val typeParameterStub = KotlinTypeParameterStubImpl( + typeParameterListStub, + name = name.ref(), + isInVariance = proto.getVariance() == Variance.IN, + isOutVariance = proto.getVariance() == Variance.OUT + ) + createTypeParameterModifierListStub(typeParameterStub, proto) + val upperBoundProtos = proto.getUpperBoundList() + if (upperBoundProtos.isNotEmpty()) { + val upperBound = upperBoundProtos.first() + if (!upperBound.isDefaultUpperBound()) { + createTypeReferenceStub(typeParameterStub, upperBound) + } + protosForTypeConstraintList addAll upperBoundProtos.drop(1).map { Pair(name, it) } + } + } + return protosForTypeConstraintList + } + + fun createTypeConstraintListStub( + parent: StubElement, + protosForTypeConstraintList: List> + ) { + if (protosForTypeConstraintList.isEmpty()) { + return + } + val typeConstraintListStub = KotlinPlaceHolderStubImpl(parent, JetStubElementTypes.TYPE_CONSTRAINT_LIST) + for ((name, type) in protosForTypeConstraintList) { + val typeConstraintStub = KotlinTypeConstraintStubImpl(typeConstraintListStub, isClassObjectConstraint = false) + KotlinNameReferenceExpressionStubImpl(typeConstraintStub, name.ref()) + createTypeReferenceStub(typeConstraintStub, type) + } + } + + private fun createTypeParameterModifierListStub( + typeParameterStub: KotlinTypeParameterStubImpl, + typeParameterProto: ProtoBuf.TypeParameter + ) { + val modifiers = ArrayList() + when (typeParameterProto.getVariance()) { + Variance.IN -> modifiers.add(JetTokens.IN_KEYWORD) + Variance.OUT -> modifiers.add(JetTokens.OUT_KEYWORD) + } + if (typeParameterProto.getReified()) { + modifiers.add(JetTokens.REIFIED_KEYWORD) + } + createModifierListStub(typeParameterStub, modifiers) + } + + private fun Type.isDefaultUpperBound(): Boolean { + val constructor = getConstructor() + if (constructor.getKind() != Type.Constructor.Kind.CLASS) { + return false + } + val classId = c.nameResolver.getClassId(constructor.getId()) + return KotlinBuiltIns.isAny(classId.asSingleFqName()) && this.getNullable() + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/clsStubBuilding.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/clsStubBuilding.kt new file mode 100644 index 00000000000..809de1eefb8 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/decompiler/stubBuilder/clsStubBuilding.kt @@ -0,0 +1,204 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.decompiler.stubBuilder + + +import org.jetbrains.jet.lang.resolve.name.FqName +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinFileStubImpl +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinPlaceHolderStubImpl +import org.jetbrains.jet.lang.psi.JetPackageDirective +import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes +import com.intellij.psi.stubs.StubElement +import com.intellij.psi.PsiElement +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinNameReferenceExpressionStubImpl +import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression +import org.jetbrains.jet.lang.resolve.name.ClassId +import org.jetbrains.jet.lang.psi.stubs.KotlinUserTypeStub +import org.jetbrains.jet.lang.resolve.name.SpecialNames +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinUserTypeStubImpl +import org.jetbrains.jet.lexer.JetModifierKeywordToken +import org.jetbrains.jet.descriptors.serialization.Flags +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinModifierListStubImpl +import org.jetbrains.jet.lang.psi.stubs.impl.ModifierMaskUtils +import org.jetbrains.jet.descriptors.serialization.ProtoBuf +import com.intellij.util.io.StringRef +import org.jetbrains.jet.descriptors.serialization.descriptors.ProtoContainer +import org.jetbrains.jet.lang.psi.stubs.impl.KotlinAnnotationEntryStubImpl +import org.jetbrains.jet.lang.psi.JetConstructorCalleeExpression +import org.jetbrains.jet.lang.psi.JetTypeReference +import org.jetbrains.jet.lang.resolve.name.Name +import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotatedCallableKind +import org.jetbrains.jet.descriptors.serialization.ProtoBuf.Callable.CallableKind + +fun createTopLevelClassStub(classId: ClassId, classProto: ProtoBuf.Class, context: ClsStubBuilderContext): KotlinFileStubImpl { + val fileStub = createFileStub(classId.getPackageFqName()) + createClassStub(fileStub, classProto, classId, context) + return fileStub +} + +fun createPackageFacadeFileStub( + packageProto: ProtoBuf.Package, + packageFqName: FqName, + c: ClsStubBuilderContext +): KotlinFileStubImpl { + val fileStub = createFileStub(packageFqName) + val container = ProtoContainer(null, packageFqName) + for (callableProto in packageProto.getMemberList()) { + createCallableStub(fileStub, callableProto, c, container) + } + return fileStub +} + +fun createIncompatibleAbiVersionFileStub(packageFqName: FqName) = createFileStub(packageFqName) + +fun createFileStub(packageFqName: FqName): KotlinFileStubImpl { + val fileStub = KotlinFileStubImpl(null, packageFqName.asString(), packageFqName.isRoot()) + val packageDirectiveStub = KotlinPlaceHolderStubImpl(fileStub, JetStubElementTypes.PACKAGE_DIRECTIVE) + createStubForPackageName(packageDirectiveStub, packageFqName) + return fileStub +} + +fun createStubForPackageName(packageDirectiveStub: KotlinPlaceHolderStubImpl, packageFqName: FqName) { + val segments = packageFqName.pathSegments().toArrayList() + val iterator = segments.listIterator(segments.size()) + + fun recCreateStubForPackageName(current: StubElement) { + when (iterator.previousIndex()) { + -1 -> return + 0 -> { + KotlinNameReferenceExpressionStubImpl(current, iterator.previous().ref()) + return + } + else -> { + val lastSegment = iterator.previous() + val receiver = KotlinPlaceHolderStubImpl(current, JetStubElementTypes.DOT_QUALIFIED_EXPRESSION) + recCreateStubForPackageName(receiver) + KotlinNameReferenceExpressionStubImpl(receiver, lastSegment.ref()) + } + } + } + + recCreateStubForPackageName(packageDirectiveStub) +} + +fun createStubForTypeName(typeClassId: ClassId, parent: StubElement): KotlinUserTypeStub { + //TODO: should go away with default objects + val segments = typeClassId.asSingleFqName().pathSegments().filter { !SpecialNames.isClassObjectName(it) }.toArrayList() + assert(segments.isNotEmpty()) + val iterator = segments.listIterator(segments.size()) + + fun recCreateStubForType(current: StubElement): KotlinUserTypeStub { + val lastSegment = iterator.previous() + val userTypeStub = KotlinUserTypeStubImpl(current, isAbsoluteInRootPackage = false) + if (iterator.hasPrevious()) { + recCreateStubForType(userTypeStub) + } + KotlinNameReferenceExpressionStubImpl(userTypeStub, lastSegment.ref()) + return userTypeStub + } + + return recCreateStubForType(parent) +} + +enum class FlagsToModifiers { + MODALITY { + override fun getModifiers(flags: Int): JetModifierKeywordToken { + val modality = Flags.MODALITY.get(flags) + return when (modality) { + ProtoBuf.Modality.ABSTRACT -> JetTokens.ABSTRACT_KEYWORD + ProtoBuf.Modality.FINAL -> JetTokens.FINAL_KEYWORD + ProtoBuf.Modality.OPEN -> JetTokens.OPEN_KEYWORD + else -> throw IllegalStateException("Unexpected modality: $modality") + } + } + } + + VISIBILITY { + override fun getModifiers(flags: Int): JetModifierKeywordToken? { + val visibility = Flags.VISIBILITY.get(flags) + return when (visibility) { + ProtoBuf.Visibility.PRIVATE -> JetTokens.PRIVATE_KEYWORD + ProtoBuf.Visibility.INTERNAL -> JetTokens.INTERNAL_KEYWORD + ProtoBuf.Visibility.PROTECTED -> JetTokens.PROTECTED_KEYWORD + ProtoBuf.Visibility.PUBLIC -> JetTokens.PUBLIC_KEYWORD + //TODO: support extra visibility + else -> throw IllegalStateException("Unexpected visibility: $visibility") + } + } + } + + INNER { + override fun getModifiers(flags: Int): JetModifierKeywordToken? { + return if (Flags.INNER.get(flags)) JetTokens.INNER_KEYWORD else null + } + } + + abstract fun getModifiers(flags: Int): JetModifierKeywordToken? +} + +fun createModifierListStubForDeclaration( + parent: StubElement, + flags: Int, + flagsToTranslate: List = listOf(), + additionalModifiers: List = listOf() +): KotlinModifierListStubImpl { + assert(flagsToTranslate.isNotEmpty()) + + val modifiers = flagsToTranslate.map { it.getModifiers(flags) }.filterNotNull() + additionalModifiers + return createModifierListStub(parent, modifiers)!! +} + +fun createModifierListStub( + parent: StubElement, + modifiers: Collection +): KotlinModifierListStubImpl? { + if (modifiers.isEmpty()) { + return null + } + return KotlinModifierListStubImpl( + parent, + ModifierMaskUtils.computeMask { it in modifiers }, + JetStubElementTypes.MODIFIER_LIST + ) +} + +fun createAnnotationStubs(annotationIds: List, modifierList: KotlinModifierListStubImpl) = annotationIds.forEach { + annotationClassId -> + val annotationEntryStubImpl = KotlinAnnotationEntryStubImpl( + modifierList, + shortName = annotationClassId.asSingleFqName().shortName().ref(), + hasValueArguments = false + ) + val constructorCallee = KotlinPlaceHolderStubImpl(annotationEntryStubImpl, JetStubElementTypes.CONSTRUCTOR_CALLEE) + val typeReference = KotlinPlaceHolderStubImpl(constructorCallee, JetStubElementTypes.TYPE_REFERENCE) + createStubForTypeName(annotationClassId, typeReference) +} + +val ProtoBuf.Callable.annotatedCallableKind: AnnotatedCallableKind + get() { + val callableKind = Flags.CALLABLE_KIND[getFlags()] + return when (callableKind) { + CallableKind.VAL, CallableKind.VAR -> AnnotatedCallableKind.PROPERTY + CallableKind.FUN, CallableKind.CONSTRUCTOR -> AnnotatedCallableKind.FUNCTION + else -> throw IllegalStateException("Unsupported callable kind: ${callableKind}") + } + } + +fun Name.ref() = StringRef.fromString(this.asString()) + +fun FqName.ref() = StringRef.fromString(this.asString())