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