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