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 017package org.jetbrains.jet.codegen.binding; 018 019import com.intellij.openapi.vfs.VirtualFile; 020import com.intellij.psi.PsiElement; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.descriptors.*; 024import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; 025import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl; 026import org.jetbrains.jet.lang.psi.*; 027import org.jetbrains.jet.lang.resolve.BindingContext; 028import org.jetbrains.jet.lang.resolve.BindingTrace; 029import org.jetbrains.jet.lang.resolve.java.JvmAbi; 030import org.jetbrains.jet.lang.resolve.java.JvmClassName; 031import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode; 032import org.jetbrains.jet.lang.resolve.name.FqName; 033import org.jetbrains.jet.lang.resolve.name.Name; 034import org.jetbrains.jet.lang.resolve.scopes.JetScope; 035import org.jetbrains.jet.lang.types.JetType; 036import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 037import org.jetbrains.jet.util.slicedmap.Slices; 038import org.jetbrains.jet.util.slicedmap.WritableSlice; 039 040import java.util.*; 041 042import static org.jetbrains.jet.codegen.CodegenUtil.isInterface; 043import static org.jetbrains.jet.lang.resolve.BindingContext.*; 044import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; 045 046public 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<DeclarationDescriptor, JvmClassName> FQN = Slices.createSimpleSlice(); 054 055 public static final WritableSlice<JvmClassName, Boolean> SCRIPT_NAMES = Slices.createSimpleSetSlice(); 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, ClassDescriptorFromJvmBytecode> SAM_VALUE = Slices.createSimpleSlice(); 062 063 private CodegenBinding() { 064 } 065 066 public static void initTrace(BindingTrace bindingTrace, Collection<JetFile> files) { 067 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(bindingTrace); 068 for (JetFile file : allFilesInNamespaces(bindingTrace.getBindingContext(), files)) { 069 file.accept(visitor); 070 } 071 } 072 073 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) { 074 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry)); 075 } 076 077 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) { 078 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor)); 079 } 080 081 @NotNull 082 public static JvmClassName classNameForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) { 083 ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor); 084 //noinspection ConstantConditions 085 return fqn(bindingContext, classDescriptor); 086 } 087 088 @NotNull 089 public static JvmClassName classNameForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) { 090 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script); 091 if (scriptDescriptor == null) { 092 throw new IllegalStateException("Script descriptor not found by PSI " + script); 093 } 094 return classNameForScriptDescriptor(bindingContext, scriptDescriptor); 095 } 096 097 public static ClassDescriptor enclosingClassDescriptor(BindingContext bindingContext, ClassDescriptor descriptor) { 098 CalculatedClosure closure = bindingContext.get(CLOSURE, descriptor); 099 return closure == null ? null : closure.getEnclosingClass(); 100 } 101 102 @NotNull 103 public static ClassDescriptor anonymousClassForFunction( 104 @NotNull BindingContext bindingContext, 105 @NotNull FunctionDescriptor descriptor 106 ) { 107 //noinspection ConstantConditions 108 return bindingContext.get(CLASS_FOR_FUNCTION, descriptor); 109 } 110 111 @NotNull 112 private static JvmClassName fqn(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor descriptor) { 113 //noinspection ConstantConditions 114 return bindingContext.get(FQN, descriptor); 115 } 116 117 @NotNull 118 public static JvmClassName classNameForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) { 119 if (expression instanceof JetObjectLiteralExpression) { 120 JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression; 121 expression = jetObjectLiteralExpression.getObjectDeclaration(); 122 } 123 124 ClassDescriptor descriptor = bindingContext.get(CLASS, expression); 125 if (descriptor == null) { 126 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression); 127 assert functionDescriptor != null; 128 return classNameForAnonymousClass(bindingContext, functionDescriptor); 129 } 130 131 return fqn(bindingContext, descriptor); 132 } 133 134 @NotNull 135 public static JvmClassName classNameForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) { 136 ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, descriptor); 137 return fqn(bindingContext, classDescriptor); 138 } 139 140 public static void registerClassNameForScript( 141 BindingTrace bindingTrace, 142 @NotNull ScriptDescriptor scriptDescriptor, 143 @NotNull JvmClassName className 144 ) { 145 bindingTrace.record(SCRIPT_NAMES, className); 146 147 ClassDescriptorImpl classDescriptor = new ClassDescriptorImpl( 148 scriptDescriptor, 149 Collections.<AnnotationDescriptor>emptyList(), 150 Modality.FINAL, 151 Name.special("<script-" + className + ">")); 152 classDescriptor.initialize( 153 false, 154 Collections.<TypeParameterDescriptor>emptyList(), 155 Collections.singletonList(KotlinBuiltIns.getInstance().getAnyType()), 156 JetScope.EMPTY, 157 Collections.<ConstructorDescriptor>emptySet(), 158 null, 159 false); 160 161 recordClosure(bindingTrace, null, classDescriptor, null, className, false); 162 163 assert PsiCodegenPredictor.checkPredictedClassNameForFun(bindingTrace.getBindingContext(), scriptDescriptor, classDescriptor); 164 bindingTrace.record(CLASS_FOR_SCRIPT, scriptDescriptor, classDescriptor); 165 } 166 167 public static boolean canHaveOuter(BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) { 168 if (isSingleton(bindingContext, classDescriptor)) { 169 return false; 170 } 171 172 ClassDescriptor enclosing = enclosingClassDescriptor(bindingContext, classDescriptor); 173 if (enclosing == null) { 174 return false; 175 } 176 177 ClassKind kind = classDescriptor.getKind(); 178 if (kind == ClassKind.CLASS) { 179 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor); 180 } 181 else if (kind == ClassKind.OBJECT) { 182 return !isSingleton(bindingContext, enclosing); 183 } 184 else { 185 return false; 186 } 187 } 188 189 public static boolean isSingleton(BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) { 190 ClassKind kind = classDescriptor.getKind(); 191 if (kind == ClassKind.CLASS_OBJECT) { 192 return true; 193 } 194 195 if (kind == ClassKind.OBJECT && isObjectDeclaration(bindingContext, classDescriptor)) { 196 return true; 197 } 198 199 if (kind == ClassKind.ENUM_ENTRY) { 200 return true; 201 } 202 203 return false; 204 } 205 206 static void recordClosure( 207 BindingTrace bindingTrace, 208 @Nullable JetElement element, 209 ClassDescriptor classDescriptor, 210 @Nullable ClassDescriptor enclosing, 211 JvmClassName name, 212 boolean functionLiteral 213 ) { 214 JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element); 215 216 CallableDescriptor enclosingReceiver = null; 217 if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) { 218 enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration(); 219 enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor 220 ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty() 221 : enclosingReceiver; 222 223 if (enclosingReceiver.getReceiverParameter() == null) { 224 enclosingReceiver = null; 225 } 226 } 227 228 MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver); 229 230 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, name); 231 bindingTrace.record(FQN, classDescriptor, name); 232 bindingTrace.record(CLOSURE, classDescriptor, closure); 233 234 // TODO: this is temporary before we have proper inner classes 235 if (canHaveOuter(bindingTrace.getBindingContext(), classDescriptor) && !functionLiteral) { 236 closure.setCaptureThis(); 237 } 238 239 if (enclosing != null) { 240 recordInnerClass(bindingTrace, enclosing, classDescriptor); 241 } 242 } 243 244 private static void recordInnerClass( 245 @NotNull BindingTrace bindingTrace, 246 @NotNull ClassDescriptor outer, 247 @NotNull ClassDescriptor inner 248 ) { 249 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer); 250 if (innerClasses == null) { 251 innerClasses = new ArrayList<ClassDescriptor>(); 252 bindingTrace.record(INNER_CLASSES, outer, innerClasses); 253 } 254 innerClasses.add(inner); 255 } 256 257 public static void registerClassNameForScript(BindingTrace bindingTrace, @NotNull JetScript jetScript, @NotNull JvmClassName className) { 258 ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript); 259 if (descriptor == null) { 260 throw new IllegalStateException("Descriptor is not found for PSI " + jetScript); 261 } 262 registerClassNameForScript(bindingTrace, descriptor, className); 263 } 264 265 @NotNull 266 public static Collection<JetFile> allFilesInNamespaces(BindingContext bindingContext, Collection<JetFile> files) { 267 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding 268 // for scripts especially in case of REPL 269 270 HashSet<FqName> names = new HashSet<FqName>(); 271 for (JetFile file : files) { 272 if (!file.isScript()) { 273 names.add(JetPsiUtil.getFQName(file)); 274 } 275 } 276 277 HashSet<JetFile> answer = new HashSet<JetFile>(); 278 answer.addAll(files); 279 280 for (FqName name : names) { 281 NamespaceDescriptor namespaceDescriptor = bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, name); 282 Collection<JetFile> jetFiles = bindingContext.get(NAMESPACE_TO_FILES, namespaceDescriptor); 283 if (jetFiles != null) 284 answer.addAll(jetFiles); 285 } 286 287 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer); 288 Collections.sort(sortedAnswer, new Comparator<JetFile>() { 289 @NotNull 290 private String path(JetFile file) { 291 VirtualFile virtualFile = file.getVirtualFile(); 292 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName(); 293 return virtualFile.getPath(); 294 } 295 296 @Override 297 public int compare(JetFile first, JetFile second) { 298 return path(first).compareTo(path(second)); 299 } 300 }); 301 302 return sortedAnswer; 303 } 304 305 public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) { 306 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration); 307 if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) { 308 return true; 309 } 310 return false; 311 } 312 313 public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) { 314 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration); 315 if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) { 316 return true; 317 } 318 return false; 319 } 320 321 public static boolean isLocalNamedFun(DeclarationDescriptor fd) { 322 if (fd instanceof FunctionDescriptor) { 323 FunctionDescriptor descriptor = (FunctionDescriptor) fd; 324 return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial(); 325 } 326 return false; 327 } 328 329 @NotNull 330 public static JvmClassName getJvmInternalName(BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) { 331 descriptor = descriptor.getOriginal(); 332 JvmClassName name = bindingTrace.getBindingContext().get(FQN, descriptor); 333 if (name != null) { 334 return name; 335 } 336 337 name = JvmClassName.byInternalName(getJvmInternalFQNameImpl(bindingTrace, descriptor)); 338 339 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, name); 340 bindingTrace.record(FQN, descriptor, name); 341 return name; 342 } 343 344 private static String getJvmInternalFQNameImpl(BindingTrace bindingTrace, DeclarationDescriptor descriptor) { 345 if (descriptor instanceof FunctionDescriptor) { 346 throw new IllegalStateException("requested fq name for function: " + descriptor); 347 } 348 349 if (descriptor.getContainingDeclaration() instanceof ModuleDescriptor || descriptor instanceof ScriptDescriptor) { 350 return ""; 351 } 352 353 if (descriptor instanceof ModuleDescriptor) { 354 throw new IllegalStateException("missed something"); 355 } 356 357 if (descriptor instanceof ClassDescriptor) { 358 ClassDescriptor klass = (ClassDescriptor) descriptor; 359 if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) { 360 if (klass.getContainingDeclaration() instanceof ClassDescriptor) { 361 ClassDescriptor containingKlass = (ClassDescriptor) klass.getContainingDeclaration(); 362 if (containingKlass.getKind() == ClassKind.ENUM_CLASS) { 363 return getJvmInternalName(bindingTrace, containingKlass).getInternalName(); 364 } 365 else { 366 return getJvmInternalName(bindingTrace, containingKlass).getInternalName() + JvmAbi.CLASS_OBJECT_SUFFIX; 367 } 368 } 369 } 370 371 JvmClassName name = bindingTrace.getBindingContext().get(FQN, descriptor); 372 if (name != null) { 373 return name.getInternalName(); 374 } 375 } 376 377 DeclarationDescriptor container = descriptor.getContainingDeclaration(); 378 379 if (container == null) { 380 throw new IllegalStateException("descriptor has no container: " + descriptor); 381 } 382 383 Name name = descriptor.getName(); 384 385 String baseName = getJvmInternalName(bindingTrace, container).getInternalName(); 386 if (!baseName.isEmpty()) { 387 return baseName + (container instanceof NamespaceDescriptor ? "/" : "$") + name.getIdentifier(); 388 } 389 390 return name.getIdentifier(); 391 } 392 393 public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) { 394 if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false; 395 VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor; 396 return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar(); 397 } 398 399 public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) { 400 //noinspection SuspiciousMethodCalls 401 CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor); 402 return closure != null && closure.getCaptureThis() != null; 403 } 404 405 private static JetDelegatorToSuperCall findSuperCall( 406 BindingContext bindingContext, 407 JetElement classOrObject 408 ) { 409 if (!(classOrObject instanceof JetClassOrObject)) { 410 return null; 411 } 412 413 if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) { 414 return null; 415 } 416 for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) { 417 if (specifier instanceof JetDelegatorToSuperCall) { 418 JetType superType = bindingContext.get(TYPE, specifier.getTypeReference()); 419 assert superType != null; 420 ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor(); 421 assert superClassDescriptor != null; 422 if (!isInterface(superClassDescriptor)) { 423 return (JetDelegatorToSuperCall) specifier; 424 } 425 } 426 } 427 428 return null; 429 } 430}