001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.codegen.binding; 018 019 import com.intellij.psi.PsiElement; 020 import com.intellij.psi.util.PsiTreeUtil; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.kotlin.codegen.AsmUtil; 024 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 025 import org.jetbrains.kotlin.fileClasses.FileClasses; 026 import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; 027 import org.jetbrains.kotlin.name.Name; 028 import org.jetbrains.kotlin.psi.*; 029 import org.jetbrains.org.objectweb.asm.Type; 030 031 import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration; 032 033 public final class PsiCodegenPredictor { 034 private PsiCodegenPredictor() { 035 } 036 037 public static boolean checkPredictedNameFromPsi( 038 @NotNull DeclarationDescriptor descriptor, 039 @Nullable Type nameFromDescriptors, 040 @NotNull JvmFileClassesProvider fileClassesManager 041 ) { 042 PsiElement element = descriptorToDeclaration(descriptor); 043 if (element instanceof KtDeclaration) { 044 String classNameFromPsi = getPredefinedJvmInternalName((KtDeclaration) element, fileClassesManager); 045 assert classNameFromPsi == null || Type.getObjectType(classNameFromPsi).equals(nameFromDescriptors) : 046 String.format("Invalid algorithm for getting qualified name from psi! Predicted: %s, actual %s\n" + 047 "Element: %s", classNameFromPsi, nameFromDescriptors, element.getText()); 048 } 049 050 return true; 051 } 052 053 /** 054 * @return null if no prediction can be done. 055 */ 056 @Nullable 057 public static String getPredefinedJvmInternalName( 058 @NotNull KtDeclaration declaration, 059 @NotNull JvmFileClassesProvider fileClassesProvider 060 ) { 061 // TODO: Method won't work for declarations inside companion objects 062 // TODO: Method won't give correct class name for traits implementations 063 064 if (declaration instanceof KtPropertyAccessor) { 065 return getPredefinedJvmInternalName(((KtPropertyAccessor) declaration).getProperty(), fileClassesProvider); 066 } 067 KtDeclaration parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration); 068 069 String parentInternalName; 070 if (parentDeclaration != null) { 071 parentInternalName = getPredefinedJvmInternalName(parentDeclaration, fileClassesProvider); 072 if (parentInternalName == null) { 073 return null; 074 } 075 } 076 else { 077 KtFile containingFile = declaration.getContainingKtFile(); 078 079 if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty) { 080 Name name = ((KtNamedDeclaration) declaration).getNameAsName(); 081 return name == null ? null : FileClasses.getFileClassInternalName(fileClassesProvider, containingFile) + "$" + name.asString(); 082 } 083 084 parentInternalName = AsmUtil.internalNameByFqNameWithoutInnerClasses(containingFile.getPackageFqName()); 085 } 086 087 if (!PsiTreeUtil.instanceOf(declaration, KtClass.class, KtObjectDeclaration.class, KtNamedFunction.class, KtProperty.class) || 088 isEnumEntryWithoutBody(declaration)) { 089 // Other subclasses are not valid for class name prediction. 090 // For example JetFunctionLiteral 091 return null; 092 } 093 094 Name name = ((KtNamedDeclaration) declaration).getNameAsName(); 095 if (name == null) { 096 return null; 097 } 098 099 if (declaration instanceof KtNamedFunction) { 100 if (!(parentDeclaration instanceof KtClass || parentDeclaration instanceof KtObjectDeclaration)) { 101 // Can't generate predefined name for internal functions 102 return null; 103 } 104 } 105 106 // NOTE: looks like a bug - for class in getter of top level property class name will be $propertyName$ClassName but not 107 // PackageClassName$propertyName$ClassName 108 if (declaration instanceof KtProperty) { 109 return parentInternalName + "$" + name.asString(); 110 } 111 112 if (parentInternalName.isEmpty()) { 113 return name.asString(); 114 } 115 116 return parentInternalName + (parentDeclaration == null ? "/" : "$") + name.asString(); 117 } 118 119 private static boolean isEnumEntryWithoutBody(KtDeclaration declaration) { 120 if (!(declaration instanceof KtEnumEntry)) { 121 return false; 122 } 123 KtClassBody body = ((KtEnumEntry) declaration).getBody(); 124 return body == null || body.getDeclarations().size() == 0; 125 } 126 }