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