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.lang.resolve.lazy;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import kotlin.Function0;
022    import kotlin.Function1;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.annotations.TestOnly;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.psi.JetFile;
028    import org.jetbrains.jet.lang.psi.JetImportDirective;
029    import org.jetbrains.jet.lang.resolve.BindingTrace;
030    import org.jetbrains.jet.lang.resolve.Importer;
031    import org.jetbrains.jet.lang.resolve.ImportsResolver;
032    import org.jetbrains.jet.lang.resolve.JetModuleUtil;
033    import org.jetbrains.jet.lang.resolve.name.LabelName;
034    import org.jetbrains.jet.lang.resolve.name.Name;
035    import org.jetbrains.jet.lang.resolve.scopes.*;
036    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
037    import org.jetbrains.jet.utils.Printer;
038    
039    import java.util.Collection;
040    import java.util.Collections;
041    import java.util.List;
042    import java.util.Set;
043    
044    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
045    
046    public class LazyImportScope implements JetScope, LazyEntity {
047        private final ResolveSession resolveSession;
048        private final PackageViewDescriptor packageDescriptor;
049        private final ImportsProvider importsProvider;
050        private final JetScope rootScope;
051        private final BindingTrace traceForImportResolve;
052        private final String debugName;
053    
054        private static class ImportResolveStatus {
055            private final LookupMode lookupMode;
056            private final JetScope scope;
057            private final Collection<? extends DeclarationDescriptor> descriptors;
058    
059            ImportResolveStatus(LookupMode lookupMode, JetScope scope, Collection<? extends DeclarationDescriptor> descriptors) {
060                this.lookupMode = lookupMode;
061                this.scope = scope;
062                this.descriptors = descriptors;
063            }
064        }
065    
066        private class ImportDirectiveResolveCache {
067            private final JetImportDirective directive;
068    
069            @Nullable
070            private volatile ImportResolveStatus importResolveStatus;
071    
072            private ImportDirectiveResolveCache(JetImportDirective directive) {
073                this.directive = directive;
074            }
075    
076            private JetScope scopeForMode(final LookupMode mode) {
077                ImportResolveStatus status = importResolveStatus;
078                if (status != null && (status.lookupMode == mode || status.lookupMode == LookupMode.EVERYTHING)) {
079                    return status.scope;
080                }
081    
082                return resolveSession.getStorageManager().compute(new Function0<JetScope>() {
083                    @Override
084                    public JetScope invoke() {
085                        ImportResolveStatus cachedStatus = importResolveStatus;
086                        if (cachedStatus != null && (cachedStatus.lookupMode == mode || cachedStatus.lookupMode == LookupMode.EVERYTHING)) {
087                            return cachedStatus.scope;
088                        }
089    
090                        WritableScope directiveImportScope = new WritableScopeImpl(
091                                JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
092                                "Scope for import '" + directive.getText() + "' resolve in " + toString());
093                        directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
094    
095                        Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
096                        directiveUnderResolve = directive;
097    
098                        Collection<? extends DeclarationDescriptor> descriptors;
099                        try {
100                            descriptors = resolveSession.getQualifiedExpressionResolver().processImportReference(
101                                    directive,
102                                    rootScope,
103                                    packageDescriptor.getMemberScope(),
104                                    importer,
105                                    traceForImportResolve,
106                                    resolveSession.getModuleDescriptor(),
107                                    mode);
108                            if (mode == LookupMode.EVERYTHING) {
109                                ImportsResolver.checkPlatformTypesMappedToKotlin(
110                                        packageDescriptor.getModule(),
111                                        traceForImportResolve,
112                                        directive,
113                                        descriptors
114                                );
115                            }
116                        }
117                        finally {
118                            directiveUnderResolve = null;
119                            directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
120                        }
121    
122                        importResolveStatus = new ImportResolveStatus(mode, directiveImportScope, descriptors);
123                        return directiveImportScope;
124                    }
125                });
126            }
127        }
128    
129        private final MemoizedFunctionToNotNull<JetImportDirective, ImportDirectiveResolveCache> importedScopesProvider;
130    
131        private JetImportDirective directiveUnderResolve = null;
132    
133        public LazyImportScope(
134                @NotNull ResolveSession resolveSession,
135                @NotNull PackageViewDescriptor packageDescriptor,
136                @NotNull List<JetImportDirective> imports,
137                @NotNull BindingTrace traceForImportResolve,
138                @NotNull String debugName,
139                boolean inRootPackage
140        ) {
141            this.resolveSession = resolveSession;
142            this.packageDescriptor = packageDescriptor;
143            this.importsProvider = new ImportsProvider(resolveSession.getStorageManager(), imports);
144            this.traceForImportResolve = traceForImportResolve;
145            this.debugName = debugName;
146    
147            this.importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetImportDirective, ImportDirectiveResolveCache>() {
148                @Override
149                public ImportDirectiveResolveCache invoke(JetImportDirective directive) {
150                    return new ImportDirectiveResolveCache(directive);
151                }
152            });
153    
154            this.rootScope = JetModuleUtil.getImportsResolutionScope(resolveSession.getModuleDescriptor(), inRootPackage);
155        }
156    
157        public static LazyImportScope createImportScopeForFile(
158                @NotNull ResolveSession resolveSession,
159                @NotNull PackageViewDescriptor packageDescriptor,
160                @NotNull JetFile jetFile,
161                @NotNull BindingTrace traceForImportResolve,
162                @NotNull String debugName
163        ) {
164            return new LazyImportScope(
165                    resolveSession,
166                    packageDescriptor,
167                    Lists.reverse(jetFile.getImportDirectives()),
168                    traceForImportResolve,
169                    debugName,
170                    packageDescriptor.getFqName().isRoot());
171        }
172    
173        @Override
174        public void forceResolveAllContents() {
175            for (JetImportDirective importDirective : importsProvider.getAllImports()) {
176                getImportScope(importDirective, LookupMode.EVERYTHING);
177    
178                ImportResolveStatus status = importedScopesProvider.invoke(importDirective).importResolveStatus;
179                if (status != null && !status.descriptors.isEmpty()) {
180                    ImportsResolver.reportUselessImport(importDirective, this, status.descriptors, traceForImportResolve);
181                }
182            }
183        }
184    
185        @Nullable
186        private <D extends DeclarationDescriptor> D selectFirstFromImports(
187                final Name name,
188                final LookupMode lookupMode,
189                final JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
190        ) {
191            return resolveSession.getStorageManager().compute(new Function0<D>() {
192                @Override
193                public D invoke() {
194                    for (JetImportDirective directive : importsProvider.getImports(name)) {
195                        if (directive == directiveUnderResolve) {
196                            // This is the recursion in imports analysis
197                            return null;
198                        }
199    
200                        D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
201                        if (foundDescriptor != null) {
202                            return foundDescriptor;
203                        }
204                    }
205    
206                    return null;
207                }
208            });
209        }
210    
211        @NotNull
212        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
213                final Name name,
214                final LookupMode lookupMode,
215                final JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
216        ) {
217            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
218                @Override
219                public Collection<D> invoke() {
220                    Set<D> descriptors = Sets.newHashSet();
221                    for (JetImportDirective directive : importsProvider.getImports(name)) {
222                        if (directive == directiveUnderResolve) {
223                            // This is the recursion in imports analysis
224                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
225                        }
226    
227                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
228                    }
229    
230                    return descriptors;
231                }
232            });
233        }
234    
235        @NotNull
236        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
237                final LookupMode lookupMode,
238                final JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
239        ) {
240            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
241                @Override
242                public Collection<D> invoke() {
243                    Set<D> descriptors = Sets.newHashSet();
244                    for (JetImportDirective directive : importsProvider.getAllImports()) {
245                        if (directive == directiveUnderResolve) {
246                            // This is the recursion in imports analysis
247                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
248                        }
249    
250                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
251                    }
252    
253                    return descriptors;
254                }
255            });
256        }
257    
258        @NotNull
259        private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) {
260            return importedScopesProvider.invoke(directive).scopeForMode(lookupMode);
261        }
262    
263        @Nullable
264        @Override
265        public ClassifierDescriptor getClassifier(@NotNull Name name) {
266            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR);
267        }
268    
269        @Nullable
270        @Override
271        public PackageViewDescriptor getPackage(@NotNull Name name) {
272            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.PACKAGE_SCOPE_SELECTOR);
273        }
274    
275        @NotNull
276        @Override
277        public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
278            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR);
279        }
280    
281        @Nullable
282        @Override
283        public VariableDescriptor getLocalVariable(@NotNull Name name) {
284            return null;
285        }
286    
287        @NotNull
288        @Override
289        public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
290            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR);
291        }
292    
293        @NotNull
294        @Override
295        public DeclarationDescriptor getContainingDeclaration() {
296            return packageDescriptor;
297        }
298    
299        @NotNull
300        @Override
301        public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
302            return Collections.emptyList();
303        }
304    
305        @NotNull
306        @Override
307        public Collection<DeclarationDescriptor> getAllDescriptors() {
308            return collectFromImports(LookupMode.EVERYTHING, JetScopeSelectorUtil.ALL_DESCRIPTORS_SCOPE_SELECTOR);
309        }
310    
311        @NotNull
312        @Override
313        public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
314            return Collections.emptyList();
315        }
316    
317        @NotNull
318        @Override
319        public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
320            return Collections.emptyList();
321        }
322    
323        @Override
324        public String toString() {
325            return "LazyImportScope: " + debugName;
326        }
327    
328        @TestOnly
329        @Override
330        public void printScopeStructure(@NotNull Printer p) {
331            p.println(getClass().getSimpleName(), ": ", debugName, " {");
332            p.pushIndent();
333    
334            p.println("packageDescriptor = ", packageDescriptor);
335    
336            p.print("rootScope = ");
337            rootScope.printScopeStructure(p.withholdIndentOnce());
338    
339            p.popIndent();
340            p.println("}");
341        }
342    }