* Merged gcj-abi-2-dev-branch to trunk.
(Actual changes too large to list in the commit message; see ChangeLog.) From-SVN: r91270
This commit is contained in:
parent
ec0641f612
commit
367390404d
70 changed files with 11301 additions and 3355 deletions
484
libjava/gnu/gcj/runtime/PersistentByteMap.java
Normal file
484
libjava/gnu/gcj/runtime/PersistentByteMap.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
result = (SharedLibHelper) ref.get();
|
||||
if (result != null)
|
||||
s = new HashSet();
|
||||
map.put(libname, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
272
libjava/gnu/gcj/tools/gcj_dbtool/Main.java
Normal file
272
libjava/gnu/gcj/tools/gcj_dbtool/Main.java
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue