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.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.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 import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
045 import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
046 import static org.jetbrains.jet.lang.resolve.source.SourcePackage.toSourceElement;
047
048 public class CodegenBinding {
049 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
050
051 public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice();
052
053 public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice();
054
055 public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
056
057 public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
058
059 public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
060
061 public static final WritableSlice<JetExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
062
063 public static final WritableSlice<JetWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.
064 <JetWhenExpression, WhenByEnumsMapping>sliceBuilder().build();
065
066 public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
067 Slices.<String, List<WhenByEnumsMapping>>sliceBuilder().build();
068
069 static {
070 BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
071 }
072
073 private CodegenBinding() {
074 }
075
076 public static void initTrace(@NotNull GenerationState state) {
077 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
078 for (JetFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
079 file.accept(visitor);
080 }
081 }
082
083 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) {
084 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
085 }
086
087 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
088 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
089 }
090
091 // SCRIPT: Generate asmType for script, move to ScriptingUtil
092 @NotNull
093 public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) {
094 ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor);
095 //noinspection ConstantConditions
096 return getAsmType(bindingContext, classDescriptor);
097 }
098
099 // SCRIPT: Generate asmType for script, move to ScriptingUtil
100 @NotNull
101 public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) {
102 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script);
103 if (scriptDescriptor == null) {
104 throw new IllegalStateException("Script descriptor not found by PSI " + script);
105 }
106 return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor);
107 }
108
109 @NotNull
110 public static ClassDescriptor anonymousClassForFunction(
111 @NotNull BindingContext bindingContext,
112 @NotNull FunctionDescriptor descriptor
113 ) {
114 //noinspection ConstantConditions
115 return bindingContext.get(CLASS_FOR_FUNCTION, 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 getAsmType(bindingContext, descriptor);
133 }
134
135 @NotNull
136 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
137 return getAsmType(bindingContext, anonymousClassForFunction(bindingContext, descriptor));
138 }
139
140 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
141 if (classDescriptor.getKind() != ClassKind.CLASS) {
142 return false;
143 }
144
145 MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
146 if (closure == null || closure.getEnclosingClass() == null) {
147 return false;
148 }
149
150 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
151 }
152
153 static void recordClosure(
154 @NotNull BindingTrace trace,
155 @NotNull ClassDescriptor classDescriptor,
156 @Nullable ClassDescriptor enclosing,
157 @NotNull Type asmType
158 ) {
159 JetElement element = (JetElement) descriptorToDeclaration(classDescriptor);
160 assert element != null : "No source element for " + classDescriptor;
161
162 MutableClosure closure = new MutableClosure(classDescriptor, findSuperCall(trace.getBindingContext(), element), enclosing);
163
164 if (classDescriptor.isInner()) {
165 closure.setCaptureThis();
166 }
167
168 assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType);
169 trace.record(ASM_TYPE, classDescriptor, asmType);
170 trace.record(CLOSURE, classDescriptor, closure);
171
172 //TEMPORARY EAT INNER CLASS INFO FOR FUNCTION LITERALS
173 //TODO: we should understand that lambda/closure would be inlined and don't generate inner class record
174 if (enclosing != null && !(element instanceof JetFunctionLiteral)) {
175 recordInnerClass(trace, enclosing, classDescriptor);
176 }
177 }
178
179 private static void recordInnerClass(
180 @NotNull BindingTrace bindingTrace,
181 @NotNull ClassDescriptor outer,
182 @NotNull ClassDescriptor inner
183 ) {
184 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
185 if (innerClasses == null) {
186 innerClasses = new ArrayList<ClassDescriptor>();
187 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
188 }
189 innerClasses.add(inner);
190 }
191
192 // SCRIPT: register asmType for script, move to ScriptingUtil
193 public static void registerClassNameForScript(@NotNull BindingTrace trace, @NotNull JetScript script, @NotNull Type asmType) {
194 ScriptDescriptor descriptor = trace.getBindingContext().get(SCRIPT, script);
195 if (descriptor == null) {
196 throw new IllegalStateException("Script descriptor is not found for PSI: " + JetPsiUtil.getElementTextWithContext(script));
197 }
198
199 String simpleName = asmType.getInternalName().substring(asmType.getInternalName().lastIndexOf('/') + 1);
200 ClassDescriptorImpl classDescriptor =
201 new ClassDescriptorImpl(descriptor, Name.special("<script-" + simpleName + ">"), Modality.FINAL,
202 Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()), toSourceElement(script));
203 classDescriptor.initialize(JetScope.Empty.INSTANCE$, Collections.<ConstructorDescriptor>emptySet(), null);
204
205 recordClosure(trace, classDescriptor, null, asmType);
206
207 trace.record(CLASS_FOR_SCRIPT, descriptor, classDescriptor);
208 }
209
210 @NotNull
211 private static Collection<JetFile> allFilesInPackages(BindingContext bindingContext, Collection<JetFile> files) {
212 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
213 // for scripts especially in case of REPL
214
215 // SCRIPT: collect fq names for files that are not scripts
216 HashSet<FqName> names = new HashSet<FqName>();
217 for (JetFile file : files) {
218 if (!file.isScript()) {
219 names.add(file.getPackageFqName());
220 }
221 }
222
223 HashSet<JetFile> answer = new HashSet<JetFile>();
224 answer.addAll(files);
225
226 for (FqName name : names) {
227 Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
228 if (jetFiles != null) {
229 answer.addAll(jetFiles);
230 }
231 }
232
233 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
234 Collections.sort(sortedAnswer, new Comparator<JetFile>() {
235 @NotNull
236 private String path(JetFile file) {
237 VirtualFile virtualFile = file.getVirtualFile();
238 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
239 return virtualFile.getPath();
240 }
241
242 @Override
243 public int compare(@NotNull JetFile first, @NotNull JetFile second) {
244 return path(first).compareTo(path(second));
245 }
246 });
247
248 return sortedAnswer;
249 }
250
251 public static boolean isLocalNamedFun(@Nullable DeclarationDescriptor fd) {
252 return isLocalFunOrLambda(fd) && !fd.getName().isSpecial();
253 }
254
255 /*named or not*/
256 public static boolean isLocalFunOrLambda(@Nullable DeclarationDescriptor fd) {
257 if (fd instanceof FunctionDescriptor) {
258 FunctionDescriptor descriptor = (FunctionDescriptor) fd;
259 return descriptor.getVisibility() == Visibilities.LOCAL;
260 }
261 return false;
262 }
263
264 @NotNull
265 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
266 Type type = bindingContext.get(ASM_TYPE, klass);
267 assert type != null : "Type is not yet recorded for " + klass;
268 return type;
269 }
270
271 @Nullable
272 private static ResolvedCall<ConstructorDescriptor> findSuperCall(
273 @NotNull BindingContext bindingContext,
274 @NotNull JetElement classOrObject
275 ) {
276 if (!(classOrObject instanceof JetClassOrObject)) return null;
277
278 if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) return null;
279
280 for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
281 if (!(specifier instanceof JetDelegatorToSuperCall)) continue;
282
283 ResolvedCall<?> resolvedCall = getResolvedCall(specifier, bindingContext);
284 if (resolvedCall == null) continue;
285
286 CallableDescriptor constructor = resolvedCall.getResultingDescriptor();
287 if (constructor instanceof ConstructorDescriptor && !isInterface(constructor.getContainingDeclaration())) {
288 //noinspection unchecked
289 return (ResolvedCall<ConstructorDescriptor>) resolvedCall;
290 }
291 }
292
293 return null;
294 }
295
296 @NotNull
297 public static Collection<ClassDescriptor> getAllInnerClasses(
298 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
299 ) {
300 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
301 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
302
303 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
304
305 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
306 do {
307 ClassDescriptor currentClass = stack.pop();
308 if (allInnerClasses.add(currentClass)) {
309 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
310 if (nextClasses != null) {
311 for (ClassDescriptor nextClass : nextClasses) {
312 stack.push(nextClass);
313 }
314 }
315 }
316 } while (!stack.isEmpty());
317
318 return allInnerClasses;
319 }
320 }