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 }