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