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 com.google.common.base.Splitter; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableMap; 22 import com.google.common.collect.ImmutableSet; 23 import com.google.turbine.binder.CompUnitPreprocessor.PreprocessedCompUnit; 24 import com.google.turbine.binder.Resolve.CanonicalResolver; 25 import com.google.turbine.binder.bound.BoundClass; 26 import com.google.turbine.binder.bound.HeaderBoundClass; 27 import com.google.turbine.binder.bound.ModuleInfo; 28 import com.google.turbine.binder.bound.PackageSourceBoundClass; 29 import com.google.turbine.binder.bound.PackageSourceBoundModule; 30 import com.google.turbine.binder.bound.SourceBoundClass; 31 import com.google.turbine.binder.bound.SourceHeaderBoundClass; 32 import com.google.turbine.binder.bound.SourceModuleInfo; 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.CompoundTopLevelIndex; 44 import com.google.turbine.binder.lookup.ImportIndex; 45 import com.google.turbine.binder.lookup.ImportScope; 46 import com.google.turbine.binder.lookup.MemberImportIndex; 47 import com.google.turbine.binder.lookup.Scope; 48 import com.google.turbine.binder.lookup.SimpleTopLevelIndex; 49 import com.google.turbine.binder.lookup.TopLevelIndex; 50 import com.google.turbine.binder.lookup.WildImportIndex; 51 import com.google.turbine.binder.sym.ClassSymbol; 52 import com.google.turbine.binder.sym.FieldSymbol; 53 import com.google.turbine.binder.sym.ModuleSymbol; 54 import com.google.turbine.diag.TurbineError; 55 import com.google.turbine.diag.TurbineError.ErrorKind; 56 import com.google.turbine.diag.TurbineLog; 57 import com.google.turbine.model.Const; 58 import com.google.turbine.model.TurbineFlag; 59 import com.google.turbine.tree.Tree; 60 import com.google.turbine.tree.Tree.CompUnit; 61 import com.google.turbine.tree.Tree.ModDecl; 62 import com.google.turbine.type.Type; 63 import java.util.List; 64 import java.util.Optional; 65 66 /** The entry point for analysis. */ 67 public class Binder { 68 69 /** Binds symbols and types to the given compilation units. */ bind( List<CompUnit> units, ClassPath classpath, ClassPath bootclasspath, Optional<String> moduleVersion)70 public static BindingResult bind( 71 List<CompUnit> units, 72 ClassPath classpath, 73 ClassPath bootclasspath, 74 Optional<String> moduleVersion) { 75 76 ImmutableList<PreprocessedCompUnit> preProcessedUnits = CompUnitPreprocessor.preprocess(units); 77 78 SimpleEnv<ClassSymbol, SourceBoundClass> ienv = bindSourceBoundClasses(preProcessedUnits); 79 80 ImmutableSet<ClassSymbol> syms = ienv.asMap().keySet(); 81 82 CompoundTopLevelIndex tli = 83 CompoundTopLevelIndex.of( 84 SimpleTopLevelIndex.of(ienv.asMap().keySet()), 85 bootclasspath.index(), 86 classpath.index()); 87 88 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv = 89 CompoundEnv.of(classpath.env()).append(bootclasspath.env()); 90 91 CompoundEnv<ModuleSymbol, ModuleInfo> classPathModuleEnv = 92 CompoundEnv.of(classpath.moduleEnv()).append(bootclasspath.moduleEnv()); 93 94 TurbineLog log = new TurbineLog(); 95 96 BindPackagesResult bindPackagesResult = 97 bindPackages(log, ienv, tli, preProcessedUnits, classPathEnv); 98 99 SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv = bindPackagesResult.classes; 100 SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules = bindPackagesResult.modules; 101 102 Env<ClassSymbol, SourceHeaderBoundClass> henv = bindHierarchy(log, syms, psenv, classPathEnv); 103 104 Env<ClassSymbol, SourceTypeBoundClass> tenv = 105 bindTypes( 106 log, 107 syms, 108 henv, 109 CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv)); 110 111 log.maybeThrow(); 112 113 tenv = 114 constants( 115 syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); 116 tenv = 117 disambiguateTypeAnnotations( 118 syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); 119 tenv = 120 canonicalizeTypes( 121 syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); 122 123 ImmutableList<SourceModuleInfo> boundModules = 124 bindModules( 125 modules, 126 CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv), 127 classPathModuleEnv, 128 moduleVersion); 129 130 ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder(); 131 for (ClassSymbol sym : syms) { 132 result.put(sym, tenv.get(sym)); 133 } 134 return new BindingResult(result.build(), boundModules, classPathEnv); 135 } 136 137 /** Records enclosing declarations of member classes, and group classes by compilation unit. */ bindSourceBoundClasses( ImmutableList<PreprocessedCompUnit> units)138 static SimpleEnv<ClassSymbol, SourceBoundClass> bindSourceBoundClasses( 139 ImmutableList<PreprocessedCompUnit> units) { 140 SimpleEnv.Builder<ClassSymbol, SourceBoundClass> envBuilder = SimpleEnv.builder(); 141 for (PreprocessedCompUnit unit : units) { 142 for (SourceBoundClass type : unit.types()) { 143 SourceBoundClass prev = envBuilder.put(type.sym(), type); 144 if (prev != null) { 145 throw TurbineError.format( 146 unit.source(), type.decl().position(), ErrorKind.DUPLICATE_DECLARATION, type.sym()); 147 } 148 } 149 } 150 return envBuilder.build(); 151 } 152 153 static class BindPackagesResult { 154 final SimpleEnv<ClassSymbol, PackageSourceBoundClass> classes; 155 final SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules; 156 BindPackagesResult( SimpleEnv<ClassSymbol, PackageSourceBoundClass> classes, SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules)157 BindPackagesResult( 158 SimpleEnv<ClassSymbol, PackageSourceBoundClass> classes, 159 SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules) { 160 this.classes = classes; 161 this.modules = modules; 162 } 163 } 164 165 /** Initializes scopes for compilation unit and package-level lookup. */ bindPackages( TurbineLog log, Env<ClassSymbol, SourceBoundClass> ienv, TopLevelIndex tli, ImmutableList<PreprocessedCompUnit> units, CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv)166 private static BindPackagesResult bindPackages( 167 TurbineLog log, 168 Env<ClassSymbol, SourceBoundClass> ienv, 169 TopLevelIndex tli, 170 ImmutableList<PreprocessedCompUnit> units, 171 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { 172 173 SimpleEnv.Builder<ClassSymbol, PackageSourceBoundClass> env = SimpleEnv.builder(); 174 SimpleEnv.Builder<ModuleSymbol, PackageSourceBoundModule> modules = SimpleEnv.builder(); 175 Scope javaLang = tli.lookupPackage(ImmutableList.of("java", "lang")); 176 if (javaLang == null) { 177 // TODO(cushon): add support for diagnostics without a source position, and make this one 178 // of those 179 throw new IllegalArgumentException("Could not find java.lang on bootclasspath"); 180 } 181 CompoundScope topLevel = CompoundScope.base(tli.scope()).append(javaLang); 182 for (PreprocessedCompUnit unit : units) { 183 ImmutableList<String> packagename = 184 ImmutableList.copyOf(Splitter.on('/').omitEmptyStrings().split(unit.packageName())); 185 Scope packageScope = tli.lookupPackage(packagename); 186 CanonicalSymbolResolver importResolver = 187 new CanonicalResolver( 188 unit.packageName(), 189 CompoundEnv.<ClassSymbol, BoundClass>of(classPathEnv).append(ienv)); 190 ImportScope importScope = 191 ImportIndex.create(log.withSource(unit.source()), importResolver, tli, unit.imports()); 192 ImportScope wildImportScope = WildImportIndex.create(importResolver, tli, unit.imports()); 193 MemberImportIndex memberImports = 194 new MemberImportIndex(unit.source(), importResolver, tli, unit.imports()); 195 ImportScope scope = 196 ImportScope.fromScope(topLevel) 197 .append(wildImportScope) 198 .append(ImportScope.fromScope(packageScope)) 199 .append(importScope); 200 if (unit.module().isPresent()) { 201 ModDecl module = unit.module().get(); 202 modules.put( 203 new ModuleSymbol(module.moduleName()), 204 new PackageSourceBoundModule(module, scope, memberImports, unit.source())); 205 } 206 for (SourceBoundClass type : unit.types()) { 207 env.put(type.sym(), new PackageSourceBoundClass(type, scope, memberImports, unit.source())); 208 } 209 } 210 return new BindPackagesResult(env.build(), modules.build()); 211 } 212 213 /** Binds the type hierarchy (superclasses and interfaces) for all classes in the compilation. */ bindHierarchy( TurbineLog log, Iterable<ClassSymbol> syms, final SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv, CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv)214 private static Env<ClassSymbol, SourceHeaderBoundClass> bindHierarchy( 215 TurbineLog log, 216 Iterable<ClassSymbol> syms, 217 final SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv, 218 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { 219 ImmutableMap.Builder< 220 ClassSymbol, LazyEnv.Completer<ClassSymbol, HeaderBoundClass, SourceHeaderBoundClass>> 221 completers = ImmutableMap.builder(); 222 for (ClassSymbol sym : syms) { 223 completers.put( 224 sym, 225 new LazyEnv.Completer<ClassSymbol, HeaderBoundClass, SourceHeaderBoundClass>() { 226 @Override 227 public SourceHeaderBoundClass complete( 228 Env<ClassSymbol, HeaderBoundClass> henv, ClassSymbol sym) { 229 PackageSourceBoundClass base = psenv.get(sym); 230 return HierarchyBinder.bind(log.withSource(base.source()), sym, base, henv); 231 } 232 }); 233 } 234 return new LazyEnv<>(completers.build(), classPathEnv); 235 } 236 bindTypes( TurbineLog log, ImmutableSet<ClassSymbol> syms, Env<ClassSymbol, SourceHeaderBoundClass> shenv, Env<ClassSymbol, HeaderBoundClass> henv)237 private static Env<ClassSymbol, SourceTypeBoundClass> bindTypes( 238 TurbineLog log, 239 ImmutableSet<ClassSymbol> syms, 240 Env<ClassSymbol, SourceHeaderBoundClass> shenv, 241 Env<ClassSymbol, HeaderBoundClass> henv) { 242 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 243 for (ClassSymbol sym : syms) { 244 SourceHeaderBoundClass base = shenv.get(sym); 245 builder.put(sym, TypeBinder.bind(log.withSource(base.source()), henv, sym, base)); 246 } 247 return builder.build(); 248 } 249 canonicalizeTypes( ImmutableSet<ClassSymbol> syms, Env<ClassSymbol, SourceTypeBoundClass> stenv, Env<ClassSymbol, TypeBoundClass> tenv)250 private static Env<ClassSymbol, SourceTypeBoundClass> canonicalizeTypes( 251 ImmutableSet<ClassSymbol> syms, 252 Env<ClassSymbol, SourceTypeBoundClass> stenv, 253 Env<ClassSymbol, TypeBoundClass> tenv) { 254 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 255 for (ClassSymbol sym : syms) { 256 builder.put(sym, CanonicalTypeBinder.bind(sym, stenv.get(sym), tenv)); 257 } 258 return builder.build(); 259 } 260 bindModules( SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules, CompoundEnv<ClassSymbol, TypeBoundClass> env, CompoundEnv<ModuleSymbol, ModuleInfo> moduleEnv, Optional<String> moduleVersion)261 private static ImmutableList<SourceModuleInfo> bindModules( 262 SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules, 263 CompoundEnv<ClassSymbol, TypeBoundClass> env, 264 CompoundEnv<ModuleSymbol, ModuleInfo> moduleEnv, 265 Optional<String> moduleVersion) { 266 // Allow resolution of modules in the current compilation. Currently this is only needed for 267 // version strings in requires directives. 268 moduleEnv = 269 moduleEnv.append( 270 new Env<ModuleSymbol, ModuleInfo>() { 271 @Override 272 public ModuleInfo get(ModuleSymbol sym) { 273 PackageSourceBoundModule info = modules.get(sym); 274 if (info != null) { 275 return new ModuleInfo( 276 info.module().moduleName(), 277 moduleVersion.orElse(null), 278 /* flags= */ 0, 279 /* annos= */ ImmutableList.of(), 280 /* requires= */ ImmutableList.of(), 281 /* exports= */ ImmutableList.of(), 282 /* opens= */ ImmutableList.of(), 283 /* uses= */ ImmutableList.of(), 284 /* provides= */ ImmutableList.of()); 285 } 286 return null; 287 } 288 }); 289 ImmutableList.Builder<SourceModuleInfo> bound = ImmutableList.builder(); 290 for (PackageSourceBoundModule module : modules.asMap().values()) { 291 bound.add(ModuleBinder.bind(module, env, moduleEnv, moduleVersion)); 292 } 293 return bound.build(); 294 } 295 constants( ImmutableSet<ClassSymbol> syms, Env<ClassSymbol, SourceTypeBoundClass> env, CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv)296 private static Env<ClassSymbol, SourceTypeBoundClass> constants( 297 ImmutableSet<ClassSymbol> syms, 298 Env<ClassSymbol, SourceTypeBoundClass> env, 299 CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv) { 300 301 // Prepare to lazily evaluate constant fields in each compilation unit. 302 // The laziness is necessary since constant fields can reference other 303 // constant fields. 304 ImmutableMap.Builder<FieldSymbol, LazyEnv.Completer<FieldSymbol, Const.Value, Const.Value>> 305 completers = ImmutableMap.builder(); 306 for (ClassSymbol sym : syms) { 307 SourceTypeBoundClass info = env.get(sym); 308 for (FieldInfo field : info.fields()) { 309 if (!isConst(field)) { 310 continue; 311 } 312 completers.put( 313 field.sym(), 314 new LazyEnv.Completer<FieldSymbol, Const.Value, Const.Value>() { 315 @Override 316 public Const.Value complete(Env<FieldSymbol, Const.Value> env1, FieldSymbol k) { 317 try { 318 return new ConstEvaluator( 319 sym, 320 sym, 321 info.memberImports(), 322 info.source(), 323 info.scope(), 324 env1, 325 baseEnv) 326 .evalFieldInitializer(field.decl().init().get(), field.type()); 327 } catch (LazyEnv.LazyBindingError e) { 328 // fields initializers are allowed to reference the field being initialized, 329 // but if they do they aren't constants 330 return null; 331 } 332 } 333 }); 334 } 335 } 336 337 // Create an environment of constant field values that combines 338 // lazily evaluated fields in the current compilation unit with 339 // constant fields in the classpath (which don't require evaluation). 340 Env<FieldSymbol, Const.Value> constenv = 341 new LazyEnv<>(completers.build(), SimpleEnv.<FieldSymbol, Const.Value>builder().build()); 342 343 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 344 for (ClassSymbol sym : syms) { 345 builder.put(sym, new ConstBinder(constenv, sym, baseEnv, env.get(sym)).bind()); 346 } 347 return builder.build(); 348 } 349 isConst(FieldInfo field)350 static boolean isConst(FieldInfo field) { 351 if ((field.access() & TurbineFlag.ACC_FINAL) == 0) { 352 return false; 353 } 354 if (field.decl() == null) { 355 return false; 356 } 357 final Optional<Tree.Expression> init = field.decl().init(); 358 if (!init.isPresent()) { 359 return false; 360 } 361 switch (field.type().tyKind()) { 362 case PRIM_TY: 363 break; 364 case CLASS_TY: 365 if (((Type.ClassTy) field.type()).sym().equals(ClassSymbol.STRING)) { 366 break; 367 } 368 // fall through 369 default: 370 return false; 371 } 372 return true; 373 } 374 375 /** 376 * Disambiguate annotations on field types and method return types that could be declaration or 377 * type annotations. 378 */ disambiguateTypeAnnotations( ImmutableSet<ClassSymbol> syms, Env<ClassSymbol, SourceTypeBoundClass> stenv, Env<ClassSymbol, TypeBoundClass> tenv)379 private static Env<ClassSymbol, SourceTypeBoundClass> disambiguateTypeAnnotations( 380 ImmutableSet<ClassSymbol> syms, 381 Env<ClassSymbol, SourceTypeBoundClass> stenv, 382 Env<ClassSymbol, TypeBoundClass> tenv) { 383 SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); 384 for (ClassSymbol sym : syms) { 385 builder.put(sym, DisambiguateTypeAnnotations.bind(stenv.get(sym), tenv)); 386 } 387 return builder.build(); 388 } 389 390 /** The result of binding: bound nodes for sources in the compilation, and the classpath. */ 391 public static class BindingResult { 392 private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units; 393 private final ImmutableList<SourceModuleInfo> modules; 394 private final CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv; 395 BindingResult( ImmutableMap<ClassSymbol, SourceTypeBoundClass> units, ImmutableList<SourceModuleInfo> modules, CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv)396 public BindingResult( 397 ImmutableMap<ClassSymbol, SourceTypeBoundClass> units, 398 ImmutableList<SourceModuleInfo> modules, 399 CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { 400 this.units = units; 401 this.modules = modules; 402 this.classPathEnv = classPathEnv; 403 } 404 405 /** Bound nodes for sources in the compilation. */ units()406 public ImmutableMap<ClassSymbol, SourceTypeBoundClass> units() { 407 return units; 408 } 409 modules()410 public ImmutableList<SourceModuleInfo> modules() { 411 return modules; 412 } 413 414 /** The classpath. */ classPathEnv()415 public CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv() { 416 return classPathEnv; 417 } 418 } 419 } 420