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