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