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