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 }