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