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 }