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 com.google.common.base.Function;
     20 import com.google.common.base.Supplier;
     21 import com.google.common.base.Suppliers;
     22 import com.google.common.collect.ImmutableMap;
     23 import com.google.turbine.binder.bytecode.BytecodeBoundClass;
     24 import com.google.turbine.binder.env.CompoundEnv;
     25 import com.google.turbine.binder.env.Env;
     26 import com.google.turbine.binder.env.SimpleEnv;
     27 import com.google.turbine.binder.lookup.TopLevelIndex;
     28 import com.google.turbine.binder.sym.ClassSymbol;
     29 import com.google.turbine.zip.Zip;
     30 import java.io.IOException;
     31 import java.nio.file.Path;
     32 import java.util.Collection;
     33 import java.util.HashMap;
     34 import java.util.LinkedHashMap;
     35 import java.util.Map;
     36 
     37 /** Sets up an environment for symbols on the classpath. */
     38 public class ClassPathBinder {
     39 
     40   /**
     41    * The prefix for repackaged transitive dependencies; see {@link
     42    * com.google.turbine.deps.Transitive}.
     43    */
     44   public static final String TRANSITIVE_PREFIX = "META-INF/TRANSITIVE/";
     45 
     46   /**
     47    * Creates an environment containing symbols in the given classpath and bootclasspath, and adds
     48    * them to the top-level index.
     49    */
     50   public static CompoundEnv<ClassSymbol, BytecodeBoundClass> bind(
     51       Collection<Path> classpath, Collection<Path> bootclasspath, TopLevelIndex.Builder tli)
     52       throws IOException {
     53     // TODO(cushon): this is going to require an env eventually,
     54     // e.g. to look up type parameters in enclosing declarations
     55     Env<ClassSymbol, BytecodeBoundClass> cp = bindClasspath(tli, classpath);
     56     Env<ClassSymbol, BytecodeBoundClass> bcp = bindClasspath(tli, bootclasspath);
     57     return CompoundEnv.of(cp).append(bcp);
     58   }
     59 
     60   private static Env<ClassSymbol, BytecodeBoundClass> bindClasspath(
     61       TopLevelIndex.Builder tli, Collection<Path> paths) throws IOException {
     62     Map<ClassSymbol, BytecodeBoundClass> transitive = new LinkedHashMap<>();
     63     Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>();
     64     Env<ClassSymbol, BytecodeBoundClass> benv =
     65         new Env<ClassSymbol, BytecodeBoundClass>() {
     66           @Override
     67           public BytecodeBoundClass get(ClassSymbol sym) {
     68             return map.get(sym);
     69           }
     70         };
     71     for (Path path : paths) {
     72       try {
     73         bindJar(tli, path, map, benv, transitive);
     74       } catch (IOException e) {
     75         throw new IOException("error reading " + path, e);
     76       }
     77     }
     78     for (Map.Entry<ClassSymbol, BytecodeBoundClass> entry : transitive.entrySet()) {
     79       ClassSymbol symbol = entry.getKey();
     80       if (!map.containsKey(symbol)) {
     81         map.put(symbol, entry.getValue());
     82         tli.insert(symbol);
     83       }
     84     }
     85     return new SimpleEnv<>(ImmutableMap.copyOf(map));
     86   }
     87 
     88   private static void bindJar(
     89       TopLevelIndex.Builder tli,
     90       Path path,
     91       Map<ClassSymbol, BytecodeBoundClass> env,
     92       Env<ClassSymbol, BytecodeBoundClass> benv,
     93       Map<ClassSymbol, BytecodeBoundClass> transitive)
     94       throws IOException {
     95     // TODO(cushon): don't leak file descriptors
     96     for (Zip.Entry ze : new Zip.ZipIterable(path)) {
     97       String name = ze.name();
     98       if (!name.endsWith(".class")) {
     99         continue;
    100       }
    101       if (name.startsWith(TRANSITIVE_PREFIX)) {
    102         ClassSymbol sym =
    103             new ClassSymbol(
    104                 name.substring(TRANSITIVE_PREFIX.length(), name.length() - ".class".length()));
    105         transitive.computeIfAbsent(
    106             sym,
    107             new Function<ClassSymbol, BytecodeBoundClass>() {
    108               @Override
    109               public BytecodeBoundClass apply(ClassSymbol sym) {
    110                 return new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString());
    111               }
    112             });
    113         continue;
    114       }
    115       ClassSymbol sym = new ClassSymbol(name.substring(0, name.length() - ".class".length()));
    116       if (!env.containsKey(sym)) {
    117         env.put(sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString()));
    118         tli.insert(sym);
    119       }
    120     }
    121   }
    122 
    123   private static Supplier<byte[]> toByteArrayOrDie(Zip.Entry ze) {
    124     return Suppliers.memoize(
    125         new Supplier<byte[]>() {
    126           @Override
    127           public byte[] get() {
    128             return ze.data();
    129           }
    130         });
    131   }
    132 }
    133