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.lang.descriptors.*; 025 import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl; 026 import org.jetbrains.jet.lang.psi.*; 027 import org.jetbrains.jet.lang.resolve.BindingContext; 028 import org.jetbrains.jet.lang.resolve.BindingTrace; 029 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 030 import org.jetbrains.jet.lang.resolve.name.FqName; 031 import org.jetbrains.jet.lang.resolve.name.Name; 032 import org.jetbrains.jet.lang.resolve.name.SpecialNames; 033 import org.jetbrains.jet.lang.resolve.scopes.JetScope; 034 import org.jetbrains.jet.lang.types.JetType; 035 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 036 import org.jetbrains.jet.util.slicedmap.BasicWritableSlice; 037 import org.jetbrains.jet.util.slicedmap.Slices; 038 import org.jetbrains.jet.util.slicedmap.WritableSlice; 039 import org.jetbrains.org.objectweb.asm.Type; 040 041 import java.util.*; 042 043 import static org.jetbrains.jet.codegen.JvmCodegenUtil.isInterface; 044 import static org.jetbrains.jet.lang.resolve.BindingContext.*; 045 046 public class CodegenBinding { 047 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice(); 048 049 public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice(); 050 051 public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice(); 052 053 public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createCollectiveSlice(); 054 055 public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice(); 056 057 public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice(); 058 059 public static final WritableSlice<JetExpression, SamType> SAM_VALUE = Slices.createSimpleSlice(); 060 061 static { 062 BasicWritableSlice.initSliceDebugNames(CodegenBinding.class); 063 } 064 065 private CodegenBinding() { 066 } 067 068 public static void initTrace(@NotNull GenerationState state) { 069 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state); 070 for (JetFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) { 071 file.accept(visitor); 072 } 073 } 074 075 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) { 076 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry)); 077 } 078 079 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) { 080 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor)); 081 } 082 083 // SCRIPT: Generate asmType for script, move to ScriptingUtil 084 @NotNull 085 public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) { 086 ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor); 087 //noinspection ConstantConditions 088 return getAsmType(bindingContext, classDescriptor); 089 } 090 091 // SCRIPT: Generate asmType for script, move to ScriptingUtil 092 @NotNull 093 public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) { 094 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script); 095 if (scriptDescriptor == null) { 096 throw new IllegalStateException("Script descriptor not found by PSI " + script); 097 } 098 return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor); 099 } 100 101 public static ClassDescriptor enclosingClassDescriptor(BindingContext bindingContext, ClassDescriptor descriptor) { 102 CalculatedClosure closure = bindingContext.get(CLOSURE, descriptor); 103 return closure == null ? null : closure.getEnclosingClass(); 104 } 105 106 @NotNull 107 public static ClassDescriptor anonymousClassForFunction( 108 @NotNull BindingContext bindingContext, 109 @NotNull FunctionDescriptor descriptor 110 ) { 111 //noinspection ConstantConditions 112 return bindingContext.get(CLASS_FOR_FUNCTION, descriptor); 113 } 114 115 @NotNull 116 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) { 117 if (expression instanceof JetObjectLiteralExpression) { 118 JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression; 119 expression = jetObjectLiteralExpression.getObjectDeclaration(); 120 } 121 122 ClassDescriptor descriptor = bindingContext.get(CLASS, expression); 123 if (descriptor == null) { 124 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression); 125 assert functionDescriptor != null; 126 return asmTypeForAnonymousClass(bindingContext, functionDescriptor); 127 } 128 129 return getAsmType(bindingContext, descriptor); 130 } 131 132 @NotNull 133 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) { 134 return getAsmType(bindingContext, anonymousClassForFunction(bindingContext, descriptor)); 135 } 136 137 // SCRIPT: register asmType for script descriptor, move to ScriptingUtil 138 public static void registerClassNameForScript( 139 BindingTrace bindingTrace, 140 @NotNull ScriptDescriptor scriptDescriptor, 141 @NotNull Type asmType 142 ) { 143 ClassDescriptorImpl classDescriptor = 144 new ClassDescriptorImpl(scriptDescriptor, Name.special("<script-" + asmType.getInternalName() + ">"), Modality.FINAL, 145 Collections.singleton(KotlinBuiltIns.getInstance().getAnyType())); 146 classDescriptor.initialize(JetScope.EMPTY, Collections.<ConstructorDescriptor>emptySet(), null); 147 148 recordClosure(bindingTrace, null, classDescriptor, null, asmType); 149 150 bindingTrace.record(CLASS_FOR_SCRIPT, scriptDescriptor, classDescriptor); 151 } 152 153 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) { 154 if (classDescriptor.getKind() != ClassKind.CLASS) { 155 return false; 156 } 157 158 ClassDescriptor enclosing = enclosingClassDescriptor(bindingContext, classDescriptor); 159 if (enclosing == null) { 160 return false; 161 } 162 163 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor); 164 } 165 166 static void recordClosure( 167 @NotNull BindingTrace bindingTrace, 168 @Nullable JetElement element, 169 @NotNull ClassDescriptor classDescriptor, 170 @Nullable ClassDescriptor enclosing, 171 @NotNull Type asmType 172 ) { 173 JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element); 174 175 CallableDescriptor enclosingReceiver = null; 176 if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) { 177 enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration(); 178 enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor 179 ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty() 180 : enclosingReceiver; 181 182 if (enclosingReceiver.getReceiverParameter() == null) { 183 enclosingReceiver = null; 184 } 185 } 186 187 MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver); 188 189 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace.getBindingContext(), classDescriptor, asmType); 190 bindingTrace.record(ASM_TYPE, classDescriptor, asmType); 191 bindingTrace.record(CLOSURE, classDescriptor, closure); 192 193 if (classDescriptor.isInner()) { 194 closure.setCaptureThis(); 195 } 196 197 //TEMPORARY EAT INNER CLASS INFO FOR FUNCTION LITERALS 198 //TODO: we should understand that lambda/closure would be inlined and don't generate inner class record 199 if (enclosing != null && !(element instanceof JetFunctionLiteral)) { 200 recordInnerClass(bindingTrace, enclosing, classDescriptor); 201 } 202 } 203 204 private static void recordInnerClass( 205 @NotNull BindingTrace bindingTrace, 206 @NotNull ClassDescriptor outer, 207 @NotNull ClassDescriptor inner 208 ) { 209 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer); 210 if (innerClasses == null) { 211 innerClasses = new ArrayList<ClassDescriptor>(); 212 bindingTrace.record(INNER_CLASSES, outer, innerClasses); 213 } 214 innerClasses.add(inner); 215 } 216 217 // SCRIPT: register asmType for script, move to ScriptingUtil 218 public static void registerClassNameForScript( 219 BindingTrace bindingTrace, 220 @NotNull JetScript jetScript, 221 @NotNull Type asmType 222 ) { 223 ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript); 224 if (descriptor == null) { 225 throw new IllegalStateException("Descriptor is not found for PSI " + jetScript); 226 } 227 registerClassNameForScript(bindingTrace, descriptor, asmType); 228 } 229 230 @NotNull 231 private static Collection<JetFile> allFilesInPackages(BindingContext bindingContext, Collection<JetFile> files) { 232 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding 233 // for scripts especially in case of REPL 234 235 // SCRIPT: collect fq names for files that are not scripts 236 HashSet<FqName> names = new HashSet<FqName>(); 237 for (JetFile file : files) { 238 if (!file.isScript()) { 239 names.add(file.getPackageFqName()); 240 } 241 } 242 243 HashSet<JetFile> answer = new HashSet<JetFile>(); 244 answer.addAll(files); 245 246 for (FqName name : names) { 247 Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name); 248 if (jetFiles != null) { 249 answer.addAll(jetFiles); 250 } 251 } 252 253 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer); 254 Collections.sort(sortedAnswer, new Comparator<JetFile>() { 255 @NotNull 256 private String path(JetFile file) { 257 VirtualFile virtualFile = file.getVirtualFile(); 258 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName(); 259 return virtualFile.getPath(); 260 } 261 262 @Override 263 public int compare(@NotNull JetFile first, @NotNull JetFile second) { 264 return path(first).compareTo(path(second)); 265 } 266 }); 267 268 return sortedAnswer; 269 } 270 271 public static boolean isLocalNamedFun(@Nullable DeclarationDescriptor fd) { 272 return isLocalFunOrLambda(fd) && !fd.getName().isSpecial(); 273 } 274 275 /*named or not*/ 276 public static boolean isLocalFunOrLambda(@Nullable DeclarationDescriptor fd) { 277 if (fd instanceof FunctionDescriptor) { 278 FunctionDescriptor descriptor = (FunctionDescriptor) fd; 279 return descriptor.getVisibility() == Visibilities.LOCAL; 280 } 281 return false; 282 } 283 284 @NotNull 285 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) { 286 klass = (ClassDescriptor) klass.getOriginal(); 287 Type alreadyComputedType = bindingContext.get(ASM_TYPE, klass); 288 if (alreadyComputedType != null) { 289 return alreadyComputedType; 290 } 291 292 Type asmType = Type.getObjectType(getAsmTypeImpl(bindingContext, klass)); 293 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingContext, klass, asmType); 294 return asmType; 295 } 296 297 @NotNull 298 private static String getAsmTypeImpl(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) { 299 DeclarationDescriptor container = klass.getContainingDeclaration(); 300 301 Name name = SpecialNames.safeIdentifier(klass.getName()); 302 if (container instanceof PackageFragmentDescriptor) { 303 String shortName = name.getIdentifier(); 304 FqName fqName = ((PackageFragmentDescriptor) container).getFqName(); 305 return fqName.isRoot() ? shortName : fqName.asString().replace('.', '/') + '/' + shortName; 306 } 307 308 assert container instanceof ClassDescriptor : "Unexpected container: " + container + " for " + klass; 309 310 String containerInternalName = getAsmType(bindingContext, (ClassDescriptor) container).getInternalName(); 311 switch (klass.getKind()) { 312 case ENUM_ENTRY: 313 return containerInternalName; 314 case CLASS_OBJECT: 315 return containerInternalName + JvmAbi.CLASS_OBJECT_SUFFIX; 316 default: 317 return containerInternalName + "$" + name.getIdentifier(); 318 } 319 } 320 321 public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) { 322 //noinspection SuspiciousMethodCalls 323 CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor); 324 return closure != null && closure.getCaptureThis() != null; 325 } 326 327 private static JetDelegatorToSuperCall findSuperCall(BindingContext bindingContext, JetElement classOrObject) { 328 if (!(classOrObject instanceof JetClassOrObject)) { 329 return null; 330 } 331 332 if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) { 333 return null; 334 } 335 336 for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) { 337 if (specifier instanceof JetDelegatorToSuperCall) { 338 JetTypeReference typeReference = specifier.getTypeReference(); 339 340 JetType superType = bindingContext.get(TYPE, typeReference); 341 assert superType != null: String.format( 342 "No type in binding context for \n---\n%s\n---\n", JetPsiUtil.getElementTextWithContext(specifier)); 343 344 ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor(); 345 assert superClassDescriptor != null; 346 if (!isInterface(superClassDescriptor)) { 347 return (JetDelegatorToSuperCall) specifier; 348 } 349 } 350 } 351 352 return null; 353 } 354 355 @NotNull 356 public static Collection<ClassDescriptor> getAllInnerClasses( 357 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass 358 ) { 359 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass); 360 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet(); 361 362 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>(); 363 364 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses); 365 do { 366 ClassDescriptor currentClass = stack.pop(); 367 if (allInnerClasses.add(currentClass)) { 368 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass); 369 if (nextClasses != null) { 370 for (ClassDescriptor nextClass : nextClasses) { 371 stack.push(nextClass); 372 } 373 } 374 } 375 } while (!stack.isEmpty()); 376 377 return allInnerClasses; 378 } 379 380 @NotNull 381 public static String getJvmInternalName(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) { 382 return getAsmType(bindingContext, classDescriptor).getClassName(); 383 } 384 }