diff options
author | tromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-11-25 03:47:08 +0000 |
---|---|---|
committer | tromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-11-25 03:47:08 +0000 |
commit | a4ccc41f9a5f050d518b8c30739a647f67756f9e (patch) | |
tree | 477abdf83653e20b0e74447d6ca47eb67b0511b8 /libjava/gnu | |
parent | 2f3c6e08b9d664df3e416a186fd2938de188e706 (diff) | |
download | gcc-a4ccc41f9a5f050d518b8c30739a647f67756f9e.tar.gz |
* Merged gcj-abi-2-dev-branch to trunk.
(Actual changes too large to list in the commit message;
see ChangeLog.)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@91270 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/gnu')
-rw-r--r-- | libjava/gnu/gcj/runtime/PersistentByteMap.java | 484 | ||||
-rw-r--r-- | libjava/gnu/gcj/runtime/SharedLibHelper.java | 98 | ||||
-rw-r--r-- | libjava/gnu/gcj/runtime/VMClassLoader.java | 3 | ||||
-rw-r--r-- | libjava/gnu/gcj/runtime/natSharedLibLoader.cc | 13 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gcj_dbtool/Main.java | 272 |
5 files changed, 852 insertions, 18 deletions
diff --git a/libjava/gnu/gcj/runtime/PersistentByteMap.java b/libjava/gnu/gcj/runtime/PersistentByteMap.java new file mode 100644 index 00000000000..90b7d33a8f4 --- /dev/null +++ b/libjava/gnu/gcj/runtime/PersistentByteMap.java @@ -0,0 +1,484 @@ +/* Copyright (C) 2004 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + + + +/* A PersistentByteMap maps a byte array to another byte array. It +uses a file that does not need to be serialized but may be +memory-mapped and read in-place. So, even if there are many instances +of gcj applications running, the can share PersistentByteMaps. + +The idea is to make searches as fast as possible: opening a +PersistentByteMap is cheap and search time doesn't grow with the +number of entries in the table. On the other hand, enumerating the +map is slow, but that is a relatively uncommon operation. + +The main use of this class is to provide a way to map the +MessageDigest of a class file to the location of a DSO that contains +the compiled version of that class. It is up the the installer of an +application to keep the DSO up to date with the jar. + +USAGE: + MessageDigest md = MessageDigest.getInstance("MD5"); + digest = md.digest(bytes); + + PersistentByteMap map + = new PersistentByteMap + (fileName, PersistentByteMap.AccessMode.READ_ONLY); + + byte[] soName = map.get(digest); + if (soName) + { + String SharedLibraryName = new String(soName); + +BUGS/FEATURES: + remove() isn't written yet. + + we can't change the capacity of a PersistentByteMap. + + 0x12345678 is a bad choice for the magic number. + + capacity is fixed once the map has been created. + + We use linear probing to resolve collisions. It might be + better to use a scheme that results in fewer probes to + determine that an item isn't found. However, even when the + table is half full there are only on average 1.5 probes for a + successful search and 2.5 probes for an unsuccessful one. + + We don't use unique strings. This wastes space. + + capacity should probably be prime, but we don't check that. + + we don't do any locking at all: adding to a PersistentByteMap + at runtime is possible, but it requires filesystem locks + around get(), put(), and remove(). +*/ + +package gnu.gcj.runtime; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.security.MessageDigest; + +public class PersistentByteMap +{ + private MappedByteBuffer buf; + + static private final int MAGIC = 0; + static private final int VERSION = 4; + static private final int CAPACITY = 8; + static private final int TABLE_BASE = 12; + static private final int STRING_BASE = 16; + static private final int STRING_SIZE = 20; + static private final int FILE_SIZE = 24; + static private final int ELEMENTS = 28; + + static private final int INT_SIZE = 4; + + static private final int TABLE_ENTRY_SIZE = 2 * INT_SIZE; + + private int capacity; // number of entries + private int table_base; // offset from start of file, in bytes + private int string_base; // offset from start of file, in bytes + private int string_size; // size of string table, in bytes + private int file_size; // size of file, in bytes; + private int elements; // number of elements in table + + private long length; // the length of the underlying file + + static private final int UNUSED_ENTRY = -1; + + static public final int KEYS = 0; + static public final int VALUES = 1; + static public final int ENTRIES = 2; + + static final public class AccessMode + { + private final FileChannel.MapMode mapMode; + + static + { + READ_ONLY = new AccessMode(FileChannel.MapMode.READ_ONLY); + READ_WRITE = new AccessMode(FileChannel.MapMode.READ_WRITE); + } + + public static final AccessMode READ_ONLY; + public static final AccessMode READ_WRITE; + + private AccessMode(FileChannel.MapMode mode) + { + this.mapMode = mode; + } + } + + private PersistentByteMap() + { + } + + public PersistentByteMap(String filename, AccessMode mode) + throws IOException + { + this(new File(filename), mode); + } + + public PersistentByteMap(File f, AccessMode mode) + throws IOException + { + FileChannel fc; + + if (mode == AccessMode.READ_ONLY) + { + FileInputStream fis = new FileInputStream(f); + fc = fis.getChannel(); + } + else + { + RandomAccessFile fos = new RandomAccessFile(f, "rw"); + fc = fos.getChannel(); + } + + length = fc.size(); + buf = fc.map(mode.mapMode, 0, length); + + int magic = getWord (MAGIC); + if (magic != 0x12345678) + throw new IllegalArgumentException(f.getName()); + + table_base = getWord (TABLE_BASE); + capacity = getWord (CAPACITY); + string_base = getWord (STRING_BASE); + string_size = getWord (STRING_SIZE); + file_size = getWord (FILE_SIZE); + elements = getWord (ELEMENTS); + + // FIXME: Insert a bunch of sanity checks here + } + + private void init (PersistentByteMap m, File f, int capacity, int strtabSize) + throws IOException + { + f.createNewFile(); + RandomAccessFile raf = new RandomAccessFile(f, "rw"); + + this.capacity = capacity; + table_base = 64; + string_base = table_base + capacity * TABLE_ENTRY_SIZE; + string_size = 0; + file_size = string_base; + elements = 0; + + int totalFileSize = string_base + strtabSize; + + // Create the file; this rounds up the size of the file to a fixed + // number of 4k pages. + byte[] _4k = new byte[4096]; + for (long i = 0; i < totalFileSize; i+= 4096) + raf.write(_4k); + + FileChannel fc = raf.getChannel(); + buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length()); + + for (int i = 0; i < capacity; i++) + putKeyPos(UNUSED_ENTRY, i); + + putWord(0x12345678, MAGIC); + putWord(0x01, VERSION); + putWord(capacity, CAPACITY); + putWord(table_base, TABLE_BASE); + putWord(string_base, STRING_BASE); + putWord(file_size, FILE_SIZE); + putWord(elements, ELEMENTS); + buf.force(); + } + + static public PersistentByteMap emptyPersistentByteMap(String filename, + int capacity, int strtabSize) + throws IOException + { + File f = new File(filename); + PersistentByteMap m = new PersistentByteMap(); + m.init(m, f, capacity, strtabSize); + return m; + } + + private int getWord (int index) + { + buf.position(index); + byte[] wordBuf = new byte[4]; + buf.get(wordBuf); + + int result = (int)wordBuf[0]&0xff; + result += ((int)wordBuf[1]&0xff) << 8; + result += ((int)wordBuf[2]&0xff) << 16; + result += ((int)wordBuf[3]&0xff) << 24; + return result; + } + + private void putWord (int word, int index) + { + buf.position(index); + byte[] wordBuf = new byte[4]; + wordBuf[0] = (byte)(word); + wordBuf[1] = (byte)(word >>> 8); + wordBuf[2] = (byte)(word >>> 16); + wordBuf[3] = (byte)(word >>> 24); + buf.put(wordBuf); + } + + public Set entrySet() + { + return null; + } + + private int getBucket(int n) + { + return table_base + (2*n * INT_SIZE); + } + + private int getKeyPos(int n) + { + return getWord(getBucket(n)); + } + + private int getValuePos(int n) + { + return getWord(getBucket(n) + INT_SIZE); + } + + private void putKeyPos(int index, int n) + { + putWord(index, getBucket(n)); + } + + private void putValuePos(int index, int n) + { + putWord(index, getBucket(n) + INT_SIZE); + } + + private byte[] getBytes(int n) + { + int len = getWord (string_base + n); + int base = string_base + n + INT_SIZE; + byte[] key = new byte[len]; + buf.position(base); + buf.get(key, 0, len); + return key; + } + + private int hash (byte[] b) + { + // We assume that the message digest is evenly distributed, so we + // only need to use a few bytes of it as the hash function. + long hashIndex + = ((b[0]&0xffL) + + ((b[1]&0xffL)<<8) + + ((b[2]&0xffL)<<16) + + ((b[3]&0xffL)<<24)); + long result = hashIndex % (long)capacity; + return (int)result; + } + + public byte[] get(byte[] digest) + { + int hashIndex = hash(digest); + + do + { + int k = getKeyPos(hashIndex); + if (k == UNUSED_ENTRY) + return null; + + if (Arrays.equals ((byte[])digest, getBytes(k))) + return getBytes(getValuePos(hashIndex)); + + // Use linear probing to resolve hash collisions. This may + // not be theoretically as good as open addressing, but it has + // good cache behviour. + hashIndex++; + hashIndex %= capacity; + } + while (true); + } + + public void put(byte[] digest, byte[] value) + throws IllegalAccessException + { + int hashIndex = hash(digest); + + // With the the table 2/3 full there will be on average 2 probes + // for a successful search and 5 probes for an unsuccessful one. + if (elements >= capacity * 2/3) + throw new IllegalAccessException("Table Full: " + elements); + + do + { + int k = getKeyPos(hashIndex); + if (k == UNUSED_ENTRY) + { + int newKey = addBytes(digest); + putKeyPos(newKey, hashIndex); + int newValue = addBytes(value); + putValuePos(newValue, hashIndex); + elements++; + putWord(elements, ELEMENTS); + return; + } + else if (Arrays.equals (digest, getBytes(k))) + { + int newValue = addBytes((byte[])value); + putValuePos(newValue, hashIndex); + return; + } + + hashIndex++; + hashIndex %= capacity; + } + while (true); + } + + private int addBytes (byte[] data) + throws IllegalAccessException + { + if (data.length + INT_SIZE >= this.length) + throw new IllegalAccessException("String table Full"); + + int extent = string_base+string_size; + int top = extent; + putWord(data.length, extent); + extent += INT_SIZE; + buf.position(extent); + buf.put(data, 0, data.length); + extent += data.length; + extent += INT_SIZE-1; + extent &= ~(INT_SIZE-1); // align + string_size = extent - string_base; + file_size = extent; + putWord (string_size, STRING_SIZE); + putWord (file_size, FILE_SIZE); + + return top - string_base; + } + + public Iterator iterator(int type) + { + return new HashIterator(type); + } + + public int size() + { + return elements; + } + + public int capacity() + { + return capacity; + } + + private final class HashIterator implements Iterator + { + /** Current index in the physical hash table. */ + + private int idx; + private int count; + private final int type; + + /** + * Construct a new HashIterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + HashIterator(int type) + { + this.type = type; + count = elements; + idx = 0; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + * @throws ConcurrentModificationException if the HashMap was modified + */ + public boolean hasNext() + { + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the HashMap was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + count--; + for (int i = idx; i < capacity; i++) + if (getKeyPos(i) != UNUSED_ENTRY) + { + idx = i+1; + if (type == VALUES) + return getBytes(getValuePos(i)); + if (type == KEYS) + return getBytes(getKeyPos(i)); + return new MapEntry(i, + getBytes(getKeyPos(i)), + getBytes(getValuePos(i))); + } + return null; + } + + /** + * Remove from the underlying collection the last element returned + * by next (optional operation). This method can be called only + * once after each call to <code>next()</code>. It does not affect + * what will be returned by subsequent calls to next. + * + * @throws IllegalStateException if next has not yet been called + * or remove has already been called since the last call + * to next. + * @throws UnsupportedOperationException if this Iterator does not + * support the remove operation. + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + static public final class MapEntry + { + private final Object key; + private final Object value; + private final int bucket; + + public MapEntry(int bucket, Object newKey, Object newValue) + { + this.key = newKey; + this.value = newValue; + this.bucket = bucket; + } + + public final Object getKey() + { + return key; + } + + public final Object getValue() + { + return value; + } + + public final int getBucket() + { + return bucket; + } + } +} diff --git a/libjava/gnu/gcj/runtime/SharedLibHelper.java b/libjava/gnu/gcj/runtime/SharedLibHelper.java index 9e170a120be..a0bfe68b83c 100644 --- a/libjava/gnu/gcj/runtime/SharedLibHelper.java +++ b/libjava/gnu/gcj/runtime/SharedLibHelper.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2003 Free Software Foundation +/* Copyright (C) 2001, 2003, 2004 Free Software Foundation This file is part of libgcj. @@ -13,6 +13,12 @@ import java.net.MalformedURLException; import java.util.HashMap; import java.security.*; import gnu.gcj.Core; +import java.util.Set; +import java.util.Iterator; +import java.util.HashSet; +import java.util.HashMap; +import java.nio.channels.FileChannel; +import java.io.*; public class SharedLibHelper { @@ -36,34 +42,82 @@ public class SharedLibHelper { synchronized (map) { - WeakReference ref = (WeakReference) map.get(libname); - if (ref != null) - return (SharedLibHelper) ref.get(); + Set s = (Set)map.get(libname); + if (s == null) + return null; + for (Iterator i=s.iterator(); i.hasNext();) + { + WeakReference ref = (WeakReference)i.next(); + if (ref != null) + return (SharedLibHelper) ref.get(); + } return null; } } + static void copyFile (File in, File out) throws IOException + { + FileChannel source = new FileInputStream(in).getChannel(); + FileChannel destination = new FileOutputStream(out).getChannel(); + source.transferTo(0, source.size(), destination); + source.close(); + destination.close(); + } + public static SharedLibHelper findHelper (ClassLoader loader, String libname, CodeSource source) { synchronized (map) { SharedLibHelper result; - WeakReference ref = (WeakReference) map.get(libname); - if (ref != null) + Set s = (Set)map.get(libname); + if (s == null) + { + s = new HashSet(); + map.put(libname, s); + } + else { - result = (SharedLibHelper) ref.get(); - if (result != null) + for (Iterator i=s.iterator(); i.hasNext();) { - if (result.loader != loader) - // FIXME - throw new UnknownError(); - return result; + WeakReference ref = (WeakReference)i.next(); + if (ref != null) + { + result = (SharedLibHelper) ref.get(); + if (result != null) + { + // A match succeeds if the library is already + // loaded by LOADER or any of its ancestors. + ClassLoader l = loader; + do + { + if (result.loader == l) + return result; + l = l.getParent(); + } + while (l != null); + } + } } - } + // Oh dear. We've already mapped this shared library, but + // with a different class loader. We need to copy it. + try + { + File copy + = File.createTempFile(new File(libname).getName(), + ".so", new File ("/tmp")); + File src = new File(libname); + copyFile (src, copy); + libname = copy.getPath(); + } + catch (IOException e) + { + return null; + } + } result = new SharedLibHelper(libname, loader, source, 0); - map.put(libname, new WeakReference(result)); + s.add(new WeakReference(result)); return result; } } @@ -73,7 +127,15 @@ public class SharedLibHelper public Class findClass(String name) { ensureInit(); - return (Class) classMap.get(name); + Class result = (Class) classMap.get(name); + if (result != null) + { + // We never want to return a class without its supers linked. + // It isn't clear from the spec, but this is what other + // implementations do in practice. + ensureSupersLinked(result); + } + return result; } public URL findResource (String name) @@ -106,6 +168,12 @@ public class SharedLibHelper native boolean hasResource(String name); native void init(); + native void ensureSupersLinked(Class k); + + public String toString () + { + return "shared object " + baseName; + } /** Called during dlopen's processing of the init section. */ void registerClass(String name, Class cls) diff --git a/libjava/gnu/gcj/runtime/VMClassLoader.java b/libjava/gnu/gcj/runtime/VMClassLoader.java index 1fc7081bb78..f9ef872b30a 100644 --- a/libjava/gnu/gcj/runtime/VMClassLoader.java +++ b/libjava/gnu/gcj/runtime/VMClassLoader.java @@ -105,7 +105,8 @@ public final class VMClassLoader extends java.net.URLClassLoader /** This is overridden to search the internal hash table, which * will only search existing linked-in classes. This will make * the default implementation of loadClass (in ClassLoader) work right. - * The implementation of this method is in java/lang/natClassLoader.cc. + * The implementation of this method is in + * gnu/gcj/runtime/natVMClassLoader.cc. */ protected native Class findClass(String name) throws java.lang.ClassNotFoundException; diff --git a/libjava/gnu/gcj/runtime/natSharedLibLoader.cc b/libjava/gnu/gcj/runtime/natSharedLibLoader.cc index 46eef755a09..184692e1040 100644 --- a/libjava/gnu/gcj/runtime/natSharedLibLoader.cc +++ b/libjava/gnu/gcj/runtime/natSharedLibLoader.cc @@ -1,6 +1,6 @@ // natSharedLibLoader.cc - Implementation of SharedLibHelper native methods. -/* Copyright (C) 2001, 2003 Free Software Foundation +/* Copyright (C) 2001, 2003, 2004 Free Software Foundation This file is part of libgcj. @@ -12,6 +12,8 @@ details. */ #include <gcj/cni.h> #include <jvm.h> +#include <execution.h> + #include <gnu/gcj/runtime/SharedLibHelper.h> #include <java/io/IOException.h> #include <java/lang/UnsupportedOperationException.h> @@ -30,9 +32,10 @@ typedef void (*CoreHookFunc) (_Jv_core_chain *); void _Jv_sharedlib_register_hook (jclass cls) { - curHelper->registerClass(cls->getName(), cls); cls->protectionDomain = curHelper->domain; cls->loader = curLoader; + cls->engine = &_Jv_soleCompiledEngine; + curHelper->registerClass(cls->getName(), cls); } static void @@ -122,3 +125,9 @@ gnu::gcj::runtime::SharedLibHelper::finalize() dlclose (handler); #endif } + +void +gnu::gcj::runtime::SharedLibHelper::ensureSupersLinked(jclass k) +{ + _Jv_Linker::wait_for_state (k, JV_STATE_LOADING); +} diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/Main.java b/libjava/gnu/gcj/tools/gcj_dbtool/Main.java new file mode 100644 index 00000000000..a478f934297 --- /dev/null +++ b/libjava/gnu/gcj/tools/gcj_dbtool/Main.java @@ -0,0 +1,272 @@ +/* Copyright (C) 2004 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gcj_dbtool; + + +import gnu.gcj.runtime.PersistentByteMap; +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.security.MessageDigest; +import java.math.BigInteger; + +public class Main +{ + public static void main (String[] s) + { + insist (s.length >= 1); + if (s[0].equals("-v")) + { + insist (s.length == 1); + System.out.println("jv-dbtool (" + + System.getProperty("java.vm.name") + + ") " + + System.getProperty("java.vm.version")); + System.out.println(); + System.out.println("Copyright 2004 Free Software Foundation, Inc."); + System.out.println("This is free software; see the source for copying conditions. There is NO"); + System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."); + return; + } + + if (s[0].equals("-n")) + { + insist (s.length >= 2 && s.length <= 3); + + int capacity = 32749; + + if (s.length == 3) + { + // The user has explicitly provided a size for the table. + // We're going to make that size prime. This isn't + // strictly necessary but it can't hurt. + + BigInteger size = new BigInteger(s[2], 10); + BigInteger two = BigInteger.ONE.add(BigInteger.ONE); + + if (size.getLowestSetBit() != 0) // A hard way to say isEven() + size = size.add(BigInteger.ONE); + + while (! size.isProbablePrime(10)) + size = size.add(two); + + capacity = size.intValue(); + + if (capacity <= 2) + { + usage(); + System.exit(1); + } + } + + try + { + PersistentByteMap b + = PersistentByteMap.emptyPersistentByteMap (s[1], capacity, capacity*64); + } + catch (Exception e) + { + System.err.println ("error: could not create " + + s[1] + ": " + e.toString()); + System.exit(2); + } + return; + } + + if (s[0].equals("-a")) + { + try + { + insist (s.length == 4); + File jar = new File(s[2]); + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_WRITE); + File soFile = new File(s[3]); + if (! soFile.isFile()) + throw new IllegalArgumentException(s[3] + " is not a file"); + + addJar(jar, b, soFile); + } + catch (Exception e) + { + System.err.println ("error: could not update " + s[1] + + ": " + e.toString()); + System.exit(2); + } + return; + } + + if (s[0].equals("-t")) + { + try + { + insist (s.length == 2); + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_ONLY); + Iterator iterator = b.iterator(PersistentByteMap.ENTRIES); + + while (iterator.hasNext()) + { + PersistentByteMap.MapEntry entry + = (PersistentByteMap.MapEntry)iterator.next(); + byte[] key = (byte[])entry.getKey(); + byte[] value = (byte[])b.get(key); + if (! Arrays.equals (value, (byte[])entry.getValue())) + { + String err + = ("Key " + bytesToString(key) + " at bucket " + + entry.getBucket()); + + throw new RuntimeException(err); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + System.exit(3); + } + return; + } + + if (s[0].equals("-l")) + { + insist (s.length == 2); + try + { + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_ONLY); + + System.out.println ("Capacity: " + b.capacity()); + System.out.println ("Size: " + b.size()); + System.out.println (); + + System.out.println ("Elements: "); + Iterator iterator = b.iterator(PersistentByteMap.ENTRIES); + + while (iterator.hasNext()) + { + PersistentByteMap.MapEntry entry + = (PersistentByteMap.MapEntry)iterator.next(); + byte[] digest = (byte[])entry.getKey(); + System.out.print ("[" + entry.getBucket() + "] " + + bytesToString(digest) + + " -> "); + System.out.println (new String((byte[])entry.getValue())); + } + } + catch (Exception e) + { + System.err.println ("error: could not list " + + s[1] + ": " + e.toString()); + System.exit(2); + } + return; + } + + if (s[0].equals("-d")) + { + insist (s.length == 2); + try + { + MessageDigest md = MessageDigest.getInstance("MD5"); + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_WRITE); + int N = b.capacity(); + byte[] bytes = new byte[1]; + byte digest[] = md.digest(bytes); + for (int i = 0; i < N; i++) + { + digest = md.digest(digest); + b.put(digest, digest); + } + } + catch (Exception e) + { + e.printStackTrace(); + System.exit(3); + } + return; + } + + usage(); + System.exit(1); + } + + private static void insist(boolean ok) + { + if (! ok) + { + usage(); + System.exit(1); + } + } + + private static void usage() + { + System.err.println + ("jv-dbtool: Manipulate gcj map database files\n" + + "\n" + + " Usage: \n" + + " jv-dbtool -n file.gcjdb [size] - Create a new gcj map database\n" + + " jv-dbtool -a file.gcjdb file.jar file.so\n" + + " - Add the contents of file.jar to the database\n" + + " jv-dbtool -t file.gcjdb - Test a gcj map database\n" + + " jv-dbtool -l file.gcjdb - List a gcj map database\n"); + } + + + private static void addJar(File f, PersistentByteMap b, File soFile) + throws Exception + { + MessageDigest md = MessageDigest.getInstance("MD5"); + + JarFile jar = new JarFile (f); + Enumeration entries = jar.entries(); + + while (entries.hasMoreElements()) + { + JarEntry classfile = (JarEntry)entries.nextElement(); + if (classfile.getName().endsWith(".class")) + { + InputStream str = jar.getInputStream(classfile); + long length = classfile.getSize(); + if (length == -1) + throw new EOFException(); + + byte[] data = new byte[length]; + int pos = 0; + while (length - pos > 0) + { + int len = str.read(data, pos, (int)(length - pos)); + if (len == -1) + throw new EOFException("Not enough data reading from: " + + classfile.getName()); + pos += len; + } + b.put(md.digest(data), + soFile.getCanonicalPath().getBytes()); + } + } + } + + static String bytesToString(byte[] b) + { + StringBuffer hexBytes = new StringBuffer(); + int length = b.length; + for (int i = 0; i < length; ++i) + hexBytes.append(Integer.toHexString(b[i] & 0xff)); + return hexBytes.toString(); + } +} + |