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