001 /* 002 * Copyright 2010-2013 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.jet.codegen.binding; 018 019 import com.intellij.openapi.project.Project; 020 import com.intellij.psi.PsiElement; 021 import com.intellij.psi.util.PsiTreeUtil; 022 import org.jetbrains.annotations.NotNull; 023 import org.jetbrains.annotations.Nullable; 024 import org.jetbrains.jet.codegen.ClassBuilderFactories; 025 import org.jetbrains.jet.codegen.PackageCodegen; 026 import org.jetbrains.jet.codegen.state.GenerationState; 027 import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 028 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 029 import org.jetbrains.jet.lang.psi.*; 030 import org.jetbrains.jet.lang.resolve.BindingContext; 031 import org.jetbrains.jet.lang.resolve.BindingContextUtils; 032 import org.jetbrains.jet.lang.resolve.BindingTrace; 033 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 034 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 035 import org.jetbrains.jet.lang.resolve.name.FqName; 036 import org.jetbrains.jet.lang.resolve.name.Name; 037 import org.jetbrains.org.objectweb.asm.Type; 038 039 import java.util.ArrayList; 040 import java.util.Collection; 041 042 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; 043 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName; 044 045 public final class PsiCodegenPredictor { 046 private PsiCodegenPredictor() { 047 } 048 049 public static boolean checkPredictedNameFromPsi( 050 @NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor, @Nullable Type nameFromDescriptors 051 ) { 052 PsiElement element = descriptorToDeclaration(bindingContext, descriptor); 053 if (element instanceof JetDeclaration) { 054 String classNameFromPsi = getPredefinedJvmInternalName((JetDeclaration) element); 055 assert classNameFromPsi == null || Type.getObjectType(classNameFromPsi).equals(nameFromDescriptors) : 056 String.format("Invalid algorithm for getting qualified name from psi! Predicted: %s, actual %s\n" + 057 "Element: %s", classNameFromPsi, nameFromDescriptors, element.getText()); 058 } 059 060 return true; 061 } 062 063 /** 064 * @return null if no prediction can be done. 065 */ 066 @Nullable 067 public static String getPredefinedJvmInternalName(@NotNull JetDeclaration declaration) { 068 // TODO: Method won't work for declarations inside class objects 069 // TODO: Method won't give correct class name for traits implementations 070 071 JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(declaration, JetDeclaration.class); 072 if (parentDeclaration instanceof JetClassObject) { 073 assert declaration instanceof JetObjectDeclaration : "Only object declarations can be children of JetClassObject: " + declaration; 074 return getPredefinedJvmInternalName(parentDeclaration); 075 } 076 077 String parentInternalName; 078 if (parentDeclaration != null) { 079 parentInternalName = getPredefinedJvmInternalName(parentDeclaration); 080 if (parentInternalName == null) { 081 return null; 082 } 083 } 084 else { 085 FqName packageFqName = declaration.getContainingJetFile().getPackageFqName(); 086 087 if (declaration instanceof JetNamedFunction) { 088 JvmClassName packageClass = JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(packageFqName)); 089 Name name = ((JetNamedFunction) declaration).getNameAsName(); 090 return name == null ? null : packageClass.getInternalName() + "$" + name.asString(); 091 } 092 093 parentInternalName = JvmClassName.byFqNameWithoutInnerClasses(packageFqName).getInternalName(); 094 } 095 096 if (declaration instanceof JetClassObject) { 097 // Get parent and assign Class object prefix 098 return parentInternalName + JvmAbi.CLASS_OBJECT_SUFFIX; 099 } 100 101 if (!PsiTreeUtil.instanceOf(declaration, JetClass.class, JetObjectDeclaration.class, JetNamedFunction.class, JetProperty.class) || 102 declaration instanceof JetEnumEntry) { 103 // Other subclasses are not valid for class name prediction. 104 // For example EnumEntry, JetFunctionLiteral 105 return null; 106 } 107 108 Name name = ((JetNamedDeclaration) declaration).getNameAsName(); 109 if (name == null) { 110 return null; 111 } 112 113 if (declaration instanceof JetNamedFunction) { 114 if (!(parentDeclaration instanceof JetClass || parentDeclaration instanceof JetObjectDeclaration)) { 115 // Can't generate predefined name for internal functions 116 return null; 117 } 118 } 119 120 // NOTE: looks like a bug - for class in getter of top level property class name will be $propertyName$ClassName but not 121 // PackageClassName$propertyName$ClassName 122 if (declaration instanceof JetProperty) { 123 return parentInternalName + "$" + name.asString(); 124 } 125 126 if (parentInternalName.isEmpty()) { 127 return name.asString(); 128 } 129 130 return parentInternalName + (parentDeclaration == null ? "/" : "$") + name.asString(); 131 } 132 133 @Nullable 134 public static JetFile getFileForPackagePartName(@NotNull Collection<JetFile> allPackageFiles, @NotNull JvmClassName className) { 135 for (JetFile file : allPackageFiles) { 136 String internalName = PackageCodegen.getPackagePartInternalName(file); 137 JvmClassName jvmClassName = JvmClassName.byInternalName(internalName); 138 if (jvmClassName.equals(className)) { 139 return file; 140 } 141 } 142 return null; 143 } 144 145 @Nullable 146 public static JetFile getFileForCodegenNamedClass( 147 @NotNull BindingContext context, 148 @NotNull Collection<JetFile> allPackageFiles, 149 @NotNull String classInternalName 150 ) { 151 Project project = allPackageFiles.iterator().next().getProject(); 152 GenerationState state = 153 new GenerationState(project, ClassBuilderFactories.THROW_EXCEPTION, context, new ArrayList<JetFile>(allPackageFiles)); 154 state.beforeCompile(); 155 156 BindingTrace trace = state.getBindingTrace(); 157 for (ClassDescriptor classDescriptor : trace.getKeys(CodegenBinding.ASM_TYPE)) { 158 Type type = trace.get(CodegenBinding.ASM_TYPE, classDescriptor); 159 if (type != null && classInternalName.equals(type.getInternalName())) { 160 return BindingContextUtils.getContainingFile(trace.getBindingContext(), classDescriptor); 161 } 162 } 163 164 return null; 165 } 166 }