• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.binder.lookup;
18 
19 import com.google.common.base.Supplier;
20 import com.google.common.base.Suppliers;
21 import com.google.common.collect.ImmutableList;
22 import com.google.turbine.binder.sym.ClassSymbol;
23 import com.google.turbine.tree.Tree;
24 import com.google.turbine.tree.Tree.ImportDecl;
25 
26 /**
27  * A scope that provides best-effort lookup for on-demand imported types in a compilation unit.
28  *
29  * <p>Resolution is lazy, imports are not evaluated until the first request for a matching simple
30  * name.
31  *
32  * <p>Static on-demand imports of types are not supported.
33  */
34 public class WildImportIndex implements ImportScope {
35 
36   /** {@link ImportScope}s for all on-demand imports in the compilation unit. */
37   private final ImmutableList<Supplier<ImportScope>> packages;
38 
WildImportIndex(ImmutableList<Supplier<ImportScope>> packages)39   public WildImportIndex(ImmutableList<Supplier<ImportScope>> packages) {
40     this.packages = packages;
41   }
42 
43   /** Creates an import index for the given top-level environment. */
create( CanonicalSymbolResolver importResolver, final TopLevelIndex cpi, ImmutableList<ImportDecl> imports)44   public static WildImportIndex create(
45       CanonicalSymbolResolver importResolver,
46       final TopLevelIndex cpi,
47       ImmutableList<ImportDecl> imports) {
48     ImmutableList.Builder<Supplier<ImportScope>> packageScopes = ImmutableList.builder();
49     for (final ImportDecl i : imports) {
50       if (i.wild()) {
51         packageScopes.add(
52             Suppliers.memoize(
53                 new Supplier<ImportScope>() {
54                   @Override
55                   public ImportScope get() {
56                     if (i.stat()) {
57                       return staticOnDemandImport(cpi, i, importResolver);
58                     } else {
59                       return onDemandImport(cpi, i, importResolver);
60                     }
61                   }
62                 }));
63       }
64     }
65     return new WildImportIndex(packageScopes.build());
66   }
67 
68   /** Full resolve the type for a non-static on-demand import. */
onDemandImport( TopLevelIndex cpi, ImportDecl i, final CanonicalSymbolResolver importResolver)69   private static ImportScope onDemandImport(
70       TopLevelIndex cpi, ImportDecl i, final CanonicalSymbolResolver importResolver) {
71     ImmutableList.Builder<String> flatNames = ImmutableList.builder();
72     for (Tree.Ident ident : i.type()) {
73       flatNames.add(ident.value());
74     }
75     Scope packageIndex = cpi.lookupPackage(flatNames.build());
76     if (packageIndex != null) {
77       // a wildcard import of a package
78       return new ImportScope() {
79         @Override
80         public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
81           return packageIndex.lookup(lookupKey);
82         }
83       };
84     }
85     LookupResult result = cpi.scope().lookup(new LookupKey(i.type()));
86     if (result == null) {
87       return null;
88     }
89     ClassSymbol member = resolveImportBase(result, importResolver, importResolver);
90     if (member == null) {
91       return null;
92     }
93     return new ImportScope() {
94       @Override
95       public LookupResult lookup(LookupKey lookupKey, ResolveFunction unused) {
96         return resolveMember(member, importResolver, importResolver, lookupKey);
97       }
98     };
99   }
100 
101   /**
102    * Resolve the base class symbol of a possibly non-canonical static on-demand import (see {@code
103    * ImportScope#staticNamedImport} for an explanation of why the possibly non-canonical part is
104    * deferred).
105    */
106   private static ImportScope staticOnDemandImport(
107       TopLevelIndex cpi, ImportDecl i, final CanonicalSymbolResolver importResolver) {
108     LookupResult result = cpi.scope().lookup(new LookupKey(i.type()));
109     if (result == null) {
110       return null;
111     }
112     return new ImportScope() {
113       @Override
114       public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) {
115         ClassSymbol member = resolveImportBase(result, resolve, importResolver);
116         if (member == null) {
117           return null;
118         }
119         return resolveMember(member, resolve, importResolver, lookupKey);
120       }
121     };
122   }
123 
124   private static LookupResult resolveMember(
125       ClassSymbol base,
126       ResolveFunction resolve,
127       CanonicalSymbolResolver importResolver,
128       LookupKey lookupKey) {
129     ClassSymbol member = resolve.resolveOne(base, lookupKey.first());
130     if (member == null) {
131       return null;
132     }
133     if (!importResolver.visible(member)) {
134       return null;
135     }
136     return new LookupResult(member, lookupKey);
137   }
138 
139   static ClassSymbol resolveImportBase(
140       LookupResult result, ResolveFunction resolve, CanonicalSymbolResolver importResolver) {
141     ClassSymbol member = (ClassSymbol) result.sym();
142     for (Tree.Ident bit : result.remaining()) {
143       member = resolve.resolveOne(member, bit);
144       if (member == null) {
145         return null;
146       }
147       if (!importResolver.visible(member)) {
148         return null;
149       }
150     }
151     return member;
152   }
153 
154   @Override
155   public LookupResult lookup(LookupKey lookup, ResolveFunction resolve) {
156     for (Supplier<ImportScope> packageScope : packages) {
157       ImportScope scope = packageScope.get();
158       if (scope == null) {
159         continue;
160       }
161       LookupResult result = scope.lookup(lookup, resolve);
162       if (result != null) {
163         return result;
164       }
165     }
166     return null;
167   }
168 }
169