1 cananian 1.10.2.8 // Loader.java, created Fri Jul 31 4:33:28 1998 by cananian 2 cananian 1.5 // Copyright (C) 1998 C. Scott Ananian <cananian@alumni.princeton.edu> 3 cananian 1.5 // Licensed under the terms of the GNU GPL; see COPYING for details. 4 cananian 1.1 package harpoon.ClassFile; 5 cananian 1.1 6 cananian 1.1 import java.io.File; 7 cananian 1.1 import java.io.InputStream; 8 cananian 1.1 import java.io.FileInputStream; 9 cananian 1.10.2.13 import java.io.Serializable; 10 cananian 1.10.2.7 import java.util.ArrayList; 11 cananian 1.10.2.7 import java.util.HashSet; 12 cananian 1.10.2.7 import java.util.Iterator; 13 cananian 1.10.2.7 import java.util.List; 14 cananian 1.10.2.7 import java.util.Set; 15 cananian 1.1 import java.util.zip.ZipFile; 16 cananian 1.1 import java.util.zip.ZipEntry; 17 cananian 1.1 18 cananian 1.10.2.13 import java.io.BufferedInputStream; 19 cananian 1.1 import java.io.IOException; 20 cananian 1.1 import java.io.FileNotFoundException; 21 cananian 1.1 22 cananian 1.10.2.11 import harpoon.Util.ArrayIterator; 23 cananian 1.16 import net.cscott.jutil.CombineIterator; 24 cananian 1.16 import net.cscott.jutil.Default; 25 cananian 1.10.2.11 import harpoon.Util.EnumerationIterator; 26 cananian 1.16 import net.cscott.jutil.FilterIterator; 27 cananian 1.10.2.2 import harpoon.Util.Util; 28 cananian 1.15 29 cananian 1.16 import net.cscott.jutil.UnmodifiableIterator; 30 cananian 1.15 31 cananian 1.6 /** 32 cananian 1.10.2.3 * Class file loader. 33 cananian 1.10.2.3 * Looks through CLASSPATH to find resources. Understands .jar and .zip 34 cananian 1.10.2.3 * files. Platform-independent (hopefully). 35 cananian 1.2 * 36 cananian 1.6 * @author C. Scott Ananian <cananian@alumni.princeton.edu> 37 cananian 1.17 * @version $Id: Loader.java,v 1.17 2004/02/08 03:21:10 cananian Exp $ 38 cananian 1.1 */ 39 cananian 1.9 public abstract class Loader { 40 cananian 1.10.2.2 static abstract class ClasspathElement { 41 cananian 1.10.2.3 /** Open a stream to read the given resource, or return 42 cananian 1.10.2.3 * <code>null</code> if resource cannot be found. */ 43 cananian 1.10.2.2 abstract InputStream getResourceAsStream(String resourcename); 44 cananian 1.10.2.11 /** Iterate over all classes in the given package. */ 45 cananian 1.10.2.11 abstract Iterator listPackage(String packagename); 46 cananian 1.10.2.2 } 47 cananian 1.10.2.2 /** A .zip or .jar file in the CLASSPATH. */ 48 cananian 1.10.2.2 static class ZipFileElement extends ClasspathElement { 49 cananian 1.10.2.2 ZipFile zf; 50 cananian 1.10.2.2 ZipFileElement(ZipFile zf) { this.zf = zf; } 51 cananian 1.10.2.14 public String toString() { return zf.getName(); } 52 cananian 1.10.2.2 InputStream getResourceAsStream(String name) { 53 cananian 1.10.2.16 name = name.replace('\\','/'); // work around bug in windows java ports 54 cananian 1.10.2.2 try { // look for name in zipfile, return null if something goes wrong. 55 cananian 1.10.2.2 ZipEntry ze = zf.getEntry(name); 56 cananian 1.10.2.2 return (ze==null)?null:zf.getInputStream(ze); 57 cananian 1.10.2.10 } catch (UnsatisfiedLinkError e) { 58 cananian 1.10.2.10 System.err.println("UNSATISFIED LINK ERROR: "+name); 59 cananian 1.10.2.10 return null; 60 cananian 1.10.2.2 } catch (IOException e) { return null; } 61 cananian 1.10.2.2 } 62 cananian 1.10.2.11 Iterator listPackage(final String pathname) { 63 cananian 1.10.2.11 // look for directory name first 64 cananian 1.10.2.11 final String filesep = System.getProperty("file.separator"); 65 cananian 1.10.2.11 /* not all .JAR files have entries for directories, unfortunately. 66 cananian 1.10.2.11 ZipEntry ze = zf.getEntry(pathname); 67 cananian 1.10.2.11 if (ze==null) return Default.nullIterator; 68 cananian 1.10.2.11 */ 69 cananian 1.10.2.11 return new FilterIterator(new EnumerationIterator(zf.entries()), 70 cananian 1.10.2.11 new FilterIterator.Filter() { 71 cananian 1.10.2.11 public boolean isElement(Object o) { ZipEntry zze=(ZipEntry) o; 72 cananian 1.10.2.11 String name = zze.getName(); 73 cananian 1.10.2.11 return (!zze.isDirectory()) && name.startsWith(pathname) && 74 cananian 1.10.2.11 name.lastIndexOf(filesep)==(pathname.length()-1); 75 cananian 1.10.2.11 } 76 cananian 1.10.2.11 public Object map(Object o) { 77 cananian 1.10.2.11 return ((ZipEntry)o).getName(); 78 cananian 1.7 } 79 cananian 1.10.2.11 }); 80 cananian 1.10.2.11 } 81 cananian 1.10.2.2 /** Close the zipfile when this object is garbage-collected. */ 82 cananian 1.10.2.2 protected void finalize() throws Throwable { 83 cananian 1.10.2.6 // yes, it is possible to finalize an uninitialized object. 84 cananian 1.10.2.6 try { if (zf!=null) zf.close(); } finally { super.finalize(); } 85 cananian 1.10.2.2 } 86 cananian 1.10.2.2 } 87 cananian 1.10.2.2 /** A regular path string in the CLASSPATH. */ 88 cananian 1.10.2.2 static class PathElement extends ClasspathElement { 89 cananian 1.10.2.2 String path; 90 cananian 1.10.2.2 PathElement(String path) { this.path = path; } 91 cananian 1.10.2.14 public String toString() { return path; } 92 cananian 1.10.2.2 InputStream getResourceAsStream(String name) { 93 cananian 1.10.2.2 try { // try to open the file, starting from path. 94 cananian 1.10.2.2 File f = new File(path, name); 95 cananian 1.10.2.5 return new FileInputStream(f); 96 cananian 1.10.2.2 } catch (FileNotFoundException e) { 97 cananian 1.10.2.2 return null; // if anything goes wrong, return null. 98 cananian 1.7 } 99 cananian 1.10.2.2 } 100 cananian 1.10.2.11 Iterator listPackage(final String pathname) { 101 cananian 1.10.2.11 File f = new File(path,pathname); 102 cananian 1.10.2.11 if (!f.exists() || !f.isDirectory()) return Default.nullIterator; 103 cananian 1.10.2.11 return new FilterIterator(new ArrayIterator(f.list()), 104 cananian 1.10.2.11 new FilterIterator.Filter() { 105 cananian 1.10.2.11 public Object map(Object o) { return pathname + ((String)o); } 106 cananian 1.10.2.11 }); 107 cananian 1.10.2.11 } 108 cananian 1.10.2.2 } 109 cananian 1.10.2.2 110 cananian 1.10.2.2 /** Static vector of ClasspathElements corresponding to CLASSPATH entries. */ 111 cananian 1.10.2.7 static final List classpathList = new ArrayList(); 112 cananian 1.10.2.2 static { // initialize classpathVector. 113 cananian 1.10.2.7 Set duplicates = new HashSet(); // don't add duplicates. 114 cananian 1.10.2.7 for (Iterator it = classpaths(); it.hasNext(); ) { 115 cananian 1.10.2.7 String path = (String) it.next(); 116 cananian 1.10.2.7 if (duplicates.contains(path)) continue; // skip duplicate. 117 cananian 1.10.2.7 else duplicates.add(path); 118 cananian 1.10.2.2 if (path.toLowerCase().endsWith(".zip") || 119 cananian 1.10.2.2 path.toLowerCase().endsWith(".jar")) 120 cananian 1.10.2.2 try { 121 cananian 1.10.2.7 classpathList.add(new ZipFileElement(new ZipFile(path))); 122 cananian 1.10.2.2 } catch (IOException ex) { /* skip this zip file, then. */ } 123 cananian 1.10.2.2 else 124 cananian 1.10.2.7 classpathList.add(new PathElement(path)); 125 cananian 1.10.2.2 } 126 cananian 1.10.2.7 ((ArrayList) classpathList).trimToSize(); // save memory. 127 cananian 1.7 } 128 cananian 1.10.2.2 129 cananian 1.10.2.7 /** Iterate over the components of the system CLASSPATH. 130 cananian 1.10.2.3 * Each element is a <code>String</code> naming one segment of the 131 cananian 1.10.2.3 * CLASSPATH. */ 132 cananian 1.10.2.7 public static final Iterator classpaths() { 133 cananian 1.10.2.4 final String pathsep = System.getProperty("path.separator"); 134 cananian 1.10.2.9 String classpath = null; 135 cananian 1.10.2.9 136 cananian 1.10.2.9 // allow overriding classpath. 137 cananian 1.10.2.9 /*if (classpath==null) classpath = System.getenv("HCLASSPATH");*/ 138 cananian 1.10.2.9 if (classpath==null) classpath = System.getProperty("harpoon.class.path"); 139 cananian 1.10.2.9 if (classpath==null) classpath = System.getProperty("java.class.path"); 140 cananian 1.13.2.1 assert classpath!=null; 141 cananian 1.7 142 cananian 1.7 // For convenience, make sure classpath begins with and ends with pathsep. 143 cananian 1.7 if (!classpath.startsWith(pathsep)) classpath = pathsep + classpath; 144 cananian 1.7 if (!classpath.endsWith(pathsep)) classpath = classpath + pathsep; 145 cananian 1.7 final String cp = classpath; 146 cananian 1.7 147 cananian 1.10.2.7 return new UnmodifiableIterator() { 148 cananian 1.7 int i=0; 149 cananian 1.10.2.7 public boolean hasNext() { 150 cananian 1.7 return (cp.length() > (i+pathsep.length())); 151 cananian 1.7 } 152 cananian 1.10.2.7 public Object next() { 153 cananian 1.7 i+=pathsep.length(); // cp begins with pathsep. 154 cananian 1.7 String path = cp.substring(i, cp.indexOf(pathsep, i)); 155 cananian 1.7 i+=path.length(); // skip over path. 156 cananian 1.7 return path; 157 cananian 1.7 } 158 cananian 1.7 }; 159 cananian 1.7 } 160 cananian 1.7 161 cananian 1.10.2.3 /** Translate a class name into a corresponding resource name. 162 cananian 1.10.2.3 * @param classname The class name to translate. 163 cananian 1.10.2.3 */ 164 cananian 1.1 public static String classToResource(String classname) { 165 cananian 1.13.2.1 assert classname.indexOf('/')==-1; // should have '.' separators. 166 cananian 1.1 String filesep = System.getProperty("file.separator"); 167 cananian 1.1 // Swap all '.' for '/' & append ".class" 168 cananian 1.1 return classname.replace('.', filesep.charAt(0)) + ".class"; 169 cananian 1.1 } 170 cananian 1.1 171 cananian 1.10.2.3 /** Open an <code>InputStream</code> on a resource found somewhere 172 cananian 1.10.2.3 * in the CLASSPATH. 173 cananian 1.1 * @param name The filename of the resource to locate. 174 cananian 1.1 */ 175 cananian 1.1 public static InputStream getResourceAsStream(String name) { 176 cananian 1.17 for (Object cpeO : classpathList) { 177 cananian 1.17 ClasspathElement cpe = (ClasspathElement) cpeO; 178 cananian 1.10.2.2 InputStream is = cpe.getResourceAsStream(name); 179 cananian 1.10.2.14 if (is!=null) { 180 cananian 1.10.2.14 //System.err.println("[LOADING "+new File(cpe.toString(),name)+"]"); 181 cananian 1.10.2.14 return is; // return stream if found. 182 cananian 1.10.2.14 } 183 cananian 1.1 } 184 cananian 1.1 // Couldn't find resource. 185 cananian 1.1 return null; 186 cananian 1.1 } 187 cananian 1.1 188 cananian 1.10.2.11 /** Returns an iterator of Strings naming the available classes in 189 cananian 1.10.2.11 * the given package which are on the classpath. */ 190 cananian 1.10.2.11 public static Iterator listClasses(String packagename) { 191 cananian 1.10.2.11 final String filesep = System.getProperty("file.separator"); 192 cananian 1.10.2.12 final String pathname = (packagename.length()==0)?"": 193 cananian 1.10.2.12 (packagename.replace('.',filesep.charAt(0))+filesep); 194 cananian 1.10.2.11 FilterIterator.Filter cpe2sl = new FilterIterator.Filter() { 195 cananian 1.10.2.11 public Object map(Object o) { 196 cananian 1.10.2.12 return ((ClasspathElement) o).listPackage(pathname); 197 cananian 1.10.2.11 } 198 cananian 1.10.2.11 }; 199 cananian 1.10.2.11 FilterIterator.Filter sl2cl = new FilterIterator.Filter() { 200 cananian 1.10.2.11 private Set nodups = new HashSet(); 201 cananian 1.10.2.11 public boolean isElement(Object o) { 202 cananian 1.10.2.11 return ((String)o).toLowerCase().endsWith(".class") && 203 cananian 1.10.2.11 !nodups.contains(o); 204 cananian 1.10.2.11 } 205 cananian 1.10.2.11 public Object map(Object o) { 206 cananian 1.10.2.11 String name = (String) o; nodups.add(o); 207 cananian 1.10.2.11 return name.substring(0,name.length()-6) 208 cananian 1.10.2.11 .replace(filesep.charAt(0),'.'); 209 cananian 1.1 } 210 cananian 1.10.2.11 }; 211 cananian 1.10.2.11 return new FilterIterator(new CombineIterator( 212 cananian 1.10.2.11 new FilterIterator(classpathList.iterator(), cpe2sl)), sl2cl); 213 cananian 1.1 } 214 cananian 1.1 215 cananian 1.10.2.13 /** System-linker: the class names resolved by this linker are always 216 cananian 1.10.2.13 * immutable and identical to those on disk. 217 cananian 1.10.2.13 */ 218 cananian 1.10.2.13 public static final Linker systemLinker = new SystemLinker(); 219 cananian 1.10.2.13 private static class SystemLinker extends Linker implements Serializable { 220 cananian 1.10.2.13 protected final HClass forDescriptor0(String descriptor) 221 cananian 1.10.2.13 throws NoSuchClassException { 222 cananian 1.13.2.1 assert descriptor.startsWith("L") && descriptor.endsWith(";"); 223 cananian 1.10.2.13 // classname in descriptor is '/' delimited. 224 cananian 1.10.2.13 String className = descriptor.substring(1, descriptor.indexOf(';')); 225 cananian 1.10.2.13 className = className.replace('/','.'); // make proper class name. 226 cananian 1.10.2.13 InputStream is = 227 cananian 1.10.2.13 Loader.getResourceAsStream(Loader.classToResource(className)); 228 cananian 1.10.2.13 if (is == null) throw new NoSuchClassException(className); 229 cananian 1.10.2.13 // OK, go ahead and load this. 230 cananian 1.10.2.13 try { 231 cananian 1.10.2.13 return /*ImplGNU*/ImplMagic.forStream(this, new BufferedInputStream(is)); 232 cananian 1.10.2.13 } catch (java.lang.ClassFormatError e) { 233 cananian 1.10.2.13 throw new NoSuchClassException(className+" ["+e.toString()+"]"); 234 cananian 1.10.2.13 } catch (java.io.IOException e) { 235 cananian 1.10.2.13 throw new NoSuchClassException(className); 236 cananian 1.10.2.13 } finally { 237 cananian 1.10.2.13 try { is.close(); } catch(java.io.IOException e) { } 238 cananian 1.10.2.13 } 239 cananian 1.10.2.13 } 240 cananian 1.10.2.13 /* Serializable interface: system linker is unique. */ 241 cananian 1.10.2.13 public Object writeReplace() { return new Stub(); } 242 cananian 1.10.2.13 private static final class Stub implements Serializable { 243 cananian 1.10.2.15 public Object readResolve() { 244 cananian 1.10.2.15 return Loader.replaceWithRelinker ? 245 cananian 1.10.2.15 Loader.systemRelinker : Loader.systemLinker; 246 cananian 1.10.2.15 } 247 cananian 1.1 } 248 cananian 1.1 } 249 cananian 1.10.2.15 // allow the user to reserialize a systemLinker (and associated classes) 250 cananian 1.10.2.15 // as a *relinker*... evil evil evil evil. [CSA 4-apr-2000] 251 cananian 1.10.2.15 final static boolean replaceWithRelinker = 252 cananian 1.10.2.15 System.getProperty("harpoon.relinker.hack", "no") 253 cananian 1.10.2.15 .equalsIgnoreCase("yes"); 254 cananian 1.10.2.15 final static Linker systemRelinker = new Relinker(systemLinker); 255 cananian 1.10.2.15 256 cananian 1.10.2.13 /** System code factory: this code factory will return bytecode 257 cananian 1.10.2.13 * representations for classes loaded via the system linker. */ 258 cananian 1.10.2.13 public static final HCodeFactory systemCodeFactory = 259 cananian 1.10.2.13 /*ImplGNU*/ImplMagic.codeFactory; 260 cananian 1.1 } 261 cananian 1.10.2.2 // set emacs indentation style. 262 cananian 1.10.2.2 // Local Variables: 263 cananian 1.10.2.2 // c-basic-offset:2 264 cananian 1.12 // End: