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; 18 19 import static com.google.common.base.Verify.verifyNotNull; 20 21 import com.google.common.base.Optional; 22 import com.google.common.base.Splitter; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.ImmutableMap; 25 import com.google.common.collect.ImmutableSet; 26 import com.google.turbine.binder.CompUnitPreprocessor.PreprocessedCompUnit; 27 import com.google.turbine.binder.Resolve.CanonicalResolver; 28 import com.google.turbine.binder.bound.BoundClass; 29 import com.google.turbine.binder.bound.HeaderBoundClass; 30 import com.google.turbine.binder.bound.PackageSourceBoundClass; 31 import com.google.turbine.binder.bound.SourceBoundClass; 32 import com.google.turbine.binder.bound.SourceHeaderBoundClass; 33 import com.google.turbine.binder.bound.SourceTypeBoundClass; 34 import com.google.turbine.binder.bound.TypeBoundClass; 35 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; 36 import com.google.turbine.binder.bytecode.BytecodeBoundClass; 37 import com.google.turbine.binder.env.CompoundEnv; 38 import com.google.turbine.binder.env.Env; 39 import com.google.turbine.binder.env.LazyEnv; 40 import com.google.turbine.binder.env.SimpleEnv; 41 import com.google.turbine.binder.lookup.CanonicalSymbolResolver; 42 import com.google.turbine.binder.lookup.CompoundScope; 43 import com.google.turbine.binder.lookup.ImportIndex; 44 import com.google.turbine.binder.lookup.ImportScope; 45 import com.google.turbine.binder.lookup.MemberImportIndex; 46 import com.google.turbine.binder.lookup.Scope; 47 import com.google.turbine.binder.lookup.TopLevelIndex; 48 import com.google.turbine.binder.lookup.WildImportIndex; 49 import com.google.turbine.binder.sym.ClassSymbol; 50 import com.google.turbine.binder.sym.FieldSymbol; 51 import com.google.turbine.model.Const; 52 import com.google.turbine.model.TurbineFlag; 53 import com.google.turbine.tree.Tree; 54 import com.google.turbine.tree.Tree.CompUnit; 55 import com.google.turbine.type.Type; 56 import java.io.IOException; 57 import java.nio.file.Path; 58 import java.util.Collection; 59 import java.util.List; 60 61 /** The entry point for analysis. */ 62 public class Binder { 63 64 /** Binds symbols and types to the given compilation units. */ 65 public static BindingResult bind( 66 List<CompUnit> units, Collection<Path> classpath, Collection<Path> bootclasspath) 67 throws IOException { 68 69 ImmutableList<PreprocessedCompUnit> preProcessedUnits = CompUnitPreprocessor.preprocess(units); 70 71 TopLevelIndex.Builder tliBuilder = TopLevelIndex.builder(); 72 73 SimpleEnv<ClassSymbol, SourceBoundClass> ienv = 74 bindSourceBoundClasses(preProcessedUnits, tliBuilder); 75 76 ImmutableSet<ClassSymbol> syms = ienv.asMap().keySet(); 77 78 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv = 79 ClassPathBinder.bind(classpath, bootclasspath, tliBuilder); 80 81 // Insertion order into the top-level index is important: 82 // * the first insert into the TLI wins 83 // * we search sources, bootclasspath, and classpath in that order 84 // * the first entry within a location wins. 85 86 TopLevelIndex tli = tliBuilder.build(); 87 88 SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv = 89 bindPackages(ienv, tli, preProcessedUnits, classPathEnv); 90 91 Env<ClassSymbol, SourceHeaderBoundClass> henv = bindHierarchy(syms, psenv, classPathEnv); 92 93 Env<ClassSymbol, SourceTypeBoundClass> tenv = 94 bindTypes( 95 syms, henv, CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv)); 96 97 tenv = 98 constants( 99 syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); 100 tenv = 101 disambiguateTypeAnnotations( 102 syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); 103 tenv = 104 canonicalizeTypes( 105 syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); 106 107 ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder(); 108 for (ClassSymbol sym : syms) { 109 result.put(sym, tenv.get(sym)); 110 } 111 return new BindingResult(result.build(), classPathEnv); 112 } 113 114 /** Records enclosing declarations of member classes, and group classes by compilation unit. */ 115 static SimpleEnv<ClassSymbol, SourceBoundClass> bindSourceBoundClasses( 116 ImmutableList<PreprocessedCompUnit> units, TopLevelIndex.Builder tliBuilder) { 117 SimpleEnv.Builder<ClassSymbol, SourceBoundClass> envBuilder = SimpleEnv.builder(); 118 for (PreprocessedCompUnit unit : units) { 119 for (SourceBoundClass type : unit.types()) { 120 envBuilder.put(type.sym(), type); 121 tliBuilder.insert(type.sym()); 122 } 123 } 124 return envBuilder.build(); 125 } 126 127 /** Initializes scopes for compilation unit and package-level lookup. */ 128 private static SimpleEnv<ClassSymbol, PackageSourceBoundClass> bindPackages( 129 Env<ClassSymbol, SourceBoundClass> ienv, 130 TopLevelIndex tli, 131 ImmutableList<PreprocessedCompUnit> units, 132 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { 133 134 SimpleEnv.Builder<ClassSymbol, PackageSourceBoundClass> env = SimpleEnv.builder(); 135 Scope javaLang = verifyNotNull(tli.lookupPackage(ImmutableList.of("java", "lang"))); 136 CompoundScope topLevel = CompoundScope.base(tli).append(javaLang); 137 for (PreprocessedCompUnit unit : units) { 138 ImmutableList<String> packagename = 139 ImmutableList.copyOf(Splitter.on('/').omitEmptyStrings().split(unit.packageName())); 140 Scope packageScope = tli.lookupPackage(packagename); 141 CanonicalSymbolResolver importResolver = 142 new CanonicalResolver( 143 packagename, CompoundEnv.<ClassSymbol, BoundClass>of(classPathEnv).append(ienv)); 144 ImportScope importScope = 145 ImportIndex.create(unit.source(), importResolver, tli, unit.imports()); 146 ImportScope wildImportScope = WildImportIndex.create(importResolver, tli, unit.imports()); 147 MemberImportIndex memberImports = 148 new MemberImportIndex(unit.source(), importResolver, tli, unit.imports()); 149 ImportScope scope = 150 ImportScope.fromScope(topLevel) 151 .append(wildImportScope) 152 .append(ImportScope.fromScope(packageScope)) 153 .append(importScope); 154 for (SourceBoundClass type : unit.types()) { 155 env.put(type.sym(), new PackageSourceBoundClass(type, scope, memberImports, unit.source())); 156 } 157 } 158 return env.build(); 159 } 160 161 /** Binds the type hierarchy (superclasses and interfaces) for all classes in the compilation. */ 162 private static Env<ClassSymbol, SourceHeaderBoundClass> bindHierarchy( 163 Iterable<ClassSymbol> syms, 164 final SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv, 165 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { 166 ImmutableMap.Builder< 167 ClassSymbol, LazyEnv.Completer<ClassSymbol, HeaderBoundClass, SourceHeaderBoundClass>> 168 completers = ImmutableMap.builder(); 169 for (ClassSymbol sym : syms) { 170 completers.put( 171 sym, 172 new LazyEnv.Completer<ClassSymbol, HeaderBoundClass, SourceHeaderBoundClass>() { 173 @Override 174 public SourceHeaderBoundClass complete( 175 Env<ClassSymbol, HeaderBoundClass> henv, ClassSymbol sym) { 176 return HierarchyBinder.bind(sym, psenv.get(sym), henv); 177 } 178 }); 179 } 180 return new LazyEnv<>(completers.build(), classPathEnv); 181 } 182 183 private static Env<ClassSymbol, SourceTypeBoundClass> bindTypes( 184 ImmutableSet<ClassSymbol> syms, 185 Env<ClassSymbol, SourceHeaderBoundClass> shenv, 186 Env<ClassSymbol, HeaderBoundClass> henv) { 187 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 188 for (ClassSymbol sym : syms) { 189 builder.put(sym, TypeBinder.bind(henv, sym, shenv.get(sym))); 190 } 191 return builder.build(); 192 } 193 194 private static Env<ClassSymbol, SourceTypeBoundClass> canonicalizeTypes( 195 ImmutableSet<ClassSymbol> syms, 196 Env<ClassSymbol, SourceTypeBoundClass> stenv, 197 Env<ClassSymbol, TypeBoundClass> tenv) { 198 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 199 for (ClassSymbol sym : syms) { 200 builder.put(sym, CanonicalTypeBinder.bind(sym, stenv.get(sym), tenv)); 201 } 202 return builder.build(); 203 } 204 205 private static Env<ClassSymbol, SourceTypeBoundClass> constants( 206 ImmutableSet<ClassSymbol> syms, 207 Env<ClassSymbol, SourceTypeBoundClass> env, 208 CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv) { 209 210 // Prepare to lazily evaluate constant fields in each compilation unit. 211 // The laziness is necessary since constant fields can reference other 212 // constant fields. 213 ImmutableMap.Builder<FieldSymbol, LazyEnv.Completer<FieldSymbol, Const.Value, Const.Value>> 214 completers = ImmutableMap.builder(); 215 for (ClassSymbol sym : syms) { 216 SourceTypeBoundClass info = env.get(sym); 217 for (FieldInfo field : info.fields()) { 218 if (!isConst(field)) { 219 continue; 220 } 221 completers.put( 222 field.sym(), 223 new LazyEnv.Completer<FieldSymbol, Const.Value, Const.Value>() { 224 @Override 225 public Const.Value complete(Env<FieldSymbol, Const.Value> env1, FieldSymbol k) { 226 try { 227 return new ConstEvaluator(sym, sym, info, info.scope(), env1, baseEnv) 228 .evalFieldInitializer(field.decl().init().get(), field.type()); 229 } catch (LazyEnv.LazyBindingError e) { 230 // fields initializers are allowed to reference the field being initialized, 231 // but if they do they aren't constants 232 return null; 233 } 234 } 235 }); 236 } 237 } 238 239 // Create an environment of constant field values that combines 240 // lazily evaluated fields in the current compilation unit with 241 // constant fields in the classpath (which don't require evaluation). 242 Env<FieldSymbol, Const.Value> constenv = 243 new LazyEnv<>(completers.build(), SimpleEnv.<FieldSymbol, Const.Value>builder().build()); 244 245 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 246 for (ClassSymbol sym : syms) { 247 builder.put(sym, new ConstBinder(constenv, sym, baseEnv, env.get(sym)).bind()); 248 } 249 return builder.build(); 250 } 251 252 static boolean isConst(FieldInfo field) { 253 if ((field.access() & TurbineFlag.ACC_FINAL) == 0) { 254 return false; 255 } 256 if (field.decl() == null) { 257 return false; 258 } 259 final Optional<Tree.Expression> init = field.decl().init(); 260 if (!init.isPresent()) { 261 return false; 262 } 263 switch (field.type().tyKind()) { 264 case PRIM_TY: 265 break; 266 case CLASS_TY: 267 if (((Type.ClassTy) field.type()).sym().equals(ClassSymbol.STRING)) { 268 break; 269 } 270 // fall through 271 default: 272 return false; 273 } 274 return true; 275 } 276 277 /** 278 * Disambiguate annotations on field types and method return types that could be declaration or 279 * type annotations. 280 */ 281 private static Env<ClassSymbol, SourceTypeBoundClass> disambiguateTypeAnnotations( 282 ImmutableSet<ClassSymbol> syms, 283 Env<ClassSymbol, SourceTypeBoundClass> stenv, 284 Env<ClassSymbol, TypeBoundClass> tenv) { 285 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 286 for (ClassSymbol sym : syms) { 287 builder.put(sym, DisambiguateTypeAnnotations.bind(stenv.get(sym), tenv)); 288 } 289 return builder.build(); 290 } 291 292 /** The result of binding: bound nodes for sources in the compilation, and the classpath. */ 293 public static class BindingResult { 294 private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units; 295 private final CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv; 296 297 public BindingResult( 298 ImmutableMap<ClassSymbol, SourceTypeBoundClass> units, 299 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { 300 this.units = units; 301 this.classPathEnv = classPathEnv; 302 } 303 304 /** Bound nodes for sources in the compilation. */ 305 public ImmutableMap<ClassSymbol, SourceTypeBoundClass> units() { 306 return units; 307 } 308 309 /** The classpath. */ 310 public CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv() { 311 return classPathEnv; 312 } 313 } 314 } 315