1 /* 2 * Copyright 2018 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 static com.google.common.base.StandardSystemProperty.JAVA_HOME; 20 21 import com.google.common.base.Supplier; 22 import com.google.common.base.Suppliers; 23 import com.google.common.collect.ImmutableMap; 24 import com.google.turbine.binder.bound.ModuleInfo; 25 import com.google.turbine.binder.bytecode.BytecodeBinder; 26 import com.google.turbine.binder.bytecode.BytecodeBoundClass; 27 import com.google.turbine.binder.env.Env; 28 import com.google.turbine.binder.env.SimpleEnv; 29 import com.google.turbine.binder.lookup.SimpleTopLevelIndex; 30 import com.google.turbine.binder.lookup.TopLevelIndex; 31 import com.google.turbine.binder.sym.ClassSymbol; 32 import com.google.turbine.binder.sym.ModuleSymbol; 33 import com.google.turbine.zip.Zip; 34 import java.io.IOException; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.util.HashMap; 39 import java.util.Map; 40 import org.checkerframework.checker.nullness.qual.Nullable; 41 42 /** Constructs a platform {@link ClassPath} from the current JDK's ct.sym file. */ 43 public class CtSymClassBinder { 44 45 @Nullable bind(String version)46 public static ClassPath bind(String version) throws IOException { 47 Path javaHome = Paths.get(JAVA_HOME.value()); 48 Path ctSym = javaHome.resolve("lib/ct.sym"); 49 if (!Files.exists(ctSym)) { 50 throw new IllegalStateException("lib/ct.sym does not exist in " + javaHome); 51 } 52 Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>(); 53 Map<ModuleSymbol, ModuleInfo> modules = new HashMap<>(); 54 Env<ClassSymbol, BytecodeBoundClass> benv = 55 new Env<ClassSymbol, BytecodeBoundClass>() { 56 @Override 57 public BytecodeBoundClass get(ClassSymbol sym) { 58 return map.get(sym); 59 } 60 }; 61 // ct.sym contains directories whose names are the concatentation of a list of target versions 62 // (e.g. 789) and which contain interface class files with a .sig extension. 63 for (Zip.Entry ze : new Zip.ZipIterable(ctSym)) { 64 String name = ze.name(); 65 if (!name.endsWith(".sig")) { 66 continue; 67 } 68 int idx = name.indexOf('/'); 69 if (idx == -1) { 70 continue; 71 } 72 // check if the directory matches the desired release 73 // TODO(cushon): what happens when version numbers contain more than one digit? 74 if (!ze.name().substring(0, idx).contains(version)) { 75 continue; 76 } 77 if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.sig")) { 78 ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(name, toByteArrayOrDie(ze)); 79 modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo); 80 continue; 81 } 82 ClassSymbol sym = new ClassSymbol(name.substring(idx + 1, name.length() - ".sig".length())); 83 map.putIfAbsent( 84 sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, ctSym + "!" + ze.name())); 85 } 86 if (map.isEmpty()) { 87 // we didn't find any classes for the desired release 88 return null; 89 } 90 SimpleEnv<ClassSymbol, BytecodeBoundClass> env = new SimpleEnv<>(ImmutableMap.copyOf(map)); 91 Env<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.copyOf(modules)); 92 TopLevelIndex index = SimpleTopLevelIndex.of(env.asMap().keySet()); 93 return new ClassPath() { 94 @Override 95 public Env<ClassSymbol, BytecodeBoundClass> env() { 96 return env; 97 } 98 99 @Override 100 public Env<ModuleSymbol, ModuleInfo> moduleEnv() { 101 return moduleEnv; 102 } 103 104 @Override 105 public TopLevelIndex index() { 106 return index; 107 } 108 109 @Override 110 public Supplier<byte[]> resource(String input) { 111 return null; 112 } 113 }; 114 } 115 116 private static Supplier<byte[]> toByteArrayOrDie(Zip.Entry ze) { 117 return Suppliers.memoize( 118 new Supplier<byte[]>() { 119 @Override 120 public byte[] get() { 121 return ze.data(); 122 } 123 }); 124 } 125 } 126