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