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.PackageCodegen; 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 * @return null if no prediction can be done. 063 */ 064 @Nullable 065 public static String getPredefinedJvmInternalName(@NotNull JetDeclaration declaration) { 066 // TODO: Method won't work for declarations inside class objects 067 // TODO: Method won't give correct class name for traits implementations 068 069 JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(declaration, JetDeclaration.class); 070 if (parentDeclaration instanceof JetClassObject) { 071 assert declaration instanceof JetObjectDeclaration : "Only object declarations can be children of JetClassObject: " + declaration; 072 return getPredefinedJvmInternalName(parentDeclaration); 073 } 074 075 String parentInternalName; 076 if (parentDeclaration != null) { 077 parentInternalName = getPredefinedJvmInternalName(parentDeclaration); 078 if (parentInternalName == null) { 079 return null; 080 } 081 } 082 else { 083 String packageName = ((JetFile) declaration.getContainingFile()).getPackageName(); 084 if (packageName == null) { 085 return null; 086 } 087 088 if (declaration instanceof JetNamedFunction) { 089 JvmClassName packageClass = JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(new FqName(packageName))); 090 Name name = ((JetNamedFunction) declaration).getNameAsName(); 091 return name == null ? null : packageClass.getInternalName() + "$" + name.asString(); 092 } 093 094 parentInternalName = JvmClassName.byFqNameWithoutInnerClasses(packageName).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 BindingContext context, 149 @NotNull Collection<JetFile> allPackageFiles, 150 @NotNull final String classInternalName 151 ) { 152 final Ref<DeclarationDescriptor> resultingDescriptor = Ref.create(); 153 154 DelegatingBindingTrace trace = new DelegatingBindingTrace(context, "trace in PsiCodegenPredictor") { 155 @Override 156 public <K, V> void record(WritableSlice<K, V> slice, K key, V value) { 157 super.record(slice, key, value); 158 if (slice == CodegenBinding.ASM_TYPE && key instanceof DeclarationDescriptor && value instanceof Type) { 159 if (classInternalName.equals(((Type) value).getInternalName())) { 160 resultingDescriptor.set((DeclarationDescriptor) key); 161 } 162 } 163 } 164 }; 165 166 CodegenBinding.initTrace(trace, allPackageFiles); 167 168 return resultingDescriptor.isNull() ? null 169 : BindingContextUtils.getContainingFile(trace.getBindingContext(), resultingDescriptor.get()); 170 } 171 }