001 /* 002 * Copyright 2010-2015 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.kotlin.codegen.binding; 018 019 import com.intellij.openapi.vfs.VirtualFile; 020 import org.jetbrains.annotations.NotNull; 021 import org.jetbrains.annotations.Nullable; 022 import org.jetbrains.kotlin.codegen.JvmCodegenUtil; 023 import org.jetbrains.kotlin.codegen.SamType; 024 import org.jetbrains.kotlin.codegen.state.GenerationState; 025 import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; 026 import org.jetbrains.kotlin.descriptors.*; 027 import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; 028 import org.jetbrains.kotlin.name.FqName; 029 import org.jetbrains.kotlin.psi.*; 030 import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; 031 import org.jetbrains.kotlin.resolve.BindingContext; 032 import org.jetbrains.kotlin.resolve.BindingTrace; 033 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; 034 import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice; 035 import org.jetbrains.kotlin.util.slicedMap.Slices; 036 import org.jetbrains.kotlin.util.slicedMap.WritableSlice; 037 import org.jetbrains.org.objectweb.asm.Type; 038 039 import java.util.*; 040 041 import static org.jetbrains.kotlin.resolve.BindingContext.*; 042 043 public class CodegenBinding { 044 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice(); 045 046 public static final WritableSlice<CallableDescriptor, ClassDescriptor> CLASS_FOR_CALLABLE = Slices.createSimpleSlice(); 047 048 public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice(); 049 050 public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice(); 051 052 public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice(); 053 054 public static final WritableSlice<KtExpression, SamType> SAM_VALUE = Slices.createSimpleSlice(); 055 056 public static final WritableSlice<KtCallElement, KtExpression> SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice(); 057 058 public static final WritableSlice<KtWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.createSimpleSlice(); 059 060 public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE = 061 Slices.createSimpleSlice(); 062 063 static { 064 BasicWritableSlice.initSliceDebugNames(CodegenBinding.class); 065 } 066 067 private CodegenBinding() { 068 } 069 070 public static void initTrace(@NotNull GenerationState state) { 071 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state); 072 for (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) { 073 file.accept(visitor); 074 } 075 } 076 077 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, KtEnumEntry enumEntry) { 078 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry)); 079 } 080 081 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) { 082 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor)); 083 } 084 085 @NotNull 086 public static ClassDescriptor anonymousClassForCallable( 087 @NotNull BindingContext bindingContext, 088 @NotNull CallableDescriptor descriptor 089 ) { 090 //noinspection ConstantConditions 091 return bindingContext.get(CLASS_FOR_CALLABLE, descriptor); 092 } 093 094 @NotNull 095 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull KtElement expression) { 096 if (expression instanceof KtObjectLiteralExpression) { 097 expression = ((KtObjectLiteralExpression) expression).getObjectDeclaration(); 098 } 099 100 ClassDescriptor descriptor = bindingContext.get(CLASS, expression); 101 if (descriptor != null) { 102 return getAsmType(bindingContext, descriptor); 103 } 104 105 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression); 106 if (functionDescriptor != null) { 107 return asmTypeForAnonymousClass(bindingContext, functionDescriptor); 108 } 109 110 VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression); 111 if (variableDescriptor != null) { 112 return asmTypeForAnonymousClass(bindingContext, variableDescriptor); 113 } 114 115 throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilsKt.getElementTextWithContext(expression)); 116 } 117 118 @NotNull 119 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) { 120 return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor)); 121 } 122 123 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) { 124 if (classDescriptor.getKind() != ClassKind.CLASS) { 125 return false; 126 } 127 128 MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor); 129 if (closure == null || closure.getEnclosingClass() == null) { 130 return false; 131 } 132 133 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor); 134 } 135 136 static void recordClosure( 137 @NotNull BindingTrace trace, 138 @NotNull ClassDescriptor classDescriptor, 139 @Nullable ClassDescriptor enclosing, 140 @NotNull Type asmType, 141 @NotNull JvmFileClassesProvider fileClassesManager 142 ) { 143 KtElement element = (KtElement) DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor); 144 assert element != null : "No source element for " + classDescriptor; 145 146 MutableClosure closure = new MutableClosure(classDescriptor, enclosing); 147 148 if (classDescriptor.isInner()) { 149 closure.setCaptureThis(); 150 } 151 152 assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType, fileClassesManager); 153 trace.record(ASM_TYPE, classDescriptor, asmType); 154 trace.record(CLOSURE, classDescriptor, closure); 155 156 // Note: at the moment this is needed for light classes only 157 // TODO: refactor this out 158 if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) { 159 recordInnerClass(trace, enclosing, classDescriptor); 160 } 161 } 162 163 private static void recordInnerClass( 164 @NotNull BindingTrace bindingTrace, 165 @NotNull ClassDescriptor outer, 166 @NotNull ClassDescriptor inner 167 ) { 168 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer); 169 if (innerClasses == null) { 170 innerClasses = new ArrayList<ClassDescriptor>(1); 171 bindingTrace.record(INNER_CLASSES, outer, innerClasses); 172 } 173 innerClasses.add(inner); 174 } 175 176 @NotNull 177 private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) { 178 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding 179 // for scripts especially in case of REPL 180 181 Set<FqName> names = new HashSet<FqName>(); 182 for (KtFile file : files) { 183 if (!file.isScript()) { 184 names.add(file.getPackageFqName()); 185 } 186 } 187 188 Set<KtFile> answer = new HashSet<KtFile>(); 189 answer.addAll(files); 190 191 for (FqName name : names) { 192 Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name); 193 if (jetFiles != null) { 194 answer.addAll(jetFiles); 195 } 196 } 197 198 List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer); 199 Collections.sort(sortedAnswer, new Comparator<KtFile>() { 200 @NotNull 201 private String path(KtFile file) { 202 VirtualFile virtualFile = file.getVirtualFile(); 203 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName(); 204 return virtualFile.getPath(); 205 } 206 207 @Override 208 public int compare(@NotNull KtFile first, @NotNull KtFile second) { 209 return path(first).compareTo(path(second)); 210 } 211 }); 212 213 return sortedAnswer; 214 } 215 216 @NotNull 217 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) { 218 Type type = bindingContext.get(ASM_TYPE, klass); 219 assert type != null : "Type is not yet recorded for " + klass; 220 return type; 221 } 222 223 @NotNull 224 public static Collection<ClassDescriptor> getAllInnerClasses( 225 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass 226 ) { 227 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass); 228 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet(); 229 230 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>(); 231 232 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses); 233 do { 234 ClassDescriptor currentClass = stack.pop(); 235 if (allInnerClasses.add(currentClass)) { 236 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass); 237 if (nextClasses != null) { 238 for (ClassDescriptor nextClass : nextClasses) { 239 stack.push(nextClass); 240 } 241 } 242 } 243 } while (!stack.isEmpty()); 244 245 return allInnerClasses; 246 } 247 }