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 }