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