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