001 /* 002 * Copyright 2010-2013 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 com.intellij.psi.PsiElement; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.asm4.Type; 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.java.descriptor.JavaClassDescriptor; 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.JetType; 035 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 036 import org.jetbrains.jet.util.slicedmap.Slices; 037 import org.jetbrains.jet.util.slicedmap.WritableSlice; 038 039 import java.util.*; 040 041 import static org.jetbrains.jet.codegen.CodegenUtil.isInterface; 042 import static org.jetbrains.jet.lang.resolve.BindingContext.*; 043 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; 044 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClass; 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.createSimpleSlice(); 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, JavaClassDescriptor> SAM_VALUE = Slices.createSimpleSlice(); 060 061 private CodegenBinding() { 062 } 063 064 public static void initTrace(BindingTrace bindingTrace, Collection<JetFile> files) { 065 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(bindingTrace); 066 for (JetFile file : allFilesInNamespaces(bindingTrace.getBindingContext(), files)) { 067 file.accept(visitor); 068 } 069 } 070 071 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) { 072 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry)); 073 } 074 075 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) { 076 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor)); 077 } 078 079 @NotNull 080 public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) { 081 ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor); 082 //noinspection ConstantConditions 083 return asmType(bindingContext, classDescriptor); 084 } 085 086 @NotNull 087 public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) { 088 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script); 089 if (scriptDescriptor == null) { 090 throw new IllegalStateException("Script descriptor not found by PSI " + script); 091 } 092 return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor); 093 } 094 095 public static ClassDescriptor enclosingClassDescriptor(BindingContext bindingContext, ClassDescriptor descriptor) { 096 CalculatedClosure closure = bindingContext.get(CLOSURE, descriptor); 097 return closure == null ? null : closure.getEnclosingClass(); 098 } 099 100 @NotNull 101 public static ClassDescriptor anonymousClassForFunction( 102 @NotNull BindingContext bindingContext, 103 @NotNull FunctionDescriptor descriptor 104 ) { 105 //noinspection ConstantConditions 106 return bindingContext.get(CLASS_FOR_FUNCTION, descriptor); 107 } 108 109 @NotNull 110 private static Type asmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor descriptor) { 111 //noinspection ConstantConditions 112 return bindingContext.get(ASM_TYPE, 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 asmType(bindingContext, descriptor); 130 } 131 132 @NotNull 133 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) { 134 ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, descriptor); 135 return asmType(bindingContext, classDescriptor); 136 } 137 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 BindingTrace bindingTrace, 168 @Nullable JetElement element, 169 ClassDescriptor classDescriptor, 170 @Nullable ClassDescriptor enclosing, 171 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, 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 if (enclosing != null) { 198 recordInnerClass(bindingTrace, enclosing, classDescriptor); 199 } 200 } 201 202 private static void recordInnerClass( 203 @NotNull BindingTrace bindingTrace, 204 @NotNull ClassDescriptor outer, 205 @NotNull ClassDescriptor inner 206 ) { 207 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer); 208 if (innerClasses == null) { 209 innerClasses = new ArrayList<ClassDescriptor>(); 210 bindingTrace.record(INNER_CLASSES, outer, innerClasses); 211 } 212 innerClasses.add(inner); 213 } 214 215 public static void registerClassNameForScript( 216 BindingTrace bindingTrace, 217 @NotNull JetScript jetScript, 218 @NotNull Type asmType 219 ) { 220 ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript); 221 if (descriptor == null) { 222 throw new IllegalStateException("Descriptor is not found for PSI " + jetScript); 223 } 224 registerClassNameForScript(bindingTrace, descriptor, asmType); 225 } 226 227 @NotNull 228 public static Collection<JetFile> allFilesInNamespaces(BindingContext bindingContext, Collection<JetFile> files) { 229 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding 230 // for scripts especially in case of REPL 231 232 HashSet<FqName> names = new HashSet<FqName>(); 233 for (JetFile file : files) { 234 if (!file.isScript()) { 235 names.add(JetPsiUtil.getFQName(file)); 236 } 237 } 238 239 HashSet<JetFile> answer = new HashSet<JetFile>(); 240 answer.addAll(files); 241 242 for (FqName name : names) { 243 Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name); 244 if (jetFiles != null) { 245 answer.addAll(jetFiles); 246 } 247 } 248 249 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer); 250 Collections.sort(sortedAnswer, new Comparator<JetFile>() { 251 @NotNull 252 private String path(JetFile file) { 253 VirtualFile virtualFile = file.getVirtualFile(); 254 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName(); 255 return virtualFile.getPath(); 256 } 257 258 @Override 259 public int compare(JetFile first, JetFile second) { 260 return path(first).compareTo(path(second)); 261 } 262 }); 263 264 return sortedAnswer; 265 } 266 267 public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) { 268 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration); 269 if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) { 270 return true; 271 } 272 return false; 273 } 274 275 public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) { 276 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration); 277 if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) { 278 return true; 279 } 280 return false; 281 } 282 283 public static boolean isLocalNamedFun(DeclarationDescriptor fd) { 284 if (fd instanceof FunctionDescriptor) { 285 FunctionDescriptor descriptor = (FunctionDescriptor) fd; 286 return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial(); 287 } 288 return false; 289 } 290 291 @NotNull 292 public static Type getAsmType(@NotNull BindingTrace bindingTrace, @NotNull ClassDescriptor klass) { 293 klass = (ClassDescriptor) klass.getOriginal(); 294 Type alreadyComputedType = bindingTrace.getBindingContext().get(ASM_TYPE, klass); 295 if (alreadyComputedType != null) { 296 return alreadyComputedType; 297 } 298 299 Type asmType = Type.getObjectType(getAsmTypeImpl(bindingTrace, klass)); 300 301 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, klass, asmType); 302 bindingTrace.record(ASM_TYPE, klass, asmType); 303 return asmType; 304 } 305 306 @NotNull 307 private static String getAsmTypeImpl(@NotNull BindingTrace bindingTrace, @NotNull ClassDescriptor klass) { 308 DeclarationDescriptor container = klass.getContainingDeclaration(); 309 310 if (container instanceof PackageFragmentDescriptor) { 311 String shortName = klass.getName().getIdentifier(); 312 FqName fqName = ((PackageFragmentDescriptor) container).getFqName(); 313 return fqName.isRoot() ? shortName : fqName.asString().replace('.', '/') + '/' + shortName; 314 } 315 316 assert container instanceof ClassDescriptor : "Unexpected container: " + container + " for " + klass; 317 318 String containerInternalName = getAsmType(bindingTrace, (ClassDescriptor) container).getInternalName(); 319 if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) { 320 if (isEnumClass(container)) { 321 return containerInternalName; 322 } 323 else if (klass.getKind() == ClassKind.OBJECT) { 324 return containerInternalName + "$" + klass.getName(); 325 } 326 else { 327 return containerInternalName + JvmAbi.CLASS_OBJECT_SUFFIX; 328 } 329 } 330 return containerInternalName + "$" + klass.getName().getIdentifier(); 331 } 332 333 public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) { 334 if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false; 335 VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor; 336 return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar(); 337 } 338 339 public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) { 340 //noinspection SuspiciousMethodCalls 341 CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor); 342 return closure != null && closure.getCaptureThis() != null; 343 } 344 345 private static JetDelegatorToSuperCall findSuperCall( 346 BindingContext bindingContext, 347 JetElement classOrObject 348 ) { 349 if (!(classOrObject instanceof JetClassOrObject)) { 350 return null; 351 } 352 353 if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) { 354 return null; 355 } 356 for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) { 357 if (specifier instanceof JetDelegatorToSuperCall) { 358 JetType superType = bindingContext.get(TYPE, specifier.getTypeReference()); 359 assert superType != null; 360 ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor(); 361 assert superClassDescriptor != null; 362 if (!isInterface(superClassDescriptor)) { 363 return (JetDelegatorToSuperCall) specifier; 364 } 365 } 366 } 367 368 return null; 369 } 370 371 @NotNull 372 public static Collection<ClassDescriptor> getAllInnerClasses( 373 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass 374 ) { 375 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass); 376 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet(); 377 378 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>(); 379 380 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses); 381 do { 382 ClassDescriptor currentClass = stack.pop(); 383 if (allInnerClasses.add(currentClass)) { 384 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass); 385 if (nextClasses != null) { 386 for (ClassDescriptor nextClass : nextClasses) { 387 stack.push(nextClass); 388 } 389 } 390 } 391 } while (!stack.isEmpty()); 392 393 return allInnerClasses; 394 } 395 396 @NotNull 397 public static String getJvmInternalName(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) { 398 Type asmType = bindingContext.get(CodegenBinding.ASM_TYPE, classDescriptor); 399 assert (asmType != null); 400 401 String jvmInternalName = asmType.getClassName(); 402 assert (jvmInternalName != null); 403 404 return jvmInternalName; 405 } 406 }