Build kotlin stubs for compiled Kotlin files

This means that JetPsi would be built for compiled kotlin files
This commit is contained in:
Pavel V. Talanov
2014-10-31 20:24:54 +03:00
parent e9fa9bac47
commit afc6ba5e16
9 changed files with 1021 additions and 41 deletions
@@ -106,7 +106,7 @@ public class JetClassElementType extends JetStubElementType<KotlinClassStub, Jet
StubIndexServiceFactory.getInstance().indexClass(stub, sink);
}
private static JetClassElementType getStubType(boolean isEnumEntry) {
public static JetClassElementType getStubType(boolean isEnumEntry) {
return isEnumEntry ? JetStubElementTypes.ENUM_ENTRY : JetStubElementTypes.CLASS;
}
}
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiManager;
import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.compiled.ClsStubBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.plugin.decompiler.stubBuilder.KotlinClsStubBuilder;
public class JetClassFileDecompiler extends ClassFileDecompilers.Full {
private final ClsStubBuilder stubBuilder = new KotlinClsStubBuilder();
@@ -1,40 +0,0 @@
/*
* 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
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.compiled.ClsStubBuilder
import com.intellij.psi.impl.compiled.ClassFileStubBuilder
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.stubs.PsiFileStub
import com.intellij.util.cls.ClsFormatException
import com.intellij.util.indexing.FileContent
public class KotlinClsStubBuilder : ClsStubBuilder() {
override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + 1
throws(javaClass<ClsFormatException>())
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
val file = content.getFile()
if (isKotlinInternalCompiledFile(file)) {
return null
}
return ClsFileImpl.buildFileStub(file, content.getContent())
}
}
@@ -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<out PsiElement>,
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<out PsiElement>,
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<out PsiElement> {
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"
}
}
@@ -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<out PsiElement>, classProto: ProtoBuf.Class, classId: ClassId, context: ClsStubBuilderContext) {
ClassClsStubBuilder(parent, classProto, classId, context).build()
}
private class ClassClsStubBuilder(
private val parentStub: StubElement<out PsiElement>,
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<out PsiElement> {
val isClassObject = classKind == ProtoBuf.Class.Kind.CLASS_OBJECT
if (isClassObject) {
val classObjectStub = KotlinPlaceHolderStubImpl<JetClassObject>(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<out PsiElement>): 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<JetModifierKeywordToken>()
}
return createModifierListStubForDeclaration(parent, classProto.getFlags(), relevantFlags, additionalModifiers)
}
private fun doCreateClassOrObjectStub(parent: StubElement<out PsiElement>): StubElement<out PsiElement> {
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<JetParameterList>(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<JetDelegationSpecifierList>(classOrObjectStub, JetStubElementTypes.DELEGATION_SPECIFIER_LIST)
classProto.getSupertypeList().forEach { type ->
val superClassStub = KotlinPlaceHolderStubImpl<JetDelegatorToSuperClass>(
delegationSpecifierListStub, JetStubElementTypes.DELEGATOR_SUPER_CLASS
)
typeStubBuilder.createTypeReferenceStub(superClassStub, type)
}
}
private fun createClassBodyAndMemberStubs() {
val classBody = KotlinPlaceHolderStubImpl<JetClassBody>(classOrObjectStub, JetStubElementTypes.CLASS_BODY)
createClassObjectStub(classBody)
createEnumEntryStubs(classBody)
createCallableMemberStubs(classBody)
createInnerAndNestedClasses(classBody)
}
private fun createClassObjectStub(classBody: KotlinPlaceHolderStubImpl<JetClassBody>) {
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<JetClassBody>) {
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<JetClassBody>) {
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<JetClassBody>) {
classProto.getNestedClassNameList().forEach { id ->
val nestedClassId = classId.createNestedClassId(c.nameResolver.getName(id))
createNestedClassStub(classBody, nestedClassId)
}
}
private fun createNestedClassStub(classBody: StubElement<out PsiElement>, nestedClassId: ClassId) {
val classData = c.components.classDataFinder.findClassData(nestedClassId)!!
createClassStub(classBody, classData.getClassProto(), nestedClassId, c.child(classData.getNameResolver()))
}
}
@@ -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<ProtoBuf.TypeParameter>)
= 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<ProtoBuf.TypeParameter>,
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<ProtoBuf.TypeParameter>, 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<ClassId, Unit>(
LockBasedStorageManager.NO_LOCKS, kotlinClassFinder, errorReporter) {
override fun loadConstant(desc: String, initializer: Any) = null
override fun loadAnnotation(annotationClassId: ClassId, result: MutableList<ClassId>): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
result.add(annotationClassId)
return null
}
}
@@ -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<ClsFormatException>())
fun doBuildFileStub(file: VirtualFile): PsiFileStub<JetFile>? {
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<KotlinClsStubBuilder>())
}
}
@@ -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<out PsiElement>, typeProto: Type) {
val typeReference = KotlinPlaceHolderStubImpl<JetTypeReference>(parent, JetStubElementTypes.TYPE_REFERENCE)
createTypeStub(typeReference, typeProto)
}
private fun createTypeStub(parent: StubElement<out PsiElement>, type: Type) {
val isNullable = type.getNullable()
val effectiveParent = if (isNullable) KotlinPlaceHolderStubImpl<JetNullableType>(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<out PsiElement>, 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<Type.Argument>) {
if (typeArgumentProtoList.isEmpty()) {
return
}
val typeArgumentsListStub = KotlinPlaceHolderStubImpl<JetTypeArgumentList>(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<out PsiElement>, type: Type, isExtensionFunctionType: Boolean) {
val typeArgumentList = type.getArgumentList()
val functionType = KotlinPlaceHolderStubImpl<JetFunctionType>(parent, JetStubElementTypes.FUNCTION_TYPE)
if (isExtensionFunctionType) {
val functionTypeReceiverStub
= KotlinPlaceHolderStubImpl<JetFunctionTypeReceiver>(functionType, JetStubElementTypes.FUNCTION_TYPE_RECEIVER)
val receiverTypeProto = typeArgumentList.first().getType()
createTypeReferenceStub(functionTypeReceiverStub, receiverTypeProto)
}
val parameterList = KotlinPlaceHolderStubImpl<JetParameterList>(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<out PsiElement>, callableProto: ProtoBuf.Callable, container: ProtoContainer) {
val callableKind = Flags.CALLABLE_KIND[callableProto.getFlags()]
if (callableKind == CallableKind.VAL || callableKind == CallableKind.VAR) {
return
}
val parameterListStub = KotlinPlaceHolderStubImpl<JetParameterList>(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<out PsiElement>,
typeParameterProtoList: List<ProtoBuf.TypeParameter>
): List<Pair<Name, Type>> {
if (typeParameterProtoList.isEmpty()) return listOf()
val typeParameterListStub = KotlinPlaceHolderStubImpl<JetTypeParameterList>(parent, JetStubElementTypes.TYPE_PARAMETER_LIST)
val protosForTypeConstraintList = arrayListOf<Pair<Name, Type>>()
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<out PsiElement>,
protosForTypeConstraintList: List<Pair<Name, Type>>
) {
if (protosForTypeConstraintList.isEmpty()) {
return
}
val typeConstraintListStub = KotlinPlaceHolderStubImpl<JetTypeConstraintList>(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<JetModifierKeywordToken>()
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()
}
}
@@ -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<JetPackageDirective>(fileStub, JetStubElementTypes.PACKAGE_DIRECTIVE)
createStubForPackageName(packageDirectiveStub, packageFqName)
return fileStub
}
fun createStubForPackageName(packageDirectiveStub: KotlinPlaceHolderStubImpl<JetPackageDirective>, packageFqName: FqName) {
val segments = packageFqName.pathSegments().toArrayList()
val iterator = segments.listIterator(segments.size())
fun recCreateStubForPackageName(current: StubElement<out PsiElement>) {
when (iterator.previousIndex()) {
-1 -> return
0 -> {
KotlinNameReferenceExpressionStubImpl(current, iterator.previous().ref())
return
}
else -> {
val lastSegment = iterator.previous()
val receiver = KotlinPlaceHolderStubImpl<JetDotQualifiedExpression>(current, JetStubElementTypes.DOT_QUALIFIED_EXPRESSION)
recCreateStubForPackageName(receiver)
KotlinNameReferenceExpressionStubImpl(receiver, lastSegment.ref())
}
}
}
recCreateStubForPackageName(packageDirectiveStub)
}
fun createStubForTypeName(typeClassId: ClassId, parent: StubElement<out PsiElement>): 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<out PsiElement>): 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<out PsiElement>,
flags: Int,
flagsToTranslate: List<FlagsToModifiers> = listOf(),
additionalModifiers: List<JetModifierKeywordToken> = listOf()
): KotlinModifierListStubImpl {
assert(flagsToTranslate.isNotEmpty())
val modifiers = flagsToTranslate.map { it.getModifiers(flags) }.filterNotNull() + additionalModifiers
return createModifierListStub(parent, modifiers)!!
}
fun createModifierListStub(
parent: StubElement<out PsiElement>,
modifiers: Collection<JetModifierKeywordToken>
): KotlinModifierListStubImpl? {
if (modifiers.isEmpty()) {
return null
}
return KotlinModifierListStubImpl(
parent,
ModifierMaskUtils.computeMask { it in modifiers },
JetStubElementTypes.MODIFIER_LIST
)
}
fun createAnnotationStubs(annotationIds: List<ClassId>, modifierList: KotlinModifierListStubImpl) = annotationIds.forEach {
annotationClassId ->
val annotationEntryStubImpl = KotlinAnnotationEntryStubImpl(
modifierList,
shortName = annotationClassId.asSingleFqName().shortName().ref(),
hasValueArguments = false
)
val constructorCallee = KotlinPlaceHolderStubImpl<JetConstructorCalleeExpression>(annotationEntryStubImpl, JetStubElementTypes.CONSTRUCTOR_CALLEE)
val typeReference = KotlinPlaceHolderStubImpl<JetTypeReference>(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())