Home | History | Annotate | Download | only in binder
      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