/* * Copyright 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.turbine.binder; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.turbine.binder.CompUnitPreprocessor.PreprocessedCompUnit; import com.google.turbine.binder.Resolve.CanonicalResolver; import com.google.turbine.binder.bound.BoundClass; import com.google.turbine.binder.bound.HeaderBoundClass; import com.google.turbine.binder.bound.ModuleInfo; import com.google.turbine.binder.bound.PackageSourceBoundClass; import com.google.turbine.binder.bound.PackageSourceBoundModule; import com.google.turbine.binder.bound.SourceBoundClass; import com.google.turbine.binder.bound.SourceHeaderBoundClass; import com.google.turbine.binder.bound.SourceModuleInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.CompoundEnv; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.env.LazyEnv; import com.google.turbine.binder.env.SimpleEnv; import com.google.turbine.binder.lookup.CanonicalSymbolResolver; import com.google.turbine.binder.lookup.CompoundScope; import com.google.turbine.binder.lookup.CompoundTopLevelIndex; import com.google.turbine.binder.lookup.ImportIndex; import com.google.turbine.binder.lookup.ImportScope; import com.google.turbine.binder.lookup.MemberImportIndex; import com.google.turbine.binder.lookup.Scope; import com.google.turbine.binder.lookup.SimpleTopLevelIndex; import com.google.turbine.binder.lookup.TopLevelIndex; import com.google.turbine.binder.lookup.WildImportIndex; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.ModuleSymbol; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.diag.TurbineLog; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineFlag; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.CompUnit; import com.google.turbine.tree.Tree.ModDecl; import com.google.turbine.type.Type; import java.util.List; import java.util.Optional; /** The entry point for analysis. */ public class Binder { /** Binds symbols and types to the given compilation units. */ public static BindingResult bind( List units, ClassPath classpath, ClassPath bootclasspath, Optional moduleVersion) { ImmutableList preProcessedUnits = CompUnitPreprocessor.preprocess(units); SimpleEnv ienv = bindSourceBoundClasses(preProcessedUnits); ImmutableSet syms = ienv.asMap().keySet(); CompoundTopLevelIndex tli = CompoundTopLevelIndex.of( SimpleTopLevelIndex.of(ienv.asMap().keySet()), bootclasspath.index(), classpath.index()); CompoundEnv classPathEnv = CompoundEnv.of(classpath.env()).append(bootclasspath.env()); CompoundEnv classPathModuleEnv = CompoundEnv.of(classpath.moduleEnv()).append(bootclasspath.moduleEnv()); TurbineLog log = new TurbineLog(); BindPackagesResult bindPackagesResult = bindPackages(log, ienv, tli, preProcessedUnits, classPathEnv); SimpleEnv psenv = bindPackagesResult.classes; SimpleEnv modules = bindPackagesResult.modules; Env henv = bindHierarchy(log, syms, psenv, classPathEnv); Env tenv = bindTypes( log, syms, henv, CompoundEnv.of(classPathEnv).append(henv)); log.maybeThrow(); tenv = constants( syms, tenv, CompoundEnv.of(classPathEnv).append(tenv)); tenv = disambiguateTypeAnnotations( syms, tenv, CompoundEnv.of(classPathEnv).append(tenv)); tenv = canonicalizeTypes( syms, tenv, CompoundEnv.of(classPathEnv).append(tenv)); ImmutableList boundModules = bindModules( modules, CompoundEnv.of(classPathEnv).append(tenv), classPathModuleEnv, moduleVersion); ImmutableMap.Builder result = ImmutableMap.builder(); for (ClassSymbol sym : syms) { result.put(sym, tenv.get(sym)); } return new BindingResult(result.build(), boundModules, classPathEnv); } /** Records enclosing declarations of member classes, and group classes by compilation unit. */ static SimpleEnv bindSourceBoundClasses( ImmutableList units) { SimpleEnv.Builder envBuilder = SimpleEnv.builder(); for (PreprocessedCompUnit unit : units) { for (SourceBoundClass type : unit.types()) { SourceBoundClass prev = envBuilder.put(type.sym(), type); if (prev != null) { throw TurbineError.format( unit.source(), type.decl().position(), ErrorKind.DUPLICATE_DECLARATION, type.sym()); } } } return envBuilder.build(); } static class BindPackagesResult { final SimpleEnv classes; final SimpleEnv modules; BindPackagesResult( SimpleEnv classes, SimpleEnv modules) { this.classes = classes; this.modules = modules; } } /** Initializes scopes for compilation unit and package-level lookup. */ private static BindPackagesResult bindPackages( TurbineLog log, Env ienv, TopLevelIndex tli, ImmutableList units, CompoundEnv classPathEnv) { SimpleEnv.Builder env = SimpleEnv.builder(); SimpleEnv.Builder modules = SimpleEnv.builder(); Scope javaLang = tli.lookupPackage(ImmutableList.of("java", "lang")); if (javaLang == null) { // TODO(cushon): add support for diagnostics without a source position, and make this one // of those throw new IllegalArgumentException("Could not find java.lang on bootclasspath"); } CompoundScope topLevel = CompoundScope.base(tli.scope()).append(javaLang); for (PreprocessedCompUnit unit : units) { ImmutableList packagename = ImmutableList.copyOf(Splitter.on('/').omitEmptyStrings().split(unit.packageName())); Scope packageScope = tli.lookupPackage(packagename); CanonicalSymbolResolver importResolver = new CanonicalResolver( unit.packageName(), CompoundEnv.of(classPathEnv).append(ienv)); ImportScope importScope = ImportIndex.create(log.withSource(unit.source()), importResolver, tli, unit.imports()); ImportScope wildImportScope = WildImportIndex.create(importResolver, tli, unit.imports()); MemberImportIndex memberImports = new MemberImportIndex(unit.source(), importResolver, tli, unit.imports()); ImportScope scope = ImportScope.fromScope(topLevel) .append(wildImportScope) .append(ImportScope.fromScope(packageScope)) .append(importScope); if (unit.module().isPresent()) { ModDecl module = unit.module().get(); modules.put( new ModuleSymbol(module.moduleName()), new PackageSourceBoundModule(module, scope, memberImports, unit.source())); } for (SourceBoundClass type : unit.types()) { env.put(type.sym(), new PackageSourceBoundClass(type, scope, memberImports, unit.source())); } } return new BindPackagesResult(env.build(), modules.build()); } /** Binds the type hierarchy (superclasses and interfaces) for all classes in the compilation. */ private static Env bindHierarchy( TurbineLog log, Iterable syms, final SimpleEnv psenv, CompoundEnv classPathEnv) { ImmutableMap.Builder< ClassSymbol, LazyEnv.Completer> completers = ImmutableMap.builder(); for (ClassSymbol sym : syms) { completers.put( sym, new LazyEnv.Completer() { @Override public SourceHeaderBoundClass complete( Env henv, ClassSymbol sym) { PackageSourceBoundClass base = psenv.get(sym); return HierarchyBinder.bind(log.withSource(base.source()), sym, base, henv); } }); } return new LazyEnv<>(completers.build(), classPathEnv); } private static Env bindTypes( TurbineLog log, ImmutableSet syms, Env shenv, Env henv) { SimpleEnv.Builder builder = SimpleEnv.builder(); for (ClassSymbol sym : syms) { SourceHeaderBoundClass base = shenv.get(sym); builder.put(sym, TypeBinder.bind(log.withSource(base.source()), henv, sym, base)); } return builder.build(); } private static Env canonicalizeTypes( ImmutableSet syms, Env stenv, Env tenv) { SimpleEnv.Builder builder = SimpleEnv.builder(); for (ClassSymbol sym : syms) { builder.put(sym, CanonicalTypeBinder.bind(sym, stenv.get(sym), tenv)); } return builder.build(); } private static ImmutableList bindModules( SimpleEnv modules, CompoundEnv env, CompoundEnv moduleEnv, Optional moduleVersion) { // Allow resolution of modules in the current compilation. Currently this is only needed for // version strings in requires directives. moduleEnv = moduleEnv.append( new Env() { @Override public ModuleInfo get(ModuleSymbol sym) { PackageSourceBoundModule info = modules.get(sym); if (info != null) { return new ModuleInfo( info.module().moduleName(), moduleVersion.orElse(null), /* flags= */ 0, /* annos= */ ImmutableList.of(), /* requires= */ ImmutableList.of(), /* exports= */ ImmutableList.of(), /* opens= */ ImmutableList.of(), /* uses= */ ImmutableList.of(), /* provides= */ ImmutableList.of()); } return null; } }); ImmutableList.Builder bound = ImmutableList.builder(); for (PackageSourceBoundModule module : modules.asMap().values()) { bound.add(ModuleBinder.bind(module, env, moduleEnv, moduleVersion)); } return bound.build(); } private static Env constants( ImmutableSet syms, Env env, CompoundEnv baseEnv) { // Prepare to lazily evaluate constant fields in each compilation unit. // The laziness is necessary since constant fields can reference other // constant fields. ImmutableMap.Builder> completers = ImmutableMap.builder(); for (ClassSymbol sym : syms) { SourceTypeBoundClass info = env.get(sym); for (FieldInfo field : info.fields()) { if (!isConst(field)) { continue; } completers.put( field.sym(), new LazyEnv.Completer() { @Override public Const.Value complete(Env env1, FieldSymbol k) { try { return new ConstEvaluator( sym, sym, info.memberImports(), info.source(), info.scope(), env1, baseEnv) .evalFieldInitializer(field.decl().init().get(), field.type()); } catch (LazyEnv.LazyBindingError e) { // fields initializers are allowed to reference the field being initialized, // but if they do they aren't constants return null; } } }); } } // Create an environment of constant field values that combines // lazily evaluated fields in the current compilation unit with // constant fields in the classpath (which don't require evaluation). Env constenv = new LazyEnv<>(completers.build(), SimpleEnv.builder().build()); SimpleEnv.Builder builder = SimpleEnv.builder(); for (ClassSymbol sym : syms) { builder.put(sym, new ConstBinder(constenv, sym, baseEnv, env.get(sym)).bind()); } return builder.build(); } static boolean isConst(FieldInfo field) { if ((field.access() & TurbineFlag.ACC_FINAL) == 0) { return false; } if (field.decl() == null) { return false; } final Optional init = field.decl().init(); if (!init.isPresent()) { return false; } switch (field.type().tyKind()) { case PRIM_TY: break; case CLASS_TY: if (((Type.ClassTy) field.type()).sym().equals(ClassSymbol.STRING)) { break; } // fall through default: return false; } return true; } /** * Disambiguate annotations on field types and method return types that could be declaration or * type annotations. */ private static Env disambiguateTypeAnnotations( ImmutableSet syms, Env stenv, Env tenv) { SimpleEnv.Builder builder = SimpleEnv.builder(); for (ClassSymbol sym : syms) { builder.put(sym, DisambiguateTypeAnnotations.bind(stenv.get(sym), tenv)); } return builder.build(); } /** The result of binding: bound nodes for sources in the compilation, and the classpath. */ public static class BindingResult { private final ImmutableMap units; private final ImmutableList modules; private final CompoundEnv classPathEnv; public BindingResult( ImmutableMap units, ImmutableList modules, CompoundEnv classPathEnv) { this.units = units; this.modules = modules; this.classPathEnv = classPathEnv; } /** Bound nodes for sources in the compilation. */ public ImmutableMap units() { return units; } public ImmutableList modules() { return modules; } /** The classpath. */ public CompoundEnv classPathEnv() { return classPathEnv; } } }