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