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