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. */ bindClasspath(Collection<Path> paths)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 Map<String, Supplier<byte[]>> resources = new HashMap<>(); 57 Env<ClassSymbol, BytecodeBoundClass> benv = 58 new Env<ClassSymbol, BytecodeBoundClass>() { 59 @Override 60 public BytecodeBoundClass get(ClassSymbol sym) { 61 return map.get(sym); 62 } 63 }; 64 for (Path path : paths) { 65 try { 66 bindJar(path, map, modules, benv, transitive, resources); 67 } catch (IOException e) { 68 throw new IOException("error reading " + path, e); 69 } 70 } 71 for (Map.Entry<ClassSymbol, BytecodeBoundClass> entry : transitive.entrySet()) { 72 ClassSymbol symbol = entry.getKey(); 73 map.putIfAbsent(symbol, entry.getValue()); 74 } 75 SimpleEnv<ClassSymbol, BytecodeBoundClass> env = new SimpleEnv<>(ImmutableMap.copyOf(map)); 76 SimpleEnv<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.copyOf(modules)); 77 TopLevelIndex index = SimpleTopLevelIndex.of(env.asMap().keySet()); 78 return new ClassPath() { 79 @Override 80 public Env<ClassSymbol, BytecodeBoundClass> env() { 81 return env; 82 } 83 84 @Override 85 public Env<ModuleSymbol, ModuleInfo> moduleEnv() { 86 return moduleEnv; 87 } 88 89 @Override 90 public TopLevelIndex index() { 91 return index; 92 } 93 94 @Override 95 public Supplier<byte[]> resource(String path) { 96 return resources.get(path); 97 } 98 }; 99 } 100 101 private static void bindJar( 102 Path path, 103 Map<ClassSymbol, BytecodeBoundClass> env, 104 Map<ModuleSymbol, ModuleInfo> modules, 105 Env<ClassSymbol, BytecodeBoundClass> benv, 106 Map<ClassSymbol, BytecodeBoundClass> transitive, 107 Map<String, Supplier<byte[]>> resources) 108 throws IOException { 109 // TODO(cushon): don't leak file descriptors 110 for (Zip.Entry ze : new Zip.ZipIterable(path)) { 111 String name = ze.name(); 112 if (!name.endsWith(".class")) { 113 resources.put(name, toByteArrayOrDie(ze)); 114 continue; 115 } 116 if (name.startsWith(TRANSITIVE_PREFIX)) { 117 ClassSymbol sym = 118 new ClassSymbol( 119 name.substring(TRANSITIVE_PREFIX.length(), name.length() - ".class".length())); 120 transitive.computeIfAbsent( 121 sym, 122 new Function<ClassSymbol, BytecodeBoundClass>() { 123 @Override 124 public BytecodeBoundClass apply(ClassSymbol sym) { 125 return new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString()); 126 } 127 }); 128 continue; 129 } 130 if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.class")) { 131 ModuleInfo moduleInfo = 132 BytecodeBinder.bindModuleInfo(path.toString(), toByteArrayOrDie(ze)); 133 modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo); 134 continue; 135 } 136 ClassSymbol sym = new ClassSymbol(name.substring(0, name.length() - ".class".length())); 137 env.putIfAbsent( 138 sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString())); 139 } 140 } 141 142 private static Supplier<byte[]> toByteArrayOrDie(Zip.Entry ze) { 143 return Suppliers.memoize( 144 new Supplier<byte[]>() { 145 @Override 146 public byte[] get() { 147 return ze.data(); 148 } 149 }); 150 } 151 } 152