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.util.Ref; 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.asm4.Type; 025 import org.jetbrains.jet.codegen.NamespaceCodegen; 026 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 027 import org.jetbrains.jet.lang.psi.*; 028 import org.jetbrains.jet.lang.resolve.BindingContext; 029 import org.jetbrains.jet.lang.resolve.BindingContextUtils; 030 import org.jetbrains.jet.lang.resolve.BindingTrace; 031 import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace; 032 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 033 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 034 import org.jetbrains.jet.lang.resolve.name.FqName; 035 import org.jetbrains.jet.lang.resolve.name.Name; 036 import org.jetbrains.jet.util.slicedmap.WritableSlice; 037 038 import java.util.Collection; 039 040 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; 041 import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName; 042 043 public final class PsiCodegenPredictor { 044 private PsiCodegenPredictor() { 045 } 046 047 public static boolean checkPredictedNameFromPsi( 048 @NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor, @Nullable Type nameFromDescriptors 049 ) { 050 PsiElement element = descriptorToDeclaration(bindingTrace.getBindingContext(), descriptor); 051 if (element instanceof JetDeclaration) { 052 String classNameFromPsi = getPredefinedJvmInternalName((JetDeclaration) element); 053 assert classNameFromPsi == null || Type.getObjectType(classNameFromPsi).equals(nameFromDescriptors) : 054 String.format("Invalid algorithm for getting qualified name from psi! Predicted: %s, actual %s\n" + 055 "Element: %s", classNameFromPsi, nameFromDescriptors, element.getText()); 056 } 057 058 return true; 059 } 060 061 /** 062 * TODO: Finish this method for all cases. Now it's only used and tested in JetLightClass. 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 String packageName = ((JetFile) declaration.getContainingFile()).getPackageName(); 086 if (packageName == null) { 087 return null; 088 } 089 090 if (declaration instanceof JetNamedFunction) { 091 JvmClassName packageClass = JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(new FqName(packageName))); 092 Name name = ((JetNamedFunction) declaration).getNameAsName(); 093 return name == null ? null : packageClass.getInternalName() + "$" + name.asString(); 094 } 095 096 parentInternalName = JvmClassName.byFqNameWithoutInnerClasses(packageName).getInternalName(); 097 } 098 099 if (declaration instanceof JetClassObject) { 100 // Get parent and assign Class object prefix 101 return parentInternalName + JvmAbi.CLASS_OBJECT_SUFFIX; 102 } 103 104 if (!PsiTreeUtil.instanceOf(declaration, JetClass.class, JetObjectDeclaration.class, JetNamedFunction.class, JetProperty.class) || 105 declaration instanceof JetEnumEntry) { 106 // Other subclasses are not valid for class name prediction. 107 // For example EnumEntry, JetFunctionLiteral 108 return null; 109 } 110 111 Name name = ((JetNamedDeclaration) declaration).getNameAsName(); 112 if (name == null) { 113 return null; 114 } 115 116 if (declaration instanceof JetNamedFunction) { 117 if (!(parentDeclaration instanceof JetClass || parentDeclaration instanceof JetObjectDeclaration)) { 118 // Can't generate predefined name for internal functions 119 return null; 120 } 121 } 122 123 // NOTE: looks like a bug - for class in getter of top level property class name will be $propertyName$ClassName but not 124 // namespace$propertyName$ClassName 125 if (declaration instanceof JetProperty) { 126 return parentInternalName + "$" + name.asString(); 127 } 128 129 if (parentInternalName.isEmpty()) { 130 return name.asString(); 131 } 132 133 return parentInternalName + (parentDeclaration == null ? "/" : "$") + name.asString(); 134 } 135 136 @Nullable 137 public static JetFile getFileForNamespacePartName(@NotNull Collection<JetFile> allNamespaceFiles, @NotNull JvmClassName className) { 138 for (JetFile file : allNamespaceFiles) { 139 String internalName = NamespaceCodegen.getNamespacePartInternalName(file); 140 JvmClassName jvmClassName = JvmClassName.byInternalName(internalName); 141 if (jvmClassName.equals(className)) { 142 return file; 143 } 144 } 145 return null; 146 } 147 148 @Nullable 149 public static JetFile getFileForCodegenNamedClass( 150 @NotNull BindingContext context, 151 @NotNull Collection<JetFile> allNamespaceFiles, 152 @NotNull final String classInternalName 153 ) { 154 final Ref<DeclarationDescriptor> resultingDescriptor = Ref.create(); 155 156 DelegatingBindingTrace trace = new DelegatingBindingTrace(context, "trace in PsiCodegenPredictor") { 157 @Override 158 public <K, V> void record(WritableSlice<K, V> slice, K key, V value) { 159 super.record(slice, key, value); 160 if (slice == CodegenBinding.ASM_TYPE && key instanceof DeclarationDescriptor && value instanceof Type) { 161 if (classInternalName.equals(((Type) value).getInternalName())) { 162 resultingDescriptor.set((DeclarationDescriptor) key); 163 } 164 } 165 } 166 }; 167 168 CodegenBinding.initTrace(trace, allNamespaceFiles); 169 170 return resultingDescriptor.isNull() ? null 171 : BindingContextUtils.getContainingFile(trace.getBindingContext(), resultingDescriptor.get()); 172 } 173 }