/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.tools.bootImageWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Queue;
import org.jikesrvm.classloader.TypeReference;
import org.vmmagic.unboxed.Address;
/**
* Correlate objects in host jdk with corresponding objects in target rvm
* bootimage.
*/
public class BootImageMap extends BootImageWriterMessages
implements BootImageWriterConstants {
/**
* Key->Entry map
*/
private static final Hashtable<Key,Entry> keyToEntry;
/**
* objectId->Entry map
*/
static final ArrayList<Entry> objectIdToEntry;
/**
* Entry used to represent null object
*/
private static final Entry nullEntry;
/**
* Unique ID value
*/
private static int idGenerator;
/**
* Create unique ID number
*/
private static Address newId() {
return Address.fromIntZeroExtend(idGenerator++);
}
/**
* Prepare for use.
*/
static {
keyToEntry = new Hashtable<Key,Entry>(5000);
objectIdToEntry = new ArrayList<Entry>(5000);
idGenerator = 0;
// predefine "null" object
nullEntry = new Entry(newId(), null);
nullEntry.imageAddress = Address.zero();
// slot 0 reserved for "null" object entry
objectIdToEntry.add(nullEntry);
}
/**
* Key for looking up map entry.
*/
private static class Key {
/**
* JDK object.
*/
final Object jdkObject;
/**
* Constructor.
* @param jdkObject the object to associate with the key
*/
public Key(Object jdkObject) { this.jdkObject = jdkObject; }
/**
* Returns a hash code value for the key.
* @return a hash code value for this key
*/
@Override
public int hashCode() { return jdkObject.hashCode(); }
/**
* Indicates whether some other key is "equal to" this one.
* @param that the object with which to compare
* @return true if this key is the same as the that argument;
* false otherwise
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof Key) {
Key that = (Key)obj;
if (jdkObject == that.jdkObject) {
return true;
} else if (jdkObject instanceof String && that.jdkObject instanceof String) {
return jdkObject.equals(that.jdkObject);
} else if (jdkObject instanceof Integer && that.jdkObject instanceof Integer) {
return jdkObject.equals(that.jdkObject);
}
}
return false;
}
}
/**
* Map entry associated with a key.
*/
public static class Entry {
/**
* Unique id associated with a jdk/rvm object pair.
*/
final Address objectId;
/**
* JDK object.
*/
final Object jdkObject;
/**
* Address of corresponding rvm object in bootimage
* (OBJECT_NOT_ALLOCATED --> hasn't been written to image yet)
*/
Address imageAddress;
public static class LinkInfo {
final Address addressToFixup;
final boolean objField;
final boolean root;
final String rvmFieldName;
final TypeReference rvmFieldType;
final Object parent;
LinkInfo(Address a, boolean o, boolean r, String rvmFieldName, TypeReference rvmFieldType, Object parent) {
addressToFixup = a;
objField = o;
root = r;
this.rvmFieldName = rvmFieldName;
this.rvmFieldType = rvmFieldType;
this.parent = parent;
}
}
/**
* A list of addresses where when this value is not OBJECT_NOT_ALLOCATED
*/
private Queue<LinkInfo> linkingAddresses;
private boolean pendingEntry;
/**
* Do we need space in the written object for an identity hash code
*/
private boolean hasIdentityHashCode;
/**
* An identity hash code for this entry
*/
private int identityHashCode;
/**
* Constructor.
* @param objectId unique id
* @param jdkObject the JDK object
* @param imageAddress the address of the object in the bootimage
*/
public Entry(Address objectId, Object jdkObject) {
this.objectId = objectId;
this.jdkObject = jdkObject;
this.imageAddress = OBJECT_NOT_ALLOCATED;
}
boolean isPendingEntry() {
return pendingEntry;
}
void setPendingEntry() {
pendingEntry = true;
}
void clearPendingEntry() {
pendingEntry = false;
}
/**
* Store linking information for an unresolved field
* @param toBeLinked the address that needs filling in when the field is resolved
* @param objField true if this word is an object field (as opposed to a static, or tib, or some other metadata)
* @param root Does this slot contain a possible reference into the heap? (objField must also be true)
* @param rvmFieldName name of the field
* @param rvmFieldType type of the field
* @param parent the object containing the field
*/
synchronized void addLinkingAddress(Address toBeLinked, boolean objField, boolean root, String rvmFieldName, TypeReference rvmFieldType, Object parent) {
if (linkingAddresses == null) {
linkingAddresses = new LinkedList<LinkInfo>();
}
linkingAddresses.add(new LinkInfo(toBeLinked, objField, root, rvmFieldName, rvmFieldType, parent));
}
/**
* @return a queued linking address
*/
synchronized LinkInfo removeLinkingAddress() {
if (linkingAddresses == null) {
return null;
} else {
if (linkingAddresses.peek() != null) {
return linkingAddresses.remove();
} else {
return null;
}
}
}
/**
* Mark the entry as requiring an identity hash code
*/
public void setHashed(int identityHashCode) {
this.hasIdentityHashCode = true;
this.identityHashCode = identityHashCode;
}
/**
* @return Does this entry require an identity hash code
*/
public boolean requiresIdentityHashCode() {
return hasIdentityHashCode;
}
/**
* @return the identity hash code associated with this entry
*/
public int getIdentityHashCode() {
return identityHashCode;
}
}
/**
* Find or create map entry for a jdk/rvm object pair.
* @param jdkObject JDK object
* @return map entry for the given object
*/
public static Entry findOrCreateEntry(Object jdkObject) {
if (jdkObject == null)
return nullEntry;
// Avoid duplicates of some known "safe" classes
jdkObject = BootImageObjectAddressRemapper.getInstance().intern(jdkObject);
synchronized (BootImageMap.class) {
Key key = new Key(jdkObject);
Entry entry = keyToEntry.get(key);
if (entry == null) {
entry = new Entry(newId(), jdkObject);
keyToEntry.put(key, entry);
objectIdToEntry.add(entry);
}
return entry;
}
}
/**
* Get jdk object corresponding to an object id.
* @param objectId object id
* @return jdk object
*/
public static Object getObject(int objectId) {
return objectIdToEntry.get(objectId).jdkObject;
}
/**
* Get bootimage offset of an object.
* @param jdkObject JDK object
* @return offset of corresponding rvm object within bootimage, in bytes
*/
public static Address getImageAddress(Object jdkObject, boolean fatalIfNotFound) {
BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(jdkObject);
if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
if (fatalIfNotFound) {
fail(jdkObject + " is not in bootimage");
} else {
return Address.zero();
}
}
return mapEntry.imageAddress;
}
/**
* @return enumeration of all the entries
*/
public static Enumeration<BootImageMap.Entry> elements() {
return keyToEntry.elements();
}
}