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