/*
* 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.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.SortedSet;
import java.util.Stack;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.jikesrvm.Callbacks;
import org.jikesrvm.VM;
import org.jikesrvm.ArchitectureSpecific.CodeArray;
import org.jikesrvm.ArchitectureSpecific.LazyCompilationTrampoline;
import org.jikesrvm.ArchitectureSpecific.OutOfLineMachineCode;
import org.jikesrvm.classloader.Atom;
import org.jikesrvm.classloader.BootstrapClassLoader;
import org.jikesrvm.classloader.RVMArray;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMField;
import org.jikesrvm.classloader.RVMMember;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.classloader.TypeDescriptorParsing;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.jni.FunctionTable;
import org.jikesrvm.jni.JNIEnvironment;
import org.jikesrvm.objectmodel.MiscHeader;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.objectmodel.RuntimeTable;
import org.jikesrvm.objectmodel.TIB;
import org.jikesrvm.runtime.BootRecord;
import org.jikesrvm.runtime.Entrypoints;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.Statics;
import org.jikesrvm.scheduler.RVMThread;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* Construct an RVM virtual machine bootimage.
* <pre>
* Invocation args:
* -n <filename> list of typenames to be written to bootimage
* -X:bc:<bcarg> pass bcarg to bootimage compiler as command
* line argument
* -classpath <path> list of places to look for bootimage classes
* -littleEndian write words to bootimage in little endian format?
* -trace talk while we work? (increment "verbose")
* -detailed print detailed info on traversed objects
* (double-increment "verbose")
* -demographics show summary of how boot space is used
* -ca <addr> address where code portion of boot image starts
* -da <addr> address where data portion of boot image starts
* -ra <addr> address where ref map portion of boot image starts
* -log <filename> place to write log file to
* -o <filename> place to put bootimage
* -m <filename> place to put bootimage map
* -profile time major phases of bootimage writing
* -xclasspath <path> OBSOLETE compatibility aid
* -numThreads=N number of parallel compilation threads we should create
*
* </pre>
*/
public class BootImageWriter extends BootImageWriterMessages
implements BootImageWriterConstants {
/**
* The name of the class library, used when performing oracle operations of
* trying to fill in fields when they cannot be reflected upon. Always lower
* case.
*/
private static String classLibrary;
/**
* Number of threads we should use for compilation. Default to number of
* available processors +1. This is the ideal thread pool size for something
* compute bound, but should be larger if IO is an issue.
*/
public static int numThreads = Runtime.getRuntime().availableProcessors()+1;
/**
* The boot thread
*/
private static RVMThread startupThread;
/**
* How much talking while we work?
*/
private static int verbose = 0;
private static int depthCutoff = 3; // how deeply in the recursive copy do we continue to blather
/**
* Places to look for classes when building bootimage.
*/
private static String bootImageRepositoriesAtBuildTime;
/**
* Places to look for classes when executing bootimage.
*/
private static String bootImageRepositoriesAtExecutionTime;
/**
* the bootimage
*/
private static BootImage bootImage;
/**
* Types to be placed into bootimage, stored as key/value pairs
* where key is a String like "java.lang.Object" or "[Ljava.lang.Object;"
* and value is the corresponding RVMType.
*/
private static final Hashtable<String,RVMType> bootImageTypes =
new Hashtable<String,RVMType>(5000);
/**
* For all the scalar types to be placed into bootimage, keep
* key/value pairs where key is a Key(jdkType) and value is
* a FieldInfo.
*/
private static HashMap<Key,FieldInfo> bootImageTypeFields;
/**
* Class to collecting together field information
*/
private static class FieldInfo {
/**
* Field table from JDK verion of class
*/
final Field[] jdkFields;
/**
* Fields that are the one-to-one match of rvm instanceFields
* includes superclasses
*/
Field[] jdkInstanceFields;
/**
* Fields that are the one-to-one match of rvm staticFields
*/
Field[] jdkStaticFields;
/**
* Rvm type associated with this Field info
*/
RVMType rvmType;
/**
* Jdk type associated with this Field info
*/
final Class<?> jdkType;
/**
* Constructor.
* @param jdkType the type to associate with the key
*/
public FieldInfo(Class<?> jdkType, RVMType rvmType) {
this.jdkFields = jdkType.getDeclaredFields();
this.jdkType = jdkType;
this.rvmType = rvmType;
}
}
/**
* Key for looking up fieldInfo
*/
private static class Key {
/**
* Jdk type
*/
final Class<?> jdkType;
/**
* Constructor.
* @param jdkType the type to associate with the key
*/
public Key(Class<?> jdkType) { this.jdkType = jdkType; }
/**
* Returns a hash code value for the key.
* @return a hash code value for this key
*/
@Override
public int hashCode() { return jdkType.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 that) {
return (that instanceof Key) && jdkType == ((Key)that).jdkType;
}
}
/**
* Comparator that always says entries are equivalent. For use when
* comparator defers to another comparator.
*/
private static final class IdenticalComparator implements Comparator<BootImageMap.Entry> {
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
return 0;
}
}
/**
* Comparator of boot image entries that sorts according to the type
* reference ID.
*/
private static final class TypeReferenceComparator implements Comparator<BootImageMap.Entry> {
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
TypeReference aRef = TypeReference.findOrCreate(a.jdkObject.getClass());
TypeReference bRef = TypeReference.findOrCreate(b.jdkObject.getClass());
return aRef.getId() - bRef.getId();
}
}
/**
* Comparator of boot image entries that sorts according to the name of the
* classes.
*/
private static final class ClassNameComparator implements Comparator<BootImageMap.Entry> {
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
return -a.jdkObject.getClass().toString().compareTo(b.jdkObject.getClass().toString());
}
}
/**
* Comparator of boot image entries that sorts according to the size of
* the objects.
*/
private static final class ObjectSizeComparator implements Comparator<BootImageMap.Entry> {
private final Comparator<BootImageMap.Entry> identicalSizeComparator;
ObjectSizeComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
this.identicalSizeComparator = identicalSizeComparator;
}
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
TypeReference aRef = TypeReference.findOrCreate(a.jdkObject.getClass());
TypeReference bRef = TypeReference.findOrCreate(b.jdkObject.getClass());
if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
return identicalSizeComparator.compare(a, b);
} else if (!aRef.isResolved()) {
return -1;
} else if (!bRef.isResolved()) {
return 1;
} else {
int aSize = getSize(aRef.peekType(), a.jdkObject);
int bSize = getSize(bRef.peekType(), b.jdkObject);
if (aSize == bSize) {
return identicalSizeComparator.compare(a, b);
} else {
return aSize - bSize;
}
}
}
}
/**
* Comparator of boot image entries that sorts according to the number of
* references within the objects.
*/
private static final class NumberOfReferencesComparator implements Comparator<BootImageMap.Entry> {
private final Comparator<BootImageMap.Entry> identicalSizeComparator;
NumberOfReferencesComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
this.identicalSizeComparator = identicalSizeComparator;
}
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
TypeReference aRef = TypeReference.findOrCreate(a.jdkObject.getClass());
TypeReference bRef = TypeReference.findOrCreate(b.jdkObject.getClass());
if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
return identicalSizeComparator.compare(a, b);
} else if (!aRef.isResolved()) {
return 1;
} else if (!bRef.isResolved()) {
return -1;
} else {
int aSize = getNumberOfReferences(aRef.peekType(), a.jdkObject);
int bSize = getNumberOfReferences(bRef.peekType(), b.jdkObject);
if (aSize == bSize) {
return identicalSizeComparator.compare(a, b);
} else {
return bSize - aSize;
}
}
}
}
/**
* Comparator of boot image entries that sorts according to the number of
* non-final references within the objects.
*/
private static final class NumberOfNonFinalReferencesComparator implements Comparator<BootImageMap.Entry> {
private final Comparator<BootImageMap.Entry> identicalSizeComparator;
NumberOfNonFinalReferencesComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
this.identicalSizeComparator = identicalSizeComparator;
}
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
TypeReference aRef = TypeReference.findOrCreate(a.jdkObject.getClass());
TypeReference bRef = TypeReference.findOrCreate(b.jdkObject.getClass());
if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
return identicalSizeComparator.compare(a, b);
} else if (!aRef.isResolved()) {
return 1;
} else if (!bRef.isResolved()) {
return -1;
} else {
int aSize = getNumberOfNonFinalReferences(aRef.peekType(), a.jdkObject);
int bSize = getNumberOfNonFinalReferences(bRef.peekType(), b.jdkObject);
if (aSize == bSize) {
return identicalSizeComparator.compare(a, b);
} else {
return bSize - aSize;
}
}
}
}
/**
* Comparator of boot image entries that sorts according to the density of
* non-final references within the objects.
*/
private static final class NonFinalReferenceDensityComparator implements Comparator<BootImageMap.Entry> {
private final Comparator<BootImageMap.Entry> identicalSizeComparator;
NonFinalReferenceDensityComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
this.identicalSizeComparator = identicalSizeComparator;
}
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
TypeReference aRef = TypeReference.findOrCreate(a.jdkObject.getClass());
TypeReference bRef = TypeReference.findOrCreate(b.jdkObject.getClass());
if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
return identicalSizeComparator.compare(a, b);
} else if (!aRef.isResolved()) {
return 1;
} else if (!bRef.isResolved()) {
return -1;
} else {
double aSize = (double)getNumberOfNonFinalReferences(aRef.peekType(), a.jdkObject) / (double)getSize(aRef.peekType(), a.jdkObject);
double bSize = (double)getNumberOfNonFinalReferences(bRef.peekType(), b.jdkObject) / (double)getSize(bRef.peekType(), b.jdkObject);
int result = Double.compare(aSize, bSize);
if (result == 0) {
return identicalSizeComparator.compare(a, b);
} else {
return -result;
}
}
}
}
/**
* Comparator of boot image entries that sorts according to the density of
* references within the objects.
*/
private static final class ReferenceDensityComparator implements Comparator<BootImageMap.Entry> {
private final Comparator<BootImageMap.Entry> identicalSizeComparator;
ReferenceDensityComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
this.identicalSizeComparator = identicalSizeComparator;
}
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
TypeReference aRef = TypeReference.findOrCreate(a.jdkObject.getClass());
TypeReference bRef = TypeReference.findOrCreate(b.jdkObject.getClass());
if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
return identicalSizeComparator.compare(a, b);
} else if (!aRef.isResolved()) {
return 1;
} else if (!bRef.isResolved()) {
return -1;
} else {
double aSize = (double)getNumberOfReferences(aRef.peekType(), a.jdkObject) / (double)getSize(aRef.peekType(), a.jdkObject);
double bSize = (double)getNumberOfReferences(bRef.peekType(), b.jdkObject) / (double)getSize(bRef.peekType(), b.jdkObject);
int result = Double.compare(aSize, bSize);
if (result == 0) {
return identicalSizeComparator.compare(a, b);
} else {
return -result;
}
}
}
}
/**
* Linked list that operates in FIFO manner rather than LIFO
*/
private static final class FIFOLinkedList<T> extends LinkedList<T> {
/** Serialization support to avoid warning */
static final long serialVersionUID = 242526399904424920L;
/** Remove from the other end of the linked list */
@Override
public T remove() {
return removeLast();
}
}
/**
* Entries yet to be written into the boot image
*/
private static final Queue<BootImageMap.Entry> pendingEntries;
static {
if (true) // depth first traversal
pendingEntries = new FIFOLinkedList<BootImageMap.Entry>();
else if (false) pendingEntries = new LinkedList<BootImageMap.Entry>(); // breadth first traversal
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11, new TypeReferenceComparator());
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11, new ClassNameComparator());
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new ObjectSizeComparator(new IdenticalComparator()));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NumberOfReferencesComparator(new IdenticalComparator()));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NumberOfNonFinalReferencesComparator(new IdenticalComparator()));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NumberOfNonFinalReferencesComparator(new ObjectSizeComparator(new TypeReferenceComparator())));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NumberOfReferencesComparator(new ObjectSizeComparator(new ClassNameComparator())));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NumberOfReferencesComparator(new ObjectSizeComparator(new TypeReferenceComparator())));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new ReferenceDensityComparator(new IdenticalComparator()));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NonFinalReferenceDensityComparator(new IdenticalComparator()));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new ReferenceDensityComparator(new TypeReferenceComparator()));
else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
new NonFinalReferenceDensityComparator(new TypeReferenceComparator()));
}
private static final boolean STATIC_FIELD = true;
private static final boolean INSTANCE_FIELD = false;
/**
* The absolute address at which the data portion of the
* bootImage is going to be loaded.
*/
public static Address bootImageDataAddress = Address.zero();
/**
* The absolute address at which the code portion of the
* bootImage is going to be loaded.
*/
public static Address bootImageCodeAddress = Address.zero();
/**
* The absolute address at which the reference map portion of the
* bootImage is going to be loaded.
*/
public static Address bootImageRMapAddress = Address.zero();
public static Address getBootImageDataAddress() {
if (bootImageDataAddress.isZero()) fail("BootImageWrite.getBootImageAddress called before boot image established");
return bootImageDataAddress;
}
public static Address getBootImageCodeAddress() {
if (bootImageCodeAddress.isZero()) fail("BootImageWrite.getBootImageAddress called before boot image established");
return bootImageCodeAddress;
}
public static Address getBootImageRMapAddress() {
if (bootImageRMapAddress.isZero()) fail("BootImageWrite.getBootImageAddress called before boot image established");
return bootImageRMapAddress;
}
/**
* Write words to bootimage in little endian format?
*/
private static boolean littleEndian = false;
/**
* Show how boot space is used by type
*/
private static boolean demographics = false;
/**
* Report time costs of each stage in bootimage writing and
* report any methods that take 'too long' to compile.
*/
private static boolean profile = false;
/**
* A wrapper around the calling context to aid in tracing.
*/
private static class TraceContext extends Stack<String> {
private static final long serialVersionUID = -9048590130621822408L;
/**
* Report a field that is part of our library's (GNU Classpath's)
implementation, but not the host JDK's implementation.
*/
public void traceFieldNotInHostJdk() {
traceNulledWord(": field not in host jdk");
}
/* Report a field that is an instance field in the host JDK but a static
field in ours. */
public void traceFieldNotStaticInHostJdk() {
traceNulledWord(": field not static in host jdk");
}
/* Report a field that is a different type in the host JDK. */
public void traceFieldDifferentTypeInHostJdk() {
traceNulledWord(": field different type in host jdk");
}
/**
* Report an object of a class that is not part of the bootImage.
*/
public void traceObjectNotInBootImage() {
traceNulledWord(": object not in bootimage");
}
/**
* Report nulling out a pointer.
*/
private void traceNulledWord(String message) {
say(this.toString(), message, ", writing a null");
}
/**
* Report an object of a class that is not part of the bootImage.
*/
public void traceObjectFoundThroughKnown() {
say(this.toString(), ": object found through known");
}
/**
* Generic trace routine.
*/
public void trace(String message) {
say(this.toString(), message);
}
/**
* Return a string representation of the context.
* @return string representation of this context
*/
@Override
public String toString() {
StringBuilder message = new StringBuilder();
for (int i = 0; i < size(); i++) {
if (i > 0) message.append(" --> ");
message.append(elementAt(i));
}
return message.toString();
}
/**
* Push an entity onto the context
*/
public void push(String type, String fullName) {
StringBuilder sb = new StringBuilder("(");
sb.append(type).append(")");
sb.append(fullName);
push(sb.toString());
}
/**
* Push a field access onto the context
*/
public void push(String type, String decl, String fieldName) {
StringBuilder sb = new StringBuilder("(");
sb.append(type).append(")");
sb.append(decl).append(".").append(fieldName);
push(sb.toString());
}
/**
* Push an array access onto the context
*/
public void push(String type, String decl, int index) {
StringBuilder sb = new StringBuilder("(");
sb.append(type).append(")");
sb.append(decl).append("[").append(index).append("]");
push(sb.toString());
}
}
/**
* Global trace context.
*/
private static TraceContext traceContext = new TraceContext();
@SuppressWarnings({"unused", "UnusedDeclaration"})
private static Object sillyhack;
/**
* Junk released from Statics when instantiation finishes and may
* be needed to generate the boot image report.
*/
private static Object staticsJunk;
/**
* Main.
* @param args command line arguments
*/
public static void main(String[] args) {
String logFile = null;
String bootImageCodeName = null;
String bootImageDataName = null;
String bootImageRMapName = null;
String bootImageMapName = null;
Vector<String> bootImageTypeNames = null;
String bootImageTypeNamesFile = null;
String[] bootImageCompilerArgs = {};
//
// This may look useless, but it is not: it is a kludge to prevent
// forName blowing up. By forcing the system to load some classes
// now, we ensure that forName does not cause security violations by
// trying to load into java.util later.
//
java.util.HashMap<Object,Class<?>> x = new java.util.HashMap<Object,Class<?>>();
x.put(x, x.getClass());
sillyhack = x;
//
// Process command line directives.
//
for (int i = 0; i < args.length; ++i) {
// name of class library
if (args[i].equals("-classlib")) {
if (++i >= args.length)
fail("argument syntax error: Got a -classlib flag without a following class library name");
classLibrary = args[i].toLowerCase().intern();
continue;
}
// name of code image file
if (args[i].equals("-oc")) {
if (++i >= args.length)
fail("argument syntax error: Got a -oc flag without a following code image file name");
bootImageCodeName = args[i];
continue;
}
// name of data image file
if (args[i].equals("-od")) {
if (++i >= args.length)
fail("argument syntax error: Got a -od flag without a following data image file name");
bootImageDataName = args[i];
continue;
}
// name of ref map image file
if (args[i].equals("-or")) {
if (++i >= args.length)
fail("argument syntax error: Got a -or flag without a following ref map file name");
bootImageRMapName = args[i];
continue;
}
// name of map file
if (args[i].equals("-m")) {
if (++i >= args.length)
fail("argument syntax error: Got a -m flag without a following bootImageMap file name");
bootImageMapName = args[i];
continue;
}
// image code start address
if (args[i].equals("-ca")) {
if (++i >= args.length)
fail("argument syntax error: Got a -ca flag without a following image address");
bootImageCodeAddress = Address.fromIntZeroExtend(Integer.decode(args[i]));
continue;
}
// image data start address
if (args[i].equals("-da")) {
if (++i >= args.length)
fail("argument syntax error: Got a -da flag without a following image address");
bootImageDataAddress = Address.fromIntZeroExtend(Integer.decode(args[i]));
continue;
}
// image ref map start address
if (args[i].equals("-ra")) {
if (++i >= args.length)
fail("argument syntax error: Got a -ra flag without a following image address");
bootImageRMapAddress = Address.fromIntZeroExtend(Integer.decode(args[i]));
continue;
}
// file containing names of types to be placed into bootimage
if (args[i].equals("-n")) {
if (++i >= args.length)
fail("argument syntax error: Got a -n flag without a following file name");
if (bootImageTypeNamesFile != null)
fail("argument syntax error: We've already read in the bootImageTypeNames from" +
bootImageTypeNamesFile + "; just got another -n argument" +
" telling us to read them from " + args[i]);
bootImageTypeNamesFile = args[i];
continue;
}
// bootimage compiler argument
if (args[i].startsWith("-X:bc:")) {
String[] nbca = new String[bootImageCompilerArgs.length+1];
for (int j = 0; j < bootImageCompilerArgs.length; j++) {
nbca[j] = bootImageCompilerArgs[j];
}
nbca[nbca.length-1] = args[i].substring(6);
bootImageCompilerArgs = nbca;
say("compiler arg: ", bootImageCompilerArgs[nbca.length-1]);
continue;
}
// places where rvm components live, at build time
if (args[i].equals("-classpath")) {
if (++i >= args.length)
fail("argument syntax error: Got a -classpath flag without a following classpath for build-time RVM components");
bootImageRepositoriesAtBuildTime = args[i];
continue;
}
// places where rvm components live, at execution time
if (args[i].equals("-xclasspath")) {
if (++i >= args.length)
fail("argument syntax error: Got an -xclasspath flag without a following execution-time classpath for RVM components");
bootImageRepositoriesAtExecutionTime = args[i];
continue;
}
// generate trace messages while writing bootimage (for debugging)
if (args[i].equals("-trace")) {
verbose++;
continue;
}
// generate info by type
if (args[i].equals("-demographics")) {
demographics = true;
continue;
}
// numThreads
if (args[i].startsWith("-numThreads=")) {
numThreads = Integer.parseInt(args[i].substring(12));
if (numThreads < 1) {
fail("numThreads must be a positive number, value supplied: "+ numThreads);
}
continue;
}
// profile
if (args[i].equals("-profile")) {
profile = true;
continue;
}
// log
if (args[i].equals("-log")) {
if (++i >= args.length)
fail("argument syntax error: Got a -log flag without a following argument for the log file");
logFile = args[i];
continue;
}
// generate detailed information about traversed objects (for debugging)
if (args[i].equals("-detailed")) {
verbose += 2;
continue;
}
// write words to bootimage in little endian format
if (args[i].equals("-littleEndian")) {
littleEndian = true;
continue;
}
fail("unrecognized command line argument: " + args[i]);
}
if (verbose >= 2)
traversed = new Hashtable<Object,Integer>(500);
//
// Check command line directives for correctness.
//
if (bootImageCodeName == null)
fail("please specify \"-oc <boot-image-code-filename>\"");
if (bootImageDataName == null)
fail("please specify \"-od <boot-image-data-filename>\"");
if (bootImageRMapName == null)
fail("please specify \"-or <boot-image-rmap-filename>\"");
if (bootImageTypeNamesFile == null)
fail("please specify \"-n <boot-image-type-names-filename>\"");
if (bootImageRepositoriesAtBuildTime == null)
fail("please specify \"-classpath <path>\"");
if (bootImageRepositoriesAtExecutionTime == null)
bootImageRepositoriesAtExecutionTime = bootImageRepositoriesAtBuildTime;
if (bootImageDataAddress.isZero())
fail("please specify boot-image address with \"-da <addr>\"");
if (!(bootImageDataAddress.toWord().and(Word.fromIntZeroExtend(0x00FFFFFF)).isZero()))
fail("please specify a boot-image address that is a multiple of 0x01000000");
if (bootImageCodeAddress.isZero())
fail("please specify boot-image address with \"-ca <addr>\"");
if (!(bootImageCodeAddress.toWord().and(Word.fromIntZeroExtend(0x00FFFFFF)).isZero()))
fail("please specify a boot-image address that is a multiple of 0x01000000");
if (bootImageRMapAddress.isZero())
fail("please specify boot-image address with \"-ra <addr>\"");
if (!(bootImageRMapAddress.toWord().and(Word.fromIntZeroExtend(0x00FFFFFF)).isZero()))
fail("please specify a boot-image address that is a multiple of 0x01000000");
// Redirect the log file
if (logFile != null) {
try {
PrintStream logFileStream = new PrintStream(logFile);
System.setOut(logFileStream);
System.setErr(logFileStream);
} catch (IOException ex) {
fail("Failed to open log file for redirecting: " + ex.getMessage());
}
}
//
// Initialize the bootimage.
// Do this earlier than we logically need to because we need to
// allocate a massive byte[] that will hold the bootimage in core and
// on some host JDKs it is essential to do that early while there
// is still lots of virgin storage left.
// (need to get contiguous storage before it gets fragmented by pinned objects)
//
try {
bootImage = new BootImage(littleEndian, verbose >= 1, bootImageCodeName, bootImageDataName, bootImageRMapName);
} catch (IOException e) {
fail("unable to write bootImage: "+e);
}
//
// Install handler that intercepts all object address references made by
// xxx classes executed on host jdk and substitutes a value that can be
// fixed up later when those objects are copied from host jdk to bootimage.
//
enableObjectAddressRemapper();
//
// Initialize rvm classes for use in "bootimage writing" mode.
// These rvm classes are used two ways:
// - they are used now, by host jdk, to create the bootimage
// - they are used later, by target rvm, to execute the bootimage
//
if (verbose >= 1) say("starting up");
// try {
VM.initForBootImageWriter(bootImageRepositoriesAtBuildTime,
bootImageCompilerArgs);
// } catch (Exception e) {
// fail("unable to initialize VM: "+e);
// }
//
// Create (in host jdk address space) the rvm objects that will be
// needed at run time to execute enough of the virtual machine
// to dynamically load and compile the remainder of itself.
//
long startTime = 0;
long stopTime = 0;
if (profile) startTime = System.currentTimeMillis();
try {
bootImageTypeNames = readTypeNames(bootImageTypeNamesFile);
} catch (IOException e) {
fail("unable to read the type names from " + bootImageTypeNamesFile + ": " +e);
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: readingTypeNames "+(stopTime-startTime)+" ms");
}
if (profile) startTime = System.currentTimeMillis();
try {
createBootImageObjects(bootImageTypeNames, bootImageTypeNamesFile);
} catch (Exception e) {
e.printStackTrace(System.out);
fail("unable to create objects: "+e);
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: createBootImageObjects "+(stopTime-startTime)+" ms");
}
//
// No further bootimage object references should get generated.
// If they are, they'll be assigned an objectId of "-1" (see Magic)
// and will manifest themselves as an array subscript out of bounds
// error when BootImageMap attempts to look up the object references.
//
disableObjectAddressRemapper();
////////////////////////////////////////////////////
// Copy rvm objects from host jdk into bootimage.
////////////////////////////////////////////////////
VM.writingImage = true;
if (verbose >= 1) say("Memory available: ",
String.valueOf(Runtime.getRuntime().freeMemory()),
" bytes out of ",
String.valueOf(Runtime.getRuntime().totalMemory()),
" bytes");
if (profile) startTime = System.currentTimeMillis();
//
// First object in image must be boot record (so boot loader will know
// where to find it). We'll write out an uninitialized record and not recurse
// to reserve the space, then go back and fill it in later.
//
if (verbose >= 1) say("copying boot record");
BootRecord bootRecord = BootRecord.the_boot_record;
Address bootRecordImageAddress = Address.zero();
try {
// copy just the boot record
bootRecordImageAddress = copyToBootImage(bootRecord, true, Address.max(), null, false);
if (bootRecordImageAddress.EQ(OBJECT_NOT_PRESENT)) {
fail("can't copy boot record");
}
} catch (IllegalAccessException e) {
fail("can't copy boot record: "+e);
}
//
// Next, copy the jtoc.
//
if (verbose >= 1) say("copying jtoc");
// Pointer to middle of JTOC
Address jtocImageAddress = Address.max();
try {
if (VM.BuildForIA32) {
// Force 16byte alignment of the JTOC on Intel
int[] slots = Statics.getSlotsAsIntArray();
jtocImageAddress = bootImage.allocateArray(RVMArray.IntArray, slots.length, false, 0, 16);
BootImageMap.Entry jtocEntry = BootImageMap.findOrCreateEntry(slots);
jtocEntry.imageAddress = jtocImageAddress;
}
jtocImageAddress = copyToBootImage(Statics.getSlotsAsIntArray(), false, jtocImageAddress, null, false);
if (jtocImageAddress.EQ(OBJECT_NOT_PRESENT)) {
fail("can't copy jtoc");
}
} catch (IllegalAccessException e) {
fail("can't copy jtoc: "+e);
}
Address jtocPtr = jtocImageAddress.plus(Statics.middleOfTable << LOG_BYTES_IN_INT);
if (jtocPtr.NE(bootRecord.tocRegister))
fail("mismatch in JTOC placement "+VM.addressAsHexString(jtocPtr)+" != "+ VM.addressAsHexString(bootRecord.tocRegister));
//
// Now, copy all objects reachable from jtoc, replacing each object id
// that was generated by object address remapper with the actual
// bootimage address of that object.
//
if (verbose >= 1) say("copying statics");
try {
int refSlotSize = Statics.getReferenceSlotSize();
for (int i = Statics.middleOfTable+refSlotSize, n = Statics.getHighestInUseSlot();
i <= n;
i+= refSlotSize) {
if(!Statics.isReference(i)) {
throw new Error("Static " + i + " of " + n + " isn't reference");
}
jtocCount = i; // for diagnostic
Offset jtocOff = Statics.slotAsOffset(i);
int objCookie;
if (VM.BuildFor32Addr)
objCookie = Statics.getSlotContentsAsInt(jtocOff);
else
objCookie = (int) Statics.getSlotContentsAsLong(jtocOff);
// if (verbose >= 3)
// say(" jtoc[", String.valueOf(i), "] = ", String.valueOf(objCookie));
Object jdkObject = BootImageMap.getObject(objCookie);
if (jdkObject == null)
continue;
if (verbose >= 2) traceContext.push(jdkObject.getClass().getName(),
getRvmStaticField(jtocOff) + "");
copyReferenceFieldToBootImage(jtocPtr.plus(jtocOff), jdkObject, Statics.getSlotsAsIntArray(), false, false, null, null);
if (verbose >= 2) traceContext.pop();
}
// Copy entries that are in the pending queue
processPendingEntries();
// Find and copy unallocated entries
for (int i=0; i < BootImageMap.objectIdToEntry.size(); i++) {
BootImageMap.Entry mapEntry = BootImageMap.objectIdToEntry.get(i);
if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
mapEntry.imageAddress = copyToBootImage(mapEntry.jdkObject, false, Address.max(), null, false);
fixupLinkAddresses(mapEntry);
}
}
} catch (IllegalAccessException e) {
fail("unable to copy statics: "+e);
}
jtocCount = -1;
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: filling bootimage byte[] "+(stopTime-startTime)+" ms");
}
//
// Record startup context in boot record.
//
if (verbose >= 1) say("updating boot record");
byte[] startupStack = startupThread.getStack();
CodeArray startupCode = Entrypoints.bootMethod.getCurrentEntryCodeArray();
bootRecord.tiRegister = startupThread.getLockingId();
bootRecord.spRegister = BootImageMap.getImageAddress(startupStack, true).plus(startupStack.length);
bootRecord.ipRegister = BootImageMap.getImageAddress(startupCode.getBacking(), true);
bootRecord.bootThreadOffset = Entrypoints.bootThreadField.getOffset();
bootRecord.bootImageDataStart = bootImageDataAddress;
bootRecord.bootImageDataEnd = bootImageDataAddress.plus(bootImage.getDataSize());
bootRecord.bootImageCodeStart = bootImageCodeAddress;
bootRecord.bootImageCodeEnd = bootImageCodeAddress.plus(bootImage.getCodeSize());
bootRecord.bootImageRMapStart = bootImageRMapAddress;
bootRecord.bootImageRMapEnd = bootImageRMapAddress.plus(bootImage.getRMapSize());
// Update field of boot record now by re-copying
//
if (verbose >= 1) say("re-copying boot record (and its TIB)");
try {
Address newBootRecordImageAddress = copyToBootImage(bootRecord, false, bootRecordImageAddress, null, false);
if (!newBootRecordImageAddress.EQ(bootRecordImageAddress)) {
VM.sysWriteln("bootRecordImageOffset = ", bootRecordImageAddress);
VM.sysWriteln("newBootRecordImageOffset = ", newBootRecordImageAddress);
VM._assert(newBootRecordImageAddress.EQ(bootRecordImageAddress));
}
// Make sure pending entries are fully written out
processPendingEntries();
} catch (IllegalAccessException e) {
fail("unable to update boot record: "+e);
}
if (VM.BuildWithGCTrace) {
/* Set the values in fields updated during the build process */
Offset prevAddrOffset = Entrypoints.tracePrevAddressField.getOffset();
bootImage.setAddressWord(jtocPtr.plus(prevAddrOffset),
MiscHeader.getBootImageLink().toWord(), false, false);
Offset oIDOffset = Entrypoints.traceOIDField.getOffset();
bootImage.setAddressWord(jtocPtr.plus(oIDOffset), MiscHeader.getOID(), false, false);
}
//
// Write image to disk.
//
if (profile) startTime = System.currentTimeMillis();
try {
bootImage.write();
} catch (IOException e) {
fail("unable to write bootImage: "+e);
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: writing RVM.map "+(stopTime-startTime)+" ms");
}
//
// Show space usage in boot image by type
//
if (demographics) {
spaceReport();
}
//
// Summarize status of types that were referenced by objects we put into
// the bootimage but which are not, themselves, in the bootimage. Any
// such types had better not be needed to dynamically link in the
// remainder of the virtual machine at run time!
//
if (verbose >= 2) {
for (int i = FIRST_TYPE_DICTIONARY_INDEX; i < RVMType.numTypes(); ++i) {
RVMType type = RVMType.getType(i);
if (type == null) continue;
if (!type.isResolved()) {
say("type referenced but not resolved: ", type.toString());
} else if (!type.isInstantiated()) {
say("type referenced but not instantiated: ", type.toString());
} else if (!type.isInitialized()) {
say("type referenced but not initialized: ", type.toString());
}
}
}
//
// Generate address map for debugging.
//
try {
if (bootImageMapName != null)
writeAddressMap(bootImageMapName);
} catch (IOException e) {
fail("unable to write address map: "+e);
}
if (verbose >= 1) say("done");
}
/**
* Class holding per type details for demographics
*/
private static class DemographicInformation {
/** number of allocations */
int count;
/** size allocated */
int size;
}
/**
* Compare sizes of types allocated to boot image
*/
private static class TypeComparator<T> implements Comparator<T> {
public int compare(T a, T b) {
if (a == null) return 1;
if (b == null) return -1;
if ((a instanceof RVMType) && (b instanceof RVMType)) {
RVMType typeA = (RVMType) a;
RVMType typeB = (RVMType) b;
DemographicInformation infoA = demographicData.get(typeA);
if (infoA == null) return 1;
DemographicInformation infoB = demographicData.get(typeB);
if (infoB == null) return -1;
if (infoA.size > infoB.size) return -1;
if (infoA.size < infoB.size) return 1;
return 0;
}
return 0;
}
}
/**
* Demographic data on all types
*/
private static final HashMap<RVMType,DemographicInformation> demographicData =
new HashMap<RVMType,DemographicInformation>();
/**
* Log an allocation in the boot image
* @param type the type allocated
* @param size the size of the type
*/
public static void logAllocation(RVMType type, int size) {
if(demographics) {
DemographicInformation info = demographicData.get(type);
if(info != null) {
info.count++;
info.size += size;
} else {
info = new DemographicInformation();
info.count++;
info.size += size;
demographicData.put(type, info);
}
}
}
/**
* Print a report of space usage in the boot image.
*/
public static void spaceReport() {
RVMType[] tempTypes = new RVMType[RVMType.numTypes() - FIRST_TYPE_DICTIONARY_INDEX];
for (int i = FIRST_TYPE_DICTIONARY_INDEX; i < RVMType.numTypes(); ++i)
tempTypes[i - FIRST_TYPE_DICTIONARY_INDEX] = RVMType.getType(i);
Arrays.sort(tempTypes, new TypeComparator<RVMType>());
int totalCount = 0, totalBytes = 0;
for (RVMType type : tempTypes) {
if (type == null) continue;
DemographicInformation info = demographicData.get(type);
if (info == null) continue;
totalCount += info.count;
totalBytes += info.size;
}
VM.sysWriteln("\nBoot image space report:");
VM.sysWriteln("------------------------------------------------------------------------------------------");
VM.sysWriteField(60, "TOTAL");
VM.sysWriteField(15, totalCount);
VM.sysWriteField(15, totalBytes);
VM.sysWriteln();
VM.sysWriteln("\nCompiled methods space report:");
VM.sysWriteln("------------------------------------------------------------------------------------------");
CompiledMethods.spaceReport();
VM.sysWriteln("------------------------------------------------------------------------------------------");
VM.sysWriteln("\nBoot image space usage by types:");
VM.sysWriteln("Type Count Bytes");
VM.sysWriteln("------------------------------------------------------------------------------------------");
VM.sysWriteField(60, "TOTAL");
VM.sysWriteField(15, totalCount);
VM.sysWriteField(15, totalBytes);
VM.sysWriteln();
for (RVMType type : tempTypes) {
if (type == null) continue;
DemographicInformation info = demographicData.get(type);
if (info == null) continue;
if (info.count > 0) {
VM.sysWriteField(60, type.toString());
VM.sysWriteField(15, info.count);
VM.sysWriteField(15, info.size);
VM.sysWriteln();
}
}
}
/**
* Read list of type names from a file.
* @param fileName the name of the file containing type names
* @return list type names
*/
public static Vector<String> readTypeNames(String fileName) throws IOException {
Vector<String> typeNames = new Vector<String>(500);
// DataInputStream ds = new DataInputStream(new FileInputStream(fileName));
LineNumberReader in = new LineNumberReader(new FileReader(fileName));
String typeName;
while ((typeName = in.readLine()) != null) { // stop at EOF
int index = typeName.indexOf('#');
if (index >= 0) // ignore trailing comments
typeName = typeName.substring(0, index);
typeName = typeName.trim(); // ignore leading and trailing whitespace
if (typeName.length() == 0)
continue; // ignore comment-only and whitespace-only lines
if (File.separatorChar != '/') {
typeName = typeName.replace(File.separatorChar, '/');
}
// debugging:
TypeDescriptorParsing.validateAsTypeDescriptor(typeName);
if (TypeDescriptorParsing.isValidTypeDescriptor(typeName))
typeNames.addElement(typeName);
else
fail(fileName + ":" + in.getLineNumber() +
": syntax error: \"" +
typeName + "\" does not describe any Java type.");
}
in.close();
return typeNames;
}
/**
* Create (in host jdk address space) the rvm objects that will be
* needed at run time to execute enough of the virtual machine
* to dynamically load and compile the remainder of itself.
*
* Side effect: rvm objects are created in host jdk address space
* Statics is populated
* "bootImageTypes" dictionary is populated with name/type pairs
*
* @param typeNames names of rvm classes whose static fields will contain
* the objects comprising the virtual machine bootimage
*/
public static void createBootImageObjects(Vector<String> typeNames,
String bootImageTypeNamesFile)
throws IllegalAccessException {
Callbacks.notifyBootImage(typeNames.elements());
long startTime = 0;
long stopTime = 0;
//
// Create types.
//
if (verbose >= 1) say("loading");
if (profile) startTime = System.currentTimeMillis();
for (String typeName : typeNames) {
//
// get type name
//
if (verbose >= 4)
say("typeName:", typeName);
//
// create corresponding rvm type
//
RVMType type;
try {
TypeReference tRef = TypeReference.findOrCreate(typeName);
type = tRef.resolve();
} catch (NoClassDefFoundError ncdf) {
ncdf.printStackTrace(System.out);
fail(bootImageTypeNamesFile +
" contains a class named \"" +typeName +
"\", but we can't find a class with that name: "+ ncdf);
return; // NOTREACHED
} catch (IllegalArgumentException ila) {
/* We should've caught any illegal type names at the data validation
* stage, when we read these in. If not, though,
* TypeReference.findOrCreate() will do its own sanity check. */
ila.printStackTrace(System.out);
fail(bootImageTypeNamesFile +
" is supposed to contain type names. It contains \"" + typeName +
"\", which does not parse as a legal type name: " + ila);
return; // NOTREACHED
}
type.markAsBootImageClass();
//
// convert type name from internal form to external form
// ie: Ljava/lang/Object; --> java.lang.Object
// [Ljava/lang/Object; --> [Ljava.lang.Object;
//
// NOTE: duplicate functionality. There is a method that does the same.
//
typeName = typeName.replace('/','.');
if (typeName.startsWith("L"))
typeName = typeName.substring(1, typeName.length() - 1);
//
// record name/type pair for later lookup by getRvmType()
//
bootImageTypes.put(typeName, type);
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: \tloading types "+(stopTime-startTime)+" ms");
}
if (verbose >= 1) say(String.valueOf(bootImageTypes.size()), " types");
//
// Lay out fields and method tables.
//
if (profile) startTime = System.currentTimeMillis();
if (verbose >= 1) say("resolving");
for (RVMType type : bootImageTypes.values()) {
if (verbose >= 2) say("resolving " + type);
// The resolution is supposed to be cached already.
type.resolve();
}
//
// Now that all types are resolved, do some additional fixup before we do any compilation
//
for (RVMType type : bootImageTypes.values()) {
type.allBootImageTypesResolved();
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: \tresolving types "+(stopTime-startTime)+" ms");
}
// Set tocRegister early so opt compiler can access it to
// perform fixed_jtoc optimization (compile static addresses into code).
// In the boot image, the bootrecord comes first followed by a
// Address array and then the TOC. To do this, we must fully
// simulate the alignment logic in the allocation code! Rather
// than replicate the allocation code here, we perform dummy
// allocations and then reset the boot image allocator.
BootRecord bootRecord = BootRecord.the_boot_record;
RVMClass rvmBRType = getRvmType(bootRecord.getClass()).asClass();
RVMArray intArrayType = RVMArray.IntArray;
// allocate storage for boot record
bootImage.allocateDataStorage(rvmBRType.getInstanceSize(),
ObjectModel.getAlignment(rvmBRType),
ObjectModel.getOffsetForAlignment(rvmBRType, false));
// allocate storage for JTOC (force 16byte alignment of the JTOC on Intel)
Address jtocAddress = bootImage.allocateDataStorage(intArrayType.getInstanceSize(0),
VM.BuildForIA32 ? 16 : ObjectModel.getAlignment(intArrayType),
ObjectModel.getOffsetForAlignment(intArrayType, false));
bootImage.resetAllocator();
bootRecord.tocRegister = jtocAddress.plus(intArrayType.getInstanceSize(Statics.middleOfTable));
// set up some stuff we need for compiling
OutOfLineMachineCode.init();
//
// Compile methods and populate jtoc with literals, TIBs, and machine code.
//
if (profile) startTime = System.currentTimeMillis();
if (verbose >= 1) say("instantiating");
if (verbose >= 1) say(" compiling with " + numThreads + " threads");
ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
for (RVMType type: bootImageTypes.values()) {
threadPool.execute(new BootImageWorker(type));
}
threadPool.shutdown();
try {
while(!threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)) {
say("Compilation really shouldn't take this long");
}
} catch (InterruptedException e){
throw new Error("Build interrupted", e);
}
if (BootImageWorker.instantiationFailed) {
throw new Error("Error during instantiaion");
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: \tinstantiating types "+(stopTime-startTime)+" ms");
}
// Free up unnecessary Statics data structures
staticsJunk = Statics.bootImageInstantiationFinished();
// Do the portion of JNIEnvironment initialization that can be done
// at bootimage writing time.
FunctionTable functionTable = BuildJNIFunctionTable.buildTable();
JNIEnvironment.initFunctionTable(functionTable);
//
// Collect the VM class Field to JDK class Field correspondence
// This will be needed when building the images of each object instance
// and for processing the static fields of the boot image classes
//
if (verbose >= 1) say("field info gathering");
if (profile) startTime = System.currentTimeMillis();
bootImageTypeFields = new HashMap<Key,FieldInfo>(bootImageTypes.size());
HashSet<String> invalidEntrys = new HashSet<String>();
// First retrieve the jdk Field table for each class of interest
for (RVMType rvmType : bootImageTypes.values()) {
FieldInfo fieldInfo;
if (!rvmType.isClassType())
continue; // arrays and primitives have no static or instance fields
Class<?> jdkType = getJdkType(rvmType);
if (jdkType == null)
continue; // won't need the field info
Key key = new Key(jdkType);
fieldInfo = bootImageTypeFields.get(key);
if (fieldInfo != null) {
fieldInfo.rvmType = rvmType;
} else {
if (verbose >= 1) say("making fieldinfo for " + rvmType);
fieldInfo = new FieldInfo(jdkType, rvmType);
bootImageTypeFields.put(key, fieldInfo);
// Now do all the superclasses if they don't already exist
// Can't add them in next loop as Iterator's don't allow updates to collection
for (Class<?> cls = jdkType.getSuperclass(); cls != null; cls = cls.getSuperclass()) {
key = new Key(cls);
fieldInfo = bootImageTypeFields.get(key);
if (fieldInfo != null) {
break;
} else {
if (verbose >= 1) say("making fieldinfo for " + jdkType);
fieldInfo = new FieldInfo(cls, null);
bootImageTypeFields.put(key, fieldInfo);
}
}
}
}
// Now build the one-to-one instance and static field maps
for (FieldInfo fieldInfo : bootImageTypeFields.values()) {
RVMType rvmType = fieldInfo.rvmType;
if (rvmType == null) {
if (verbose >= 1) say("bootImageTypeField entry has no rvmType:"+fieldInfo.jdkType);
continue;
}
Class<?> jdkType = fieldInfo.jdkType;
if (verbose >= 1) say("building static and instance fieldinfo for " + rvmType);
// First the static fields
//
RVMField[] rvmFields = rvmType.getStaticFields();
fieldInfo.jdkStaticFields = new Field[rvmFields.length];
for (int j = 0; j < rvmFields.length; j++) {
String rvmName = rvmFields[j].getName().toString();
for (Field f : fieldInfo.jdkFields) {
if (f.getName().equals(rvmName)) {
fieldInfo.jdkStaticFields[j] = f;
f.setAccessible(true);
break;
}
}
}
// Now the instance fields
//
rvmFields = rvmType.getInstanceFields();
fieldInfo.jdkInstanceFields = new Field[rvmFields.length];
for (int j = 0; j<rvmFields.length; j++) {
String rvmName = rvmFields[j].getName().toString();
// We look only in the JDK type that corresponds to the
// RVMType of the field's declaring class.
// This is the only way to correctly handle private fields.
jdkType = getJdkType(rvmFields[j].getDeclaringClass());
if (jdkType == null) continue;
FieldInfo jdkFieldInfo = bootImageTypeFields.get(new Key(jdkType));
if (jdkFieldInfo == null) continue;
Field[] jdkFields = jdkFieldInfo.jdkFields;
for (Field f : jdkFields) {
if (f.getName().equals(rvmName)) {
fieldInfo.jdkInstanceFields[j] = f;
f.setAccessible(true);
break;
}
}
}
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: \tcreating type mapping "+(stopTime-startTime)+" ms");
}
//
// Create stack, thread, and processor context in which rvm will begin
// execution.
startupThread = RVMThread.setupBootThread();
byte[] stack = startupThread.getStack();
// sanity check for bootstrap loader
int idx = stack.length - 1;
if (VM.LittleEndian) {
stack[idx--] = (byte)0xde;
stack[idx--] = (byte)0xad;
stack[idx--] = (byte)0xba;
stack[idx--] = (byte)0xbe;
} else {
stack[idx--] = (byte)0xbe;
stack[idx--] = (byte)0xba;
stack[idx--] = (byte)0xad;
stack[idx--] = (byte)0xde;
}
//
// Tell rvm where to find itself at execution time.
// This may not be the same place it was at build time, ie. if image is
// moved to another machine with different directory structure.
//
BootstrapClassLoader.setBootstrapRepositories(bootImageRepositoriesAtExecutionTime);
//
// Finally, populate jtoc with static field values.
// This is equivalent to the RVMClass.initialize() phase that would have
// executed each class's static constructors at run time. We simulate
// this by copying statics created in the host rvm into the appropriate
// slots of the jtoc.
//
if (verbose >= 1) say("populating jtoc with static fields");
if (profile) startTime = System.currentTimeMillis();
for (RVMType rvmType : bootImageTypes.values()) {
if (verbose >= 1) say(" jtoc for ", rvmType.toString());
if (!rvmType.isClassType())
continue; // arrays and primitives have no static fields
Class<?> jdkType = getJdkType(rvmType);
if (jdkType == null && verbose >= 1) {
say("host has no class \"" + rvmType + "\"");
}
RVMField[] rvmFields = rvmType.getStaticFields();
for (int j = 0; j < rvmFields.length; ++j) {
RVMField rvmField = rvmFields[j];
TypeReference rvmFieldType = rvmField.getType();
Offset rvmFieldOffset = rvmField.getOffset();
String rvmFieldName = rvmField.getName().toString();
Field jdkFieldAcc = null;
if (jdkType!=null &&
jdkType.equals(java.util.concurrent.locks.AbstractQueuedSynchronizer.class)) {
RVMClass c=(RVMClass)rvmType;
if (rvmFieldName.equals("stateOffset")) {
Statics.setSlotContents(
rvmFieldOffset,
c.findDeclaredField(Atom.findOrCreateAsciiAtom("state")).getOffset().toLong());
continue;
} else if (rvmFieldName.equals("headOffset")) {
Statics.setSlotContents(
rvmFieldOffset,
c.findDeclaredField(Atom.findOrCreateAsciiAtom("head")).getOffset().toLong());
continue;
} else if (rvmFieldName.equals("tailOffset")) {
Statics.setSlotContents(
rvmFieldOffset,
c.findDeclaredField(Atom.findOrCreateAsciiAtom("tail")).getOffset().toLong());
continue;
} else if (rvmFieldName.equals("waitStatusOffset")) {
try {
Statics.setSlotContents(
rvmFieldOffset,
((RVMClass)getRvmType(Class.forName("java.util.concurrent.locks.AbstractQueuedSynchronizer$Node"))).findDeclaredField(Atom.findOrCreateAsciiAtom("waitStatus")).getOffset().toLong());
} catch (ClassNotFoundException e) {
throw new Error(e);
}
continue;
}
} else if (jdkType!=null &&
jdkType.equals(java.util.concurrent.locks.LockSupport.class)) {
RVMClass c=(RVMClass)rvmType;
if (rvmFieldName.equals("parkBlockerOffset")) {
Statics.setSlotContents(
rvmFieldOffset,
((RVMClass)getRvmType(java.lang.Thread.class)).findDeclaredField(Atom.findOrCreateAsciiAtom("parkBlocker")).getOffset().toLong());
continue;
}
}
if (jdkType != null)
jdkFieldAcc = getJdkFieldAccessor(jdkType, j, STATIC_FIELD);
if (jdkFieldAcc == null) {
// we failed to get a reflective field accessors
if (jdkType != null) {
// we know the type - probably a private field of a java.lang class
if(!copyKnownStaticField(jdkType,
rvmFieldName,
rvmFieldType,
rvmFieldOffset)) {
// we didn't know the field so nullify
if (verbose >= 2) {
traceContext.push(rvmFieldType.toString(),
jdkType.getName(), rvmFieldName);
traceContext.traceFieldNotInHostJdk();
traceContext.pop();
}
Statics.setSlotContents(rvmFieldOffset, 0);
if (!VM.runningTool)
bootImage.countNulledReference();
invalidEntrys.add(jdkType.getName());
}
} else {
// no accessor and we don't know the type so nullify
if (verbose >= 2) {
traceContext.push(rvmFieldType.toString(),
rvmFieldType.toString(), rvmFieldName);
traceContext.traceFieldNotInHostJdk();
traceContext.pop();
}
Statics.setSlotContents(rvmFieldOffset, 0);
if (!VM.runningTool)
bootImage.countNulledReference();
invalidEntrys.add(rvmField.getDeclaringClass().toString());
}
continue;
}
if (! Modifier.isStatic(jdkFieldAcc.getModifiers())) {
if (verbose >= 2) traceContext.push(rvmFieldType.toString(),
jdkType.getName(), rvmFieldName);
if (verbose >= 2) traceContext.traceFieldNotStaticInHostJdk();
if (verbose >= 2) traceContext.pop();
Statics.setSlotContents(rvmFieldOffset, 0);
if (!VM.runningTool)
bootImage.countNulledReference();
invalidEntrys.add(jdkType.getName());
continue;
}
if(!equalTypes(jdkFieldAcc.getType().getName(), rvmFieldType)) {
if (verbose >= 2) traceContext.push(rvmFieldType.toString(),
jdkType.getName(), rvmFieldName);
if (verbose >= 2) traceContext.traceFieldDifferentTypeInHostJdk();
if (verbose >= 2) traceContext.pop();
Statics.setSlotContents(rvmFieldOffset, 0);
if (!VM.runningTool)
bootImage.countNulledReference();
invalidEntrys.add(jdkType.getName());
continue;
}
if (verbose >= 2)
say(" populating jtoc slot ", String.valueOf(Statics.offsetAsSlot(rvmFieldOffset)),
" with ", rvmField.toString());
if (rvmFieldType.isPrimitiveType()) {
// field is logical or numeric type
if (rvmFieldType.isBooleanType()) {
Statics.setSlotContents(rvmFieldOffset, jdkFieldAcc.getBoolean(null) ? 1 : 0);
} else if (rvmFieldType.isByteType()) {
Statics.setSlotContents(rvmFieldOffset, jdkFieldAcc.getByte(null));
} else if (rvmFieldType.isCharType()) {
Statics.setSlotContents(rvmFieldOffset, jdkFieldAcc.getChar(null));
} else if (rvmFieldType.isShortType()) {
Statics.setSlotContents(rvmFieldOffset, jdkFieldAcc.getShort(null));
} else if (rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, jdkFieldAcc.getInt(null));
} else if (rvmFieldType.isLongType()) {
// note: Endian issues handled in setSlotContents.
Statics.setSlotContents(rvmFieldOffset,
jdkFieldAcc.getLong(null));
} else if (rvmFieldType.isFloatType()) {
float f = jdkFieldAcc.getFloat(null);
Statics.setSlotContents(rvmFieldOffset,
Float.floatToIntBits(f));
} else if (rvmFieldType.isDoubleType()) {
double d = jdkFieldAcc.getDouble(null);
// note: Endian issues handled in setSlotContents.
Statics.setSlotContents(rvmFieldOffset,
Double.doubleToLongBits(d));
} else if (rvmFieldType.equals(TypeReference.Address) ||
rvmFieldType.equals(TypeReference.Word) ||
rvmFieldType.equals(TypeReference.Extent) ||
rvmFieldType.equals(TypeReference.Offset)){
Object o = jdkFieldAcc.get(null);
String msg = " static field " + rvmField.toString();
boolean warn = rvmFieldType.equals(TypeReference.Address);
Statics.setSlotContents(rvmFieldOffset, getWordValue(o, msg, warn));
} else {
fail("unexpected primitive field type: " + rvmFieldType);
}
} else {
// field is reference type
final Object o = jdkFieldAcc.get(null);
if (verbose >= 3)
say(" setting with ", VM.addressAsHexString(Magic.objectAsAddress(o)));
Statics.setSlotContents(rvmFieldOffset, o);
}
}
}
if (verbose >= 2) {
for (final String entry : invalidEntrys) {
say("Static fields of type are invalid: ", entry);
}
}
if (profile) {
stopTime = System.currentTimeMillis();
System.out.println("PROF: \tinitializing jtoc "+(stopTime-startTime)+" ms");
}
}
private static boolean equalTypes(final String name, final TypeReference rvmFieldType) {
final String descriptor = rvmFieldType.getName().toString();
if (name.equals("int")) return descriptor.equals("I");
else if (name.equals("boolean")) return descriptor.equals("Z");
else if (name.equals("byte")) return descriptor.equals("B");
else if (name.equals("char")) return descriptor.equals("C");
else if (name.equals("double")) return descriptor.equals("D");
else if (name.equals("float")) return descriptor.equals("F");
else if (name.equals("long")) return descriptor.equals("J");
else if (name.equals("short")) return descriptor.equals("S");
else if (name.startsWith("[")) {
return name.replace('.','/').equals(descriptor);
} else {
return ('L' + name.replace('.', '/') + ';').equals(descriptor);
}
}
private static final int LARGE_ARRAY_SIZE = 16*1024;
private static final int LARGE_SCALAR_SIZE = 1024;
private static int depth = -1;
private static int jtocCount = -1;
private static final String SPACES = " ";
private static void check(Word value, String msg) {
Word low = ObjectModel.maximumObjectRef(Address.zero()).toWord(); // yes, max
Word high = Word.fromIntZeroExtend(0x10000000); // we shouldn't have that many objects
if (value.GT(low) && value.LT(high) && !value.EQ(Word.fromIntZeroExtend(32767)) &&
(value.LT(Word.fromIntZeroExtend(4088)) || value.GT(Word.fromIntZeroExtend(4096)))) {
say("Warning: Suspicious Address value of ", VM.addressAsHexString(value.toAddress()),
" written for " + msg);
}
}
private static Word getWordValue(Object addr, String msg, boolean warn) {
if (addr == null) return Word.zero();
Word value = Word.zero();
if (addr instanceof Address) {
value = ((Address)addr).toWord();
} else if (addr instanceof ObjectReference) {
value = ((ObjectReference)addr).toAddress().toWord();
} else if (addr instanceof Word) {
value = (Word)addr;
} else if (addr instanceof Extent) {
value = ((Extent)addr).toWord();
} else if (addr instanceof Offset) {
value = ((Offset)addr).toWord();
} else {
say("Unhandled supposed address value: " + addr);
say(msg);
fail("incomplete boot image support");
}
if (warn) check(value, msg);
return value;
}
/**
* Write a field that contains a reference to the boot image
* @param fieldLocation address in boot image of field
* @param referencedObject the object whose address will be written at this
* location
* @param parentObject object containing this fieldLocation
* @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
*/
private static void copyReferenceFieldToBootImage(Address fieldLocation, Object referencedObject,
Object parentObject, boolean objField, boolean root, String rvmFieldName,
TypeReference rvmFieldType) throws IllegalAccessException {
if (referencedObject == null) {
bootImage.setNullAddressWord(fieldLocation, objField, root, true);
} else {
BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(referencedObject);
if (mapEntry.imageAddress.EQ(OBJECT_NOT_PRESENT)) {
if (rvmFieldName == null || !copyKnownInstanceField(parentObject, rvmFieldName, rvmFieldType, fieldLocation)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(fieldLocation, objField, root, false);
}
} else if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
Address imageAddress;
if (true) {
// Normal collection based traversal
mapEntry.addLinkingAddress(fieldLocation, objField, root, rvmFieldName, rvmFieldType, parentObject);
if (!mapEntry.isPendingEntry()) {
mapEntry.setPendingEntry();
pendingEntries.add(mapEntry);
}
imageAddress = OBJECT_ALLOCATION_DEFERRED;
root = false;
} else {
// Recurse placing work on the stack
mapEntry.imageAddress = copyToBootImage(referencedObject, false, Address.max(), parentObject, false);
imageAddress = mapEntry.imageAddress;
}
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
if (!copyKnownInstanceField(parentObject, rvmFieldName, rvmFieldType, fieldLocation)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(fieldLocation, objField, root, false);
}
} else {
bootImage.setAddressWord(fieldLocation, imageAddress.toWord(), objField, root);
}
} else {
bootImage.setAddressWord(fieldLocation, mapEntry.imageAddress.toWord(), objField, root);
}
}
}
/**
* Process any entries that have been deferred
* @throws IllegalAccessException
*/
private static void processPendingEntries() throws IllegalAccessException {
while (!pendingEntries.isEmpty()) {
BootImageMap.Entry mapEntry = pendingEntries.remove();
mapEntry.clearPendingEntry();
if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
mapEntry.imageAddress = copyToBootImage(mapEntry.jdkObject, false, Address.max(), null, false);
}
fixupLinkAddresses(mapEntry);
}
}
/**
* Iterate over link address registered with entry writing out boot image address
* @param mapEntry entry containing addresses to fix up
* @return number of entries fixed up
* @throws IllegalAccessException
*/
private static int fixupLinkAddresses(BootImageMap.Entry mapEntry) throws IllegalAccessException {
int count = 0;
BootImageMap.Entry.LinkInfo info = mapEntry.removeLinkingAddress();
while(info != null) {
if (mapEntry.imageAddress.EQ(OBJECT_NOT_PRESENT)) {
if (info.rvmFieldName == null || !copyKnownInstanceField(info.parent, info.rvmFieldName, info.rvmFieldType, info.addressToFixup)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(info.addressToFixup, info.objField, info.root, false);
}
} else {
bootImage.setAddressWord(info.addressToFixup, mapEntry.imageAddress.toWord(), info.objField, info.root);
}
info = mapEntry.removeLinkingAddress();
count++;
}
return count;
}
/**
* Copy an object (and, recursively, any of its fields or elements that
* are references) from host jdk address space into image.
*
* @param jdkObject object to be copied
* @param allocOnly if allocOnly is true, the TIB and other reference fields are not recursively copied
* @param overwriteAddress if !overwriteAddress.isMax(), then copy object to given address
* @param parentObject
* @param untraced Do not report any fields of this object as references
* @return offset of copied object within image, in bytes
* (OBJECT_NOT_PRESENT --> object not copied:
* it's not part of bootimage)
*/
private static Address copyToBootImage(Object jdkObject, boolean allocOnly,
Address overwriteAddress, Object parentObject, boolean untraced) throws IllegalAccessException
{
try {
// Return object if it is already copied and not being overwritten
BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(jdkObject);
if ((!mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) && overwriteAddress.isMax()) {
return mapEntry.imageAddress;
}
if (verbose >= 2) depth++;
// fetch object's type information
Class<?> jdkType = jdkObject.getClass();
RVMType rvmType = getRvmType(jdkType);
if (rvmType == null) {
if (verbose >= 2) traverseObject(jdkObject);
if (verbose >= 2) depth--;
return OBJECT_NOT_PRESENT; // object not part of bootimage
}
// copy object to image
if (jdkType.isArray()) {
// allocate space in image prior to recursing
int arrayCount = Array.getLength(jdkObject);
RVMArray rvmArrayType = rvmType.asArray();
boolean needsIdentityHash = mapEntry.requiresIdentityHashCode();
int identityHashValue = mapEntry.getIdentityHashCode();
Address arrayImageAddress = (overwriteAddress.isMax()) ? bootImage.allocateArray(rvmArrayType, arrayCount, needsIdentityHash, identityHashValue) : overwriteAddress;
mapEntry.imageAddress = arrayImageAddress;
mapEntry.imageAddress = copyArrayToBootImage(arrayCount, arrayImageAddress, jdkObject, jdkType,
rvmArrayType, allocOnly, overwriteAddress, parentObject, untraced);
// copy object's type information block into image, if it's not there
// already
if (!allocOnly) {
if (verbose >= 2) traceContext.push("", jdkObject.getClass().getName(), "tib");
Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), allocOnly, Address.max(), jdkObject, false);
if (verbose >= 2) traceContext.pop();
if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
fail("can't copy tib for " + jdkObject);
}
ObjectModel.setTIB(bootImage, mapEntry.imageAddress, tibImageAddress, rvmType);
}
} else if (rvmType == RVMType.ObjectReferenceArrayType || rvmType.getTypeRef().isRuntimeTable()) {
Object backing = ((RuntimeTable<?>)jdkObject).getBacking();
/* Copy the backing array, and then replace its TIB */
mapEntry.imageAddress = copyToBootImage(backing, allocOnly, overwriteAddress, jdkObject, rvmType.getTypeRef().isRuntimeTable());
if (!allocOnly) {
copyTIBToBootImage(rvmType, jdkObject, mapEntry.imageAddress);
}
} else if (jdkObject instanceof RuntimeTable) {
Object backing = ((RuntimeTable<?>)jdkObject).getBacking();
mapEntry.imageAddress = copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
} else if (rvmType == RVMType.CodeArrayType) {
// Handle the code array that is represented as either byte or int arrays
if (verbose >= 2) depth--;
Object backing = ((CodeArray)jdkObject).getBacking();
mapEntry.imageAddress = copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
} else if (rvmType.getTypeRef().isMagicType()) {
say("Unhandled copying of magic type: " + rvmType.getDescriptor().toString() +
" in object of type " + parentObject.getClass().toString());
fail("incomplete boot image support");
} else {
// allocate space in image
if (rvmType instanceof RVMArray) fail("This isn't a scalar " + rvmType);
RVMClass rvmScalarType = rvmType.asClass();
boolean needsIdentityHash = mapEntry.requiresIdentityHashCode();
int identityHashValue = mapEntry.getIdentityHashCode();
Address scalarImageAddress = (overwriteAddress.isMax()) ? bootImage.allocateScalar(rvmScalarType, needsIdentityHash, identityHashValue) : overwriteAddress;
mapEntry.imageAddress = scalarImageAddress;
mapEntry.imageAddress = copyClassToBootImage(scalarImageAddress, jdkObject, jdkType, rvmScalarType,
allocOnly, overwriteAddress, parentObject, untraced);
// copy object's type information block into image, if it's not there
// already
if (!allocOnly) {
copyTIBToBootImage(rvmType, jdkObject, mapEntry.imageAddress);
}
}
if (verbose >= 2) depth--;
return mapEntry.imageAddress;
} catch (Error e) {
e = new Error(e.getMessage()+ "\nwhile copying " +
jdkObject + (jdkObject != null ? ":"+jdkObject.getClass():"") + " from " +
parentObject + (parentObject != null ? ":"+parentObject.getClass():""),
e.getCause() != null? e.getCause() : e);
throw e;
}
}
/**
* Allocate and set TIB
*
* @param rvmType type for TIB
* @param jdkObject parent object
* @param imageAddress address of object to set TIB of
* @throws IllegalAccessException
*/
private static void copyTIBToBootImage(RVMType rvmType, Object jdkObject, Address imageAddress) throws IllegalAccessException {
if (verbose >= 2) {
depth--;
traceContext.push("", jdkObject.getClass().getName(), "tib");
}
Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), false, Address.max(), jdkObject, false);
if (verbose >= 2) {
traceContext.pop();
depth++;
}
if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
fail("can't copy tib for " + jdkObject);
}
ObjectModel.setTIB(bootImage, imageAddress, tibImageAddress, rvmType);
}
/**
* Write an object instantiating a class to the boot image
* @param scalarImageAddress address already allocated for object
* @param jdkObject object to write
* @param jdkType java.lang.Class of object
* @param rvmScalarType RVM class loader version of type
* @param allocOnly allocate the object only?
* @param overwriteAddress
* @param parentObject
* @param untraced
* @return
* @throws IllegalAccessException
*/
private static Address copyClassToBootImage(Address scalarImageAddress, Object jdkObject, Class<?> jdkType,
RVMClass rvmScalarType, boolean allocOnly, Address overwriteAddress, Object parentObject, boolean untraced)
throws IllegalAccessException {
if (verbose >= 2) {
if (depth == depthCutoff)
say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
else if (depth < depthCutoff) {
String tab = SPACES.substring(0,depth+1);
if (depth == 0 && jtocCount >= 0)
tab = tab + "jtoc #" + String.valueOf(jtocCount) + " ";
int scalarSize = rvmScalarType.getInstanceSize();
say(tab, "Copying object ", jdkType.getName(),
" size=", String.valueOf(scalarSize),
(scalarSize >= LARGE_SCALAR_SIZE) ? " large object!!!" : "");
}
}
// copy object fields from host jdk address space into image
// recurse on values that are references
RVMField[] rvmFields = rvmScalarType.getInstanceFields();
for (int i = 0; i < rvmFields.length; ++i) {
RVMField rvmField = rvmFields[i];
TypeReference rvmFieldType = rvmField.getType();
Address rvmFieldAddress = scalarImageAddress.plus(rvmField.getOffset());
String rvmFieldName = rvmField.getName().toString();
Field jdkFieldAcc = getJdkFieldAccessor(jdkType, i, INSTANCE_FIELD);
boolean untracedField = rvmField.isUntraced() || untraced;
if (jdkFieldAcc == null) {
// Field not found via reflection
if (!copyKnownInstanceField(jdkObject, rvmFieldName, rvmFieldType, rvmFieldAddress)) {
// Field wasn't a known Classpath field so write null
if (verbose >= 2) traceContext.push(rvmFieldType.toString(),
jdkType.getName(), rvmFieldName);
if (verbose >= 2) traceContext.traceFieldNotInHostJdk();
if (verbose >= 2) traceContext.pop();
if (rvmFieldType.isPrimitiveType()) {
switch (rvmField.getType().getMemoryBytes()) {
case 1: bootImage.setByte(rvmFieldAddress, 0); break;
case 2: bootImage.setHalfWord(rvmFieldAddress, 0); break;
case 4: bootImage.setFullWord(rvmFieldAddress, 0); break;
case 8: bootImage.setDoubleWord(rvmFieldAddress, 0L); break;
default:fail("unexpected field type: " + rvmFieldType); break;
}
} else {
bootImage.setNullAddressWord(rvmFieldAddress, !untracedField, !untracedField, false);
}
}
continue;
}
if (rvmFieldType.isPrimitiveType()) {
// field is logical or numeric type
if (rvmFieldType.isBooleanType()) {
bootImage.setByte(rvmFieldAddress,
jdkFieldAcc.getBoolean(jdkObject) ? 1 : 0);
} else if (rvmFieldType.isByteType()) {
bootImage.setByte(rvmFieldAddress,
jdkFieldAcc.getByte(jdkObject));
} else if (rvmFieldType.isCharType()) {
bootImage.setHalfWord(rvmFieldAddress,
jdkFieldAcc.getChar(jdkObject));
} else if (rvmFieldType.isShortType()) {
bootImage.setHalfWord(rvmFieldAddress,
jdkFieldAcc.getShort(jdkObject));
} else if (rvmFieldType.isIntType()) {
try {
bootImage.setFullWord(rvmFieldAddress, jdkFieldAcc.getInt(jdkObject));
} catch (IllegalArgumentException ex) {
// TODO: Harmony - clean this up
if (jdkObject instanceof java.util.WeakHashMap && rvmFieldName.equals("loadFactor")) {
// the field load factor field in Sun/Classpath is a float but
// in Harmony it has been "optimized" to an int
bootImage.setFullWord(rvmFieldAddress, 7500);
} else if (jdkObject instanceof java.lang.ref.ReferenceQueue && rvmFieldName.equals("head")) {
// Conflicting types between Harmony and Sun
bootImage.setFullWord(rvmFieldAddress, 0);
} else {
System.out.println("type " + rvmScalarType + ", field " + rvmField);
throw ex;
}
}
} else if (rvmFieldType.isLongType()) {
bootImage.setDoubleWord(rvmFieldAddress,
jdkFieldAcc.getLong(jdkObject));
} else if (rvmFieldType.isFloatType()) {
float f = jdkFieldAcc.getFloat(jdkObject);
bootImage.setFullWord(rvmFieldAddress,
Float.floatToIntBits(f));
} else if (rvmFieldType.isDoubleType()) {
double d = jdkFieldAcc.getDouble(jdkObject);
bootImage.setDoubleWord(rvmFieldAddress,
Double.doubleToLongBits(d));
} else if (rvmFieldType.equals(TypeReference.Address) ||
rvmFieldType.equals(TypeReference.Word) ||
rvmFieldType.equals(TypeReference.Extent) ||
rvmFieldType.equals(TypeReference.Offset)) {
Object o = jdkFieldAcc.get(jdkObject);
String msg = " instance field " + rvmField.toString();
boolean warn = rvmFieldType.equals(TypeReference.Address);
bootImage.setAddressWord(rvmFieldAddress, getWordValue(o, msg, warn), false, false);
} else {
fail("unexpected primitive field type: " + rvmFieldType);
}
} else {
// field is reference type
Object value = jdkFieldAcc.get(jdkObject);
if (!allocOnly) {
Class<?> jdkClass = jdkFieldAcc.getDeclaringClass();
if (verbose >= 2) traceContext.push(value.getClass().getName(),
jdkClass.getName(),
jdkFieldAcc.getName());
copyReferenceFieldToBootImage(rvmFieldAddress, value, jdkObject,
!untracedField, !(untracedField || rvmField.isFinal()), rvmFieldName, rvmFieldType);
}
}
}
return scalarImageAddress;
}
/**
* Write array to boot image
* @param arrayCount
* @param arrayImageAddress
* @param jdkObject
* @param jdkType
* @param rvmArrayType
* @param allocOnly
* @param overwriteAddress
* @param parentObject
* @param untraced
* @return
* @throws IllegalAccessException
*/
private static Address copyArrayToBootImage(int arrayCount, Address arrayImageAddress, Object jdkObject, Class<?> jdkType, RVMArray rvmArrayType,
boolean allocOnly, Address overwriteAddress, Object parentObject, boolean untraced)
throws IllegalAccessException {
if (verbose >= 2) {
if (depth == depthCutoff)
say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
else if (depth < depthCutoff) {
String tab = SPACES.substring(0,depth+1);
if (depth == 0 && jtocCount >= 0)
tab = tab + "jtoc #" + String.valueOf(jtocCount) + ": ";
int arraySize = rvmArrayType.getInstanceSize(arrayCount);
say(tab, "Copying array ", jdkType.getName(),
" length=", String.valueOf(arrayCount),
(arraySize >= LARGE_ARRAY_SIZE) ? " large object!!!" : "");
}
}
RVMType rvmElementType = rvmArrayType.getElementType();
// copy array elements from host jdk address space into image
// recurse on values that are references
if (rvmElementType.isPrimitiveType()) {
// array element is logical or numeric type
if (rvmElementType.equals(RVMType.BooleanType)) {
boolean[] values = (boolean[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setByte(arrayImageAddress.plus(i), values[i] ? 1 : 0);
} else if (rvmElementType.equals(RVMType.ByteType)) {
byte[] values = (byte[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setByte(arrayImageAddress.plus(i), values[i]);
} else if (rvmElementType.equals(RVMType.CharType)) {
char[] values = (char[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setHalfWord(arrayImageAddress.plus(i << LOG_BYTES_IN_CHAR), values[i]);
} else if (rvmElementType.equals(RVMType.ShortType)) {
short[] values = (short[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setHalfWord(arrayImageAddress.plus(i << LOG_BYTES_IN_SHORT), values[i]);
} else if (rvmElementType.equals(RVMType.IntType)) {
int[] values = (int[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_INT), values[i]);
} else if (rvmElementType.equals(RVMType.LongType)) {
long[] values = (long[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setDoubleWord(arrayImageAddress.plus(i << LOG_BYTES_IN_LONG), values[i]);
} else if (rvmElementType.equals(RVMType.FloatType)) {
float[] values = (float[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_FLOAT),
Float.floatToIntBits(values[i]));
} else if (rvmElementType.equals(RVMType.DoubleType)) {
double[] values = (double[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setDoubleWord(arrayImageAddress.plus(i << LOG_BYTES_IN_DOUBLE),
Double.doubleToLongBits(values[i]));
} else {
fail("unexpected primitive array type: " + rvmArrayType);
}
} else {
// array element is reference type
boolean isTIB = parentObject instanceof TIB;
Object[] values = (Object []) jdkObject;
Class<?> jdkClass = jdkObject.getClass();
if (!allocOnly) {
for (int i = 0; i<arrayCount; ++i) {
if (values[i] != null) {
if (verbose >= 2) traceContext.push(values[i].getClass().getName(), jdkClass.getName(), i);
if (isTIB && values[i] instanceof Word) {
bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), (Word)values[i], false, false);
} else if (isTIB && values[i] == LazyCompilationTrampoline.instructions) {
Address codeAddress = arrayImageAddress.plus(((TIB)parentObject).lazyMethodInvokerTrampolineIndex() << LOG_BYTES_IN_ADDRESS);
bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), codeAddress.toWord(), false, false);
} else {
copyReferenceFieldToBootImage(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), values[i],
jdkObject, !untraced, !untraced, null, null);
}
if (verbose >= 2) traceContext.pop();
} else {
bootImage.setNullAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), !untraced, !untraced, true);
}
}
}
}
return arrayImageAddress;
}
/**
* Copy a unboxed array type to the boot image
* @param jdkObject object representation
* @param rvmArrayType type of array
* @param allocOnly allocate object don't write to fields
* @param overwriteAddress addresss to write to if overwriting
* @param parentObject object containing array
* @return address of array
* @throws IllegalAccessException
*/
private static Address copyMagicArrayToBootImage(Object jdkObject,
RVMArray rvmArrayType,
boolean allocOnly,
Address overwriteAddress,
Object parentObject)
throws IllegalAccessException {
// Return object if it is already copied and not being overwritten
BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(jdkObject);
if ((!mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) && overwriteAddress.isMax()) {
return mapEntry.imageAddress;
}
if (verbose >= 2) depth++;
RVMType rvmElementType = rvmArrayType.getElementType();
// allocate space in image
int arrayCount = Array.getLength(jdkObject);
Address arrayImageAddress;
if (overwriteAddress.isMax()) {
if (rvmElementType.equals(RVMType.CodeType)) {
arrayImageAddress = bootImage.allocateCode(rvmArrayType, arrayCount);
} else {
boolean needsIdentityHash = mapEntry.requiresIdentityHashCode();
int identityHashValue = mapEntry.getIdentityHashCode();
arrayImageAddress = bootImage.allocateArray(rvmArrayType, arrayCount, needsIdentityHash, identityHashValue);
}
} else {
arrayImageAddress = overwriteAddress;
}
mapEntry.imageAddress = arrayImageAddress;
if (verbose >= 2) {
if (depth == depthCutoff)
say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
else if (depth < depthCutoff) {
String tab = SPACES.substring(0,depth+1);
if (depth == 0 && jtocCount >= 0)
tab = tab + "jtoc #" + String.valueOf(jtocCount) + ": ";
int arraySize = rvmArrayType.getInstanceSize(arrayCount);
say(tab, "Copying array ", rvmArrayType.toString(),
" length=", String.valueOf(arrayCount),
(arraySize >= LARGE_ARRAY_SIZE) ? " large object!!!" : "");
}
}
// copy array elements from host jdk address space into image
if (rvmElementType.equals(RVMType.CodeType)) {
if (VM.BuildForIA32) {
byte[] values = (byte[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setByte(arrayImageAddress.plus(i), values[i]);
} else {
int[] values = (int[]) jdkObject;
for (int i = 0; i < arrayCount; ++i)
bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_INT), values[i]);
}
} else if (rvmElementType.equals(RVMType.AddressType)) {
Address[] values = (Address[]) jdkObject;
for (int i=0; i<arrayCount; i++) {
Address addr = values[i];
String msg = "Address array element";
bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS),
getWordValue(addr, msg, true), false, false);
}
} else if (rvmElementType.equals(RVMType.WordType)) {
Word[] values = (Word[]) jdkObject;
for (int i = 0; i < arrayCount; i++) {
String msg = "Word array element ";
Word addr = values[i];
bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS),
getWordValue(addr, msg, false), false, false);
}
} else if (rvmElementType.equals(RVMType.OffsetType)) {
Offset[] values = (Offset[]) jdkObject;
for (int i = 0; i < arrayCount; i++) {
String msg = "Offset array element " + i;
Offset addr = values[i];
bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS),
getWordValue(addr, msg, false), false, false);
}
} else if (rvmElementType.equals(RVMType.ExtentType)) {
Extent[] values = (Extent[]) jdkObject;
for (int i = 0; i < arrayCount; i++) {
String msg = "Extent array element ";
Extent addr = values[i];
bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS),
getWordValue(addr, msg, false), false, false);
}
} else {
fail("unexpected magic array type: " + rvmArrayType);
}
// copy object's TIB into image, if it's not there already
if (!allocOnly) {
copyTIBToBootImage(rvmArrayType, jdkObject, mapEntry.imageAddress);
}
if (verbose >= 2) depth--;
return mapEntry.imageAddress;
}
/**
* If we can't find a field via reflection we may still determine
* and copy a value because we know the internals of Classpath.
* @param jdkType the class containing the field
* @param rvmFieldName the name of the field
* @param rvmFieldType the type reference of the field
*/
private static boolean copyKnownStaticField(Class<?> jdkType, String rvmFieldName,
TypeReference rvmFieldType,
Offset rvmFieldOffset) {
if (classLibrary == "harmony") {
if (jdkType.equals(java.lang.Number.class)) {
throw new Error("Unknown field in java.lang.Number " + rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.Boolean.class)) {
throw new Error("Unknown field in java.lang.Boolean "+ rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.Byte.class)) {
if (rvmFieldName.equals("CACHE") && rvmFieldType.isArrayType()) {
Statics.setSlotContents(rvmFieldOffset, new Byte[256]);
return true;
} else {
throw new Error("Unknown field in java.lang.Byte " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Double.class)) {
throw new Error("Unknown field in java.lang.Double " + rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.Float.class)) {
throw new Error("Unknown field in java.lang.Float " + rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.Integer.class)) {
if (rvmFieldName.equals("decimalScale") && rvmFieldType.isArrayType()) {
int[] java_lang_Integer_decimalScale = new int[] { 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };
Statics.setSlotContents(rvmFieldOffset, java_lang_Integer_decimalScale);
return true;
} else {
throw new Error("Unknown field in java.lang.Integer " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Long.class)) {
throw new Error("Unknown field in java.lang.Long " + rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.Short.class)) {
throw new Error("Unknown field in java.lang.Short " + rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.util.HashMap.class)) {
if (rvmFieldName.equals("DEFAULT_SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 16);
return true;
} else {
throw new Error("Unknown field in java.util.HashMap " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.util.AbstractMap.class)) {
throw new Error("Unknown field in java.util.AbstractMap " + rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.ref.ReferenceQueue.class)) {
if (rvmFieldName.equals("DEFAULT_QUEUE_SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 128);
return true;
} else {
throw new Error("Unknown field in java.lang.ref.ReferenceQueue " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Throwable.class)) {
if (rvmFieldName.equals("zeroLengthStackTrace") && rvmFieldType.isArrayType()) {
Statics.setSlotContents(rvmFieldOffset, new StackTraceElement[0]);
return true;
} else {
throw new Error("Unknown field in java.lang.Throwable " + rvmFieldName + " " + rvmFieldType);
}
} else {
return false;
}
} else if (classLibrary == "classpath") {
if (jdkType.equals(java.lang.Number.class)) {
if (rvmFieldName.equals("digits") && rvmFieldType.isArrayType()) {
char[] java_lang_Number_digits = new char[]{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z'
};
Statics.setSlotContents(rvmFieldOffset, java_lang_Number_digits);
return true;
} else {
throw new Error("Unknown field in java.lang.Number " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Boolean.class)) {
throw new Error("Unknown field in java.lang.Boolean "+ rvmFieldName + " " + rvmFieldType);
} else if (jdkType.equals(java.lang.Byte.class)) {
if (rvmFieldName.equals("byteCache") && rvmFieldType.isArrayType()) {
Byte[] java_lang_Byte_byteCache = new Byte[256];
// Populate table
for(int i=-128; i < 128; i++) {
Byte value = (byte) i;
BootImageMap.findOrCreateEntry(value);
java_lang_Byte_byteCache[128+i] = value;
}
Statics.setSlotContents(rvmFieldOffset, java_lang_Byte_byteCache);
return true;
} else if (rvmFieldName.equals("MIN_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, -128);
return true;
} else if (rvmFieldName.equals("MAX_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 127);
return true;
} else if (rvmFieldName.equals("SIZE") && rvmFieldType.isIntType()){
Statics.setSlotContents(rvmFieldOffset, 8); // NB not present in Java 1.4
return true;
} else {
throw new Error("Unknown field in java.lang.Byte " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Double.class)) {
if (rvmFieldName.equals("ZERO")) {
Statics.setSlotContents(rvmFieldOffset, Double.valueOf(0.0));
return true;
} else if (rvmFieldName.equals("ONE")) {
Statics.setSlotContents(rvmFieldOffset, Double.valueOf(1.0));
return true;
} else if (rvmFieldName.equals("SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 64); // NB not present in Java 1.4
return true;
} else {
throw new Error("Unknown field in java.lang.Double " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Float.class)) {
if (rvmFieldName.equals("ZERO")) {
Statics.setSlotContents(rvmFieldOffset, Float.valueOf(0.0f));
return true;
} else if (rvmFieldName.equals("ONE")) {
Statics.setSlotContents(rvmFieldOffset, Float.valueOf(1.0f));
return true;
} else if (rvmFieldName.equals("SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 32); // NB not present in Java 1.4
return true;
} else {
throw new Error("Unknown field in java.lang.Float " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Integer.class)) {
if (rvmFieldName.equals("intCache") && rvmFieldType.isArrayType()) {
Integer[] java_lang_Integer_intCache = new Integer[256];
// Populate table
for(int i=-128; i < 128; i++) {
Integer value = i;
java_lang_Integer_intCache[128+i] = value;
}
Statics.setSlotContents(rvmFieldOffset, java_lang_Integer_intCache);
return true;
} else if (rvmFieldName.equals("MIN_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, -128);
return true;
} else if (rvmFieldName.equals("MAX_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 127);
return true;
} else if (rvmFieldName.equals("SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 32); // NB not present in Java 1.4
return true;
} else {
throw new Error("Unknown field in java.lang.Integer " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Long.class)) {
if (rvmFieldName.equals("longCache") && rvmFieldType.isArrayType()) {
Long[] java_lang_Long_longCache = new Long[256];
// Populate table
for(int i=-128; i < 128; i++) {
Long value = (long)i;
BootImageMap.findOrCreateEntry(value);
java_lang_Long_longCache[128+i] = value;
}
Statics.setSlotContents(rvmFieldOffset, java_lang_Long_longCache);
return true;
} else if (rvmFieldName.equals("MIN_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, -128);
return true;
} else if (rvmFieldName.equals("MAX_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 127);
return true;
} else if (rvmFieldName.equals("SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 64); // NB not present in Java 1.4
return true;
} else {
throw new Error("Unknown field in java.lang.Long " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.lang.Short.class)) {
if (rvmFieldName.equals("shortCache") && rvmFieldType.isArrayType()) {
Short[] java_lang_Short_shortCache = new Short[256];
// Populate table
for(short i=-128; i < 128; i++) {
Short value = i;
BootImageMap.findOrCreateEntry(value);
java_lang_Short_shortCache[128+i] = value;
}
Statics.setSlotContents(rvmFieldOffset, java_lang_Short_shortCache);
return true;
} else if (rvmFieldName.equals("MIN_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, -128);
return true;
} else if (rvmFieldName.equals("MAX_CACHE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 127);
return true;
} else if (rvmFieldName.equals("SIZE") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 16); // NB not present in Java 1.4
return true;
} else {
throw new Error("Unknown field in java.lang.Short " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.util.HashMap.class)) {
if (rvmFieldName.equals("DEFAULT_CAPACITY") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 11);
return true;
} else if (rvmFieldName.equals("DEFAULT_LOAD_FACTOR") && rvmFieldType.isFloatType()) {
Statics.setSlotContents(rvmFieldOffset, Float.floatToIntBits(0.75f));
return true;
} else {
throw new Error("Unknown field in java.util.HashMap " + rvmFieldName + " " + rvmFieldType);
}
} else if (jdkType.equals(java.util.AbstractMap.class)) {
if (rvmFieldName.equals("KEYS") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 0);
return true;
} else if (rvmFieldName.equals("VALUES") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 1);
return true;
} else if (rvmFieldName.equals("ENTRIES") && rvmFieldType.isIntType()) {
Statics.setSlotContents(rvmFieldOffset, 2);
return true;
} else {
throw new Error("Unknown field in java.util.AbstractMap " + rvmFieldName + " " + rvmFieldType);
}
} else {
return false;
}
} else {
throw new Error("Unknown class library: \"" + classLibrary + "\"");
}
}
/**
* If we can't find a field via reflection we may still determine
* and copy a value because we know the internals of Classpath.
* @param jdkObject the object containing the field
* @param rvmFieldName the name of the field
* @param rvmFieldType the type reference of the field
* @param rvmFieldAddress the address that the field is being written to
*/
private static boolean copyKnownInstanceField(Object jdkObject, String rvmFieldName, TypeReference rvmFieldType, Address rvmFieldAddress)
throws IllegalAccessException {
// Class library independent objects
if (jdkObject instanceof java.lang.Class) {
Object value = null;
String fieldName = null;
boolean fieldIsFinal = false;
if(rvmFieldName.equals("type")) {
// Looks as though we're trying to write the type for Class,
// lets go over the common ones
if (jdkObject == java.lang.Boolean.TYPE) {
value = RVMType.BooleanType;
} else if (jdkObject == java.lang.Byte.TYPE) {
value = RVMType.ByteType;
} else if (jdkObject == java.lang.Character.TYPE) {
value = RVMType.CharType;
} else if (jdkObject == java.lang.Double.TYPE) {
value = RVMType.DoubleType;
} else if (jdkObject == java.lang.Float.TYPE) {
value = RVMType.FloatType;
} else if (jdkObject == java.lang.Integer.TYPE) {
value = RVMType.IntType;
} else if (jdkObject == java.lang.Long.TYPE) {
value = RVMType.LongType;
} else if (jdkObject == java.lang.Short.TYPE) {
value = RVMType.ShortType;
} else if (jdkObject == java.lang.Void.TYPE) {
value = RVMType.VoidType;
} else {
value = TypeReference.findOrCreate((Class<?>)jdkObject).peekType();
if (value == null) {
fail("Failed to populate Class.type for " + jdkObject);
}
}
fieldName = "type";
fieldIsFinal = true;
}
if ((fieldName != null) && (value != null)) {
if (verbose >= 2) traceContext.push(value.getClass().getName(),
"java.lang.Class",
fieldName);
Address imageAddress = BootImageMap.findOrCreateEntry(value).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(rvmFieldAddress, true, true, false);
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(value, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, !fieldIsFinal);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, !fieldIsFinal);
}
if (verbose >= 2) traceContext.pop();
return true;
} else {
// Unknown Class field or value for type
return false;
}
}
// Class library dependent fields
if (classLibrary == "harmony") {
if ((jdkObject instanceof java.lang.String) &&
(rvmFieldName.equals("hashCode")) &&
(rvmFieldType.isIntType())
) {
// Populate String's hashCode value
bootImage.setFullWord(rvmFieldAddress, jdkObject.hashCode());
return true;
} else if (jdkObject instanceof java.util.Locale) {
String fieldName;
Object value;
if (rvmFieldName.equals("countryCode")) {
value = ((java.util.Locale)jdkObject).getCountry();
fieldName = "countryCode";
} else if (rvmFieldName.equals("languageCode")) {
value = ((java.util.Locale)jdkObject).getLanguage();
fieldName = "languageCode";
} else if (rvmFieldName.equals("variantCode")) {
value = ((java.util.Locale)jdkObject).getVariant();
fieldName = "languageCode";
} else {
return false;
}
if (verbose >= 2) traceContext.push(value.getClass().getName(),
"java.util.Locale",
fieldName);
Address imageAddress = BootImageMap.findOrCreateEntry(value).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
throw new Error("Failed to populate " + fieldName + " in Locale");
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(value, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
}
if (verbose >= 2) traceContext.pop();
return true;
} else if ((jdkObject instanceof java.util.WeakHashMap) &&
(rvmFieldName.equals("referenceQueue"))){
Object value = new java.lang.ref.ReferenceQueue();
if (verbose >= 2) traceContext.push(value.getClass().getName(),
"java.util.WeakHashMap",
"referenceQueue");
Address imageAddress = BootImageMap.findOrCreateEntry(value).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
throw new Error("Failed to populate referenceQueue in WeakHashMap");
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(value, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
}
if (verbose >= 2) traceContext.pop();
return true;
} else if (jdkObject instanceof java.lang.ref.ReferenceQueue) {
if (rvmFieldName.equals("firstReference")){
return false;
} else {
throw new Error("Unknown field "+rvmFieldName+" in java.lang.ref.ReferenceQueue");
}
} else if (jdkObject instanceof java.lang.reflect.Constructor) {
Constructor<?> cons = (Constructor<?>)jdkObject;
if(rvmFieldName.equals("vmConstructor")) {
// fill in this RVMMethod field
String typeName = "L" + cons.getDeclaringClass().getName().replace('.','/') + ";";
RVMType type = TypeReference.findOrCreate(typeName).peekType();
if (type == null) {
throw new Error("Failed to find type for Constructor.constructor: " + cons + " " + typeName);
}
final RVMClass klass = type.asClass();
Class<?>[] consParams = cons.getParameterTypes();
RVMMethod constructor = null;
loop_over_all_constructors:
for (RVMMethod vmCons : klass.getConstructorMethods()) {
TypeReference[] vmConsParams = vmCons.getParameterTypes();
if (vmConsParams.length == consParams.length) {
for (int j = 0; j < vmConsParams.length; j++) {
if (!consParams[j].equals(vmConsParams[j].resolve().getClassForType())) {
continue loop_over_all_constructors;
}
}
constructor = vmCons;
break;
}
}
if (constructor == null) {
throw new Error("Failed to populate Constructor.cons for " + cons);
}
if (verbose >= 2) traceContext.push("vmConstructor",
"java.lang.Constructor",
"cons");
Address imageAddress = BootImageMap.findOrCreateEntry(constructor).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(rvmFieldAddress, true, false, false);
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(constructor, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
}
if (verbose >= 2) traceContext.pop();
return true;
} else if(rvmFieldName.equals("isAccessible")) {
// This field is inherited accesible flag is actually part of
// AccessibleObject
bootImage.setByte(rvmFieldAddress, cons.isAccessible() ? 1 : 0);
return true;
} else if(rvmFieldName.equals("invoker")) {
// Bytecode reflection field, can only be installed in running VM
bootImage.setNullAddressWord(rvmFieldAddress, true, false, false);
return true;
} else {
// Unknown Constructor field
throw new Error("Unknown field "+rvmFieldName+" in java.lang.reflect.Constructor");
}
} else {
// unknown field
return false;
}
} else if (classLibrary == "classpath") {
if ((jdkObject instanceof java.lang.String) &&
(rvmFieldName.equals("cachedHashCode")) &&
(rvmFieldType.isIntType())
) {
// Populate String's cachedHashCode value
bootImage.setFullWord(rvmFieldAddress, jdkObject.hashCode());
return true;
} else if (jdkObject instanceof java.lang.reflect.Constructor) {
Constructor<?> cons = (Constructor<?>)jdkObject;
if(rvmFieldName.equals("cons")) {
// fill in this RVMMethod field
String typeName = "L" + cons.getDeclaringClass().getName().replace('.','/') + ";";
RVMType type = TypeReference.findOrCreate(typeName).peekType();
if (type == null) {
throw new Error("Failed to find type for Constructor.constructor: " + cons + " " + typeName);
}
final RVMClass klass = type.asClass();
Class<?>[] consParams = cons.getParameterTypes();
RVMMethod constructor = null;
loop_over_all_constructors:
for (RVMMethod vmCons : klass.getConstructorMethods()) {
TypeReference[] vmConsParams = vmCons.getParameterTypes();
if (vmConsParams.length == consParams.length) {
for (int j = 0; j < vmConsParams.length; j++) {
if (!consParams[j].equals(vmConsParams[j].resolve().getClassForType())) {
continue loop_over_all_constructors;
}
}
constructor = vmCons;
break;
}
}
if (constructor == null) {
throw new Error("Failed to populate Constructor.cons for " + cons);
}
if (verbose >= 2) traceContext.push("VMConstructor",
"java.lang.Constructor",
"cons");
Object vmcons = java.lang.reflect.JikesRVMSupport.createVMConstructor(constructor);
Address imageAddress = BootImageMap.findOrCreateEntry(vmcons).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(rvmFieldAddress, true, false, false);
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(vmcons, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
}
if (verbose >= 2) traceContext.pop();
return true;
} else if(rvmFieldName.equals("flag")) {
// This field is inherited accesible flag is actually part of
// AccessibleObject
bootImage.setByte(rvmFieldAddress, cons.isAccessible() ? 1 : 0);
return true;
} else {
// Unknown Constructor field
return false;
}
} else if (jdkObject instanceof java.lang.ref.ReferenceQueue) {
if(rvmFieldName.equals("lock")) {
VM.sysWriteln("writing the lock field.");
Object value = new org.jikesrvm.scheduler.LightMonitor();
if (verbose>=2) traceContext.push(value.getClass().getName(),
"java.lang.ref.ReferenceQueue",
"lock");
Address imageAddress = BootImageMap.findOrCreateEntry(value).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
throw new Error("Failed to populate lock in ReferenceQueue");
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(value, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), true, false);
}
if (verbose>=2) traceContext.pop();
return true;
} else if (rvmFieldName.equals("first")){
return false;
} else {
throw new Error("Unknown field "+rvmFieldName+" in java.lang.ref.ReferenceQueue");
}
} else if (jdkObject instanceof java.util.BitSet) {
BitSet bs = (BitSet)jdkObject;
if(rvmFieldName.equals("bits")) {
int max = 0; // highest bit set in set
for(int i=bs.nextSetBit(0); i>=0; i=bs.nextSetBit(i+1)) {
max = i;
}
long[] bits = new long[(max+63)/64];
for(int i=bs.nextSetBit(0); i>=0; i=bs.nextSetBit(i+1)) {
bits[i/64] |= 1L << (i & 63);
}
if (verbose >= 2) traceContext.push("[J", "java.util.BitSet", "bits");
Address imageAddress = BootImageMap.findOrCreateEntry(bits).imageAddress;
if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
// object not part of bootimage: install null reference
if (verbose >= 2) traceContext.traceObjectNotInBootImage();
bootImage.setNullAddressWord(rvmFieldAddress, true, false);
} else if (imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
imageAddress = copyToBootImage(bits, false, Address.max(), jdkObject, false);
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), false, false);
} else {
if (verbose >= 3) traceContext.traceObjectFoundThroughKnown();
bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), false, false);
}
if (verbose >= 2) traceContext.pop();
return true;
} else {
// Unknown BitSet field
return false;
}
} else {
// Unknown field
return false;
}
} else {
throw new Error("Unknown class library: \"" + classLibrary + "\"");
}
}
private static final int OBJECT_HEADER_SIZE = 8;
private static Hashtable<Object,Integer> traversed = null;
private static final Integer VISITED = 0;
/**
* Traverse an object (and, recursively, any of its fields or elements that
* are references) in host jdk address space.
*
* @param jdkObject object to be traversed
* @return offset of copied object within image, in bytes
* (OBJECT_NOT_PRESENT --> object not copied:
* it's not part of bootimage)
*/
private static int traverseObject(Object jdkObject) throws IllegalAccessException {
//
// don't traverse an object twice
//
final Object wrapper = jdkObject;
Object key = new Object() {
@Override
public int hashCode() { return System.identityHashCode(wrapper); }
@Override
public boolean equals(Object o) {
return getClass() == o.getClass() && hashCode() == o.hashCode();
}
};
Integer sz = traversed.get(key);
if (sz != null) return sz; // object already traversed
traversed.put(key, VISITED);
if (verbose >= 2) depth++;
//
// fetch object's type information
//
Class<?> jdkType = jdkObject.getClass();
int size = OBJECT_HEADER_SIZE;
//
// recursively traverse object
//
if (jdkType.isArray()) {
size += 4; // length
int arrayCount = Array.getLength(jdkObject);
//
// traverse array elements
// recurse on values that are references
//
Class<?> jdkElementType = jdkType.getComponentType();
if (jdkElementType.isPrimitive()) {
// array element is logical or numeric type
if (jdkElementType == Boolean.TYPE) {
size += arrayCount*4;
} else if (jdkElementType == Byte.TYPE) {
size += arrayCount*1;
} else if (jdkElementType == Character.TYPE) {
size += arrayCount*2;
} else if (jdkElementType == Short.TYPE) {
size += arrayCount*2;
} else if (jdkElementType == Integer.TYPE) {
size += arrayCount*4;
} else if (jdkElementType == Long.TYPE) {
size += arrayCount*8;
} else if (jdkElementType == Float.TYPE) {
size += arrayCount*4;
} else if (jdkElementType == Double.TYPE) {
size += arrayCount*8;
} else
fail("unexpected array type: " + jdkType);
} else {
// array element is reference type
size += arrayCount*4;
Object[] values = (Object []) jdkObject;
for (int i = 0; i < arrayCount; ++i) {
if (values[i] != null) {
if (verbose >= 2) traceContext.push(values[i].getClass().getName(),
jdkType.getName(), i);
traverseObject(values[i]);
if (verbose >= 2) traceContext.pop();
}
}
}
if (verbose >= 2) {
if (depth == depthCutoff)
say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
else if (depth < depthCutoff) {
String tab = SPACES.substring(0,depth+1);
if (depth == 0 && jtocCount >= 0)
tab = tab + "jtoc #" + String.valueOf(jtocCount);
say(tab, "Traversed array ", jdkType.getName(),
" length=", String.valueOf(arrayCount),
" total size=", String.valueOf(size),
(size >= LARGE_ARRAY_SIZE) ?" large object!!!" : "");
}
}
} else {
//
// traverse object fields
// recurse on values that are references
//
for (Class<?> type = jdkType; type != null; type = type.getSuperclass()) {
Field[] jdkFields = type.getDeclaredFields();
for (int i = 0, n = jdkFields.length; i < n; ++i) {
Field jdkField = jdkFields[i];
jdkField.setAccessible(true);
Class<?> jdkFieldType = jdkField.getType();
if (jdkFieldType.isPrimitive()) {
// field is logical or numeric type
if (jdkFieldType == Boolean.TYPE)
size += 1;
else if (jdkFieldType == Byte.TYPE)
size += 1;
else if (jdkFieldType == Character.TYPE)
size += 2;
else if (jdkFieldType == Short.TYPE)
size += 2;
else if (jdkFieldType == Integer.TYPE)
size += 4;
else if (jdkFieldType == Long.TYPE)
size += 8;
else if (jdkFieldType == Float.TYPE)
size += 4;
else if (jdkFieldType == Double.TYPE)
size += 8;
else
fail("unexpected field type: " + jdkFieldType);
} else {
// field is reference type
size += 4;
Object value = jdkField.get(jdkObject);
if (value != null) {
if (verbose >= 2) traceContext.push(value.getClass().getName(),
jdkType.getName(),
jdkField.getName());
traverseObject(value);
if (verbose >= 2) traceContext.pop();
}
}
}
}
if (verbose >= 2) {
if (depth == depthCutoff)
say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
else if (depth < depthCutoff) {
String tab = SPACES.substring(0,depth+1);
if (depth == 0 && jtocCount >= 0)
tab = tab + "#" + String.valueOf(jtocCount);
say(tab, "Traversed object ", jdkType.getName(),
" total size=", String.valueOf(size),
(size >= LARGE_SCALAR_SIZE) ? " large object!!!" : "");
}
}
}
traversed.put(key, size);
if (verbose >= 2) depth--;
return size;
}
/**
* Begin recording objects referenced by rvm classes during
* loading/resolution/instantiation. These references will be converted
* to bootimage addresses when those objects are copied into bootimage.
*/
private static void enableObjectAddressRemapper() {
Magic.setObjectAddressRemapper(BootImageObjectAddressRemapper.getInstance());
}
/**
* Stop recording objects referenced by rvm classes during
* loading/resolution/instantiation.
*/
private static void disableObjectAddressRemapper() {
Magic.setObjectAddressRemapper(null);
// Remove bootimage writer's remapper object that was present when jtoc
// was populated with jdk objects. It's not part of the bootimage and we
// don't want to see warning messages about it when the bootimage is
// written.
RVMMember remapper = Entrypoints.magicObjectRemapperField;
Statics.setSlotContents(remapper.getOffset(), 0);
}
/**
* Obtain rvm type corresponding to host jdk type.
*
* @param jdkType jdk type
* @return rvm type (null --> type does not appear in list of classes
* comprising bootimage)
*/
private static RVMType getRvmType(Class<?> jdkType) {
return bootImageTypes.get(jdkType.getName());
}
/**
* Obtain host jdk type corresponding to target rvm type.
*
* @param rvmType rvm type
* @return jdk type (null --> type does not exist in host namespace)
*/
private static Class<?> getJdkType(RVMType rvmType) {
Throwable x;
try {
return Class.forName(rvmType.toString());
} catch (ExceptionInInitializerError e) {
throw e;
} catch (IllegalAccessError e) {
x = e;
} catch (UnsatisfiedLinkError e) {
x = e;
} catch (NoClassDefFoundError e) {
x = e;
} catch (SecurityException e) {
x = e;
} catch (ClassNotFoundException e) {
x = e;
}
if (verbose >= 1) {
say(x.toString());
}
return null;
}
/**
* Obtain accessor via which a field value may be fetched from host jdk
* address space.
*
* @param jdkType class whose field is sought
* @param index index in FieldInfo of field sought
* @param isStatic is field from Static field table, indicates which table to consult
* @return field accessor (null --> host class does not have specified field)
*/
private static Field getJdkFieldAccessor(Class<?> jdkType, int index, boolean isStatic) {
FieldInfo fInfo = bootImageTypeFields.get(new Key(jdkType));
Field f;
if (isStatic == STATIC_FIELD) {
f = fInfo.jdkStaticFields[index];
return f;
} else {
f = fInfo.jdkInstanceFields[index];
return f;
}
}
/**
* Figure out name of static rvm field whose value lives in specified jtoc
* slot.
*
* @param jtocSlot jtoc slot number
* @return field name
*/
private static RVMField getRvmStaticField(Offset jtocOff) {
for (int i = FIRST_TYPE_DICTIONARY_INDEX; i < RVMType.numTypes(); ++i) {
RVMType type = RVMType.getType(i);
if (type == null) continue;
if (type.isPrimitiveType() || type.isUnboxedType())
continue;
if (!type.isResolved())
continue;
for (RVMField rvmField : type.getStaticFields()) {
if (rvmField.getOffset().EQ(jtocOff))
return rvmField;
}
}
return null;
}
private static CompiledMethod findMethodOfCode(Object code) {
for (int i = 0; i < CompiledMethods.numCompiledMethods(); ++i) {
CompiledMethod compiledMethod = CompiledMethods.getCompiledMethodUnchecked(i);
if (compiledMethod != null &&
compiledMethod.isCompiled() &&
compiledMethod.getEntryCodeArray() == code)
return compiledMethod;
}
return null;
}
/**
* Write method address map for use with dbx debugger.
*
* @param fileName name of file to write the map to
*/
private static void writeAddressMap(String mapFileName) throws IOException {
if (verbose >= 1) say("writing ", mapFileName);
// Restore previously unnecessary Statics data structures
Statics.bootImageReportGeneration(staticsJunk);
FileOutputStream fos = new FileOutputStream(mapFileName);
BufferedOutputStream bos = new BufferedOutputStream(fos, 128);
PrintStream out = new PrintStream(bos, false);
out.println("#! /bin/bash");
out.println("# This is a method address map, for use with the ``dbx'' debugger.");
out.println("# To sort by \"code\" address, type \"bash <name-of-this-file>\".");
out.println("# Bootimage data: "+Integer.toHexString(BOOT_IMAGE_DATA_START.toInt()) +
"..."+Integer.toHexString(BOOT_IMAGE_DATA_START.toInt()+bootImage.getDataSize()));
out.println("# Bootimage code: "+Integer.toHexString(BOOT_IMAGE_CODE_START.toInt()) +
"..."+Integer.toHexString(BOOT_IMAGE_CODE_START.toInt()+bootImage.getCodeSize()));
out.println("# Bootimage refs: "+Integer.toHexString(BOOT_IMAGE_RMAP_START.toInt()) +
"..."+Integer.toHexString(BOOT_IMAGE_RMAP_START.toInt()+bootImage.getRMapSize()));
out.println();
out.println("(/bin/grep 'code 0x' | /bin/sort -k 4.3,4) << EOF-EOF-EOF");
out.println();
out.println("JTOC Map");
out.println("--------");
out.println("slot offset category contents details");
out.println("---- ------ -------- -------- -------");
String pad = " ";
// Numeric JTOC fields
for (int jtocSlot = Statics.getLowestInUseSlot();
jtocSlot < Statics.middleOfTable;
jtocSlot++) {
Offset jtocOff = Statics.slotAsOffset(jtocSlot);
String category;
String contents;
String details;
RVMField field = getRvmStaticField(jtocOff);
RVMField field2 = getRvmStaticField(jtocOff.plus(4));
boolean couldBeLongLiteral = Statics.isLongSizeLiteral(jtocSlot);
boolean couldBeIntLiteral = Statics.isIntSizeLiteral(jtocSlot);
if (couldBeLongLiteral && ((field == null) || (field2 == null))) {
if ((field == null) && (field2 == null)) {
category = "literal ";
long lval = Statics.getSlotContentsAsLong(jtocOff);
contents = VM.intAsHexString((int) (lval >> 32)) +
VM.intAsHexString((int) (lval & 0xffffffffL)).substring(2);
details = lval + "L";
} else if ((field == null) && (field2 != null)) {
category = "literal/field";
long lval = Statics.getSlotContentsAsLong(jtocOff);
contents = VM.intAsHexString((int) (lval >> 32)) +
VM.intAsHexString((int) (lval & 0xffffffffL)).substring(2);
details = lval + "L / " + field2.toString();
} else if ((field != null) && (field2 == null)) {
category = "literal/field";
long lval = Statics.getSlotContentsAsLong(jtocOff);
contents = VM.intAsHexString((int) (lval >> 32)) +
VM.intAsHexString((int) (lval & 0xffffffffL)).substring(2);
details = lval + "L / " + field.toString();
} else {
throw new Error("Unreachable");
}
jtocSlot++;
} else if (couldBeIntLiteral) {
if (field != null) {
category = "literal/field";
int ival = Statics.getSlotContentsAsInt(jtocOff);
contents = VM.intAsHexString(ival) + pad;
details = Integer.toString(ival) + " / " + field.toString();
} else {
category = "literal ";
int ival = Statics.getSlotContentsAsInt(jtocOff);
contents = VM.intAsHexString(ival) + pad;
details = Integer.toString(ival);
}
} else {
if (field != null) {
category = "field ";
details = field.toString();
TypeReference type = field.getType();
if (type.isIntLikeType()) {
int ival = Statics.getSlotContentsAsInt(jtocOff);
contents = VM.intAsHexString(ival) + pad;
} else if(type.isLongType()) {
long lval= Statics.getSlotContentsAsLong(jtocOff);
contents = VM.intAsHexString((int) (lval >> 32)) +
VM.intAsHexString((int) (lval & 0xffffffffL)).substring(2);
jtocSlot++;
} else if(type.isFloatType()) {
int ival = Statics.getSlotContentsAsInt(jtocOff);
contents = Float.toString(Float.intBitsToFloat(ival)) + pad;
} else if(type.isDoubleType()) {
long lval= Statics.getSlotContentsAsLong(jtocOff);
contents = Double.toString(Double.longBitsToDouble(lval)) + pad;
jtocSlot++;
} else if (type.isWordLikeType()) {
if (VM.BuildFor32Addr) {
int ival = Statics.getSlotContentsAsInt(jtocOff);
contents = VM.intAsHexString(ival) + pad;
} else {
long lval= Statics.getSlotContentsAsLong(jtocOff);
contents = VM.intAsHexString((int) (lval >> 32)) +
VM.intAsHexString((int) (lval & 0xffffffffL)).substring(2);
jtocSlot++;
}
} else {
// Unknown?
int ival = Statics.getSlotContentsAsInt(jtocOff);
category = "<? - field> ";
details = "<? - " + field.toString() + ">";
contents = VM.intAsHexString(ival) + pad;
}
} else {
// Unknown?
int ival = Statics.getSlotContentsAsInt(jtocOff);
category = "<?> ";
details = "<?>";
contents = VM.intAsHexString(ival) + pad;
}
}
out.println((jtocSlot + " ").substring(0,8) +
VM.addressAsHexString(jtocOff.toWord().toAddress()) + " " +
category + " " + contents + " " + details);
}
// Reference JTOC fields
for (int jtocSlot = Statics.middleOfTable,
n = Statics.getHighestInUseSlot();
jtocSlot <= n;
jtocSlot += Statics.getReferenceSlotSize()) {
Offset jtocOff = Statics.slotAsOffset(jtocSlot);
Object obj = BootImageMap.getObject(getIVal(jtocOff));
String category;
String details;
String contents = VM.addressAsHexString(getReferenceAddr(jtocOff, false)) + pad;
RVMField field = getRvmStaticField(jtocOff);
if (Statics.isReferenceLiteral(jtocSlot)) {
if (field != null) {
category = "literal/field";
} else {
category = "literal ";
}
if (obj == null){
details = "(null)";
} else if (obj instanceof String) {
details = "\""+ obj + "\"";
} else if (obj instanceof Class) {
details = obj.toString();;
} else if (obj instanceof TIB) {
category = "literal tib ";
RVMType type = ((TIB)obj).getType();
details = (type == null) ? "?" : type.toString();
} else {
details = "object "+ obj.getClass();
}
if (field != null) {
details += " / " + field.toString();
}
} else if (field != null) {
category = "field ";
details = field.toString();
} else if (obj instanceof TIB) {
// TIBs confuse the statics as their backing is written into the boot image
category = "tib ";
RVMType type = ((TIB)obj).getType();
details = (type == null) ? "?" : type.toString();
} else {
category = "unknown ";
if (obj instanceof String) {
details = "\""+ obj + "\"";
} else if (obj instanceof Class) {
details = obj.toString();
} else {
CompiledMethod m = findMethodOfCode(obj);
if (m != null) {
category = "code ";
details = m.getMethod().toString();
} else if (obj != null) {
details = "<?> - unrecognized field or literal of type " + obj.getClass();
} else {
details = "<?>";
}
}
}
out.println((jtocSlot + " ").substring(0,8) +
VM.addressAsHexString(jtocOff.toWord().toAddress()) + " " +
category + " " + contents + " " + details);
}
out.println();
out.println("Method Map");
out.println("----------");
out.println(" address method");
out.println(" ------- ------");
out.println();
for (int i = 0; i < CompiledMethods.numCompiledMethods(); ++i) {
CompiledMethod compiledMethod = CompiledMethods.getCompiledMethodUnchecked(i);
if (compiledMethod != null) {
RVMMethod m = compiledMethod.getMethod();
if (m != null && compiledMethod.isCompiled()) {
CodeArray instructions = compiledMethod.getEntryCodeArray();
Address code = BootImageMap.getImageAddress(instructions.getBacking(), true);
out.println(". . code " + VM.addressAsHexString(code) +
" " + compiledMethod.getMethod());
}
}
}
// Extra information on the layout of objects in the boot image
if (false) {
out.println();
out.println("Object Map");
out.println("----------");
out.println(" address type");
out.println(" ------- ------");
out.println();
SortedSet<BootImageMap.Entry> set = new TreeSet<BootImageMap.Entry>(new Comparator<BootImageMap.Entry>() {
public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
return Integer.valueOf(a.imageAddress.toInt()).compareTo(b.imageAddress.toInt());
}
});
for (Enumeration<BootImageMap.Entry> e = BootImageMap.elements(); e.hasMoreElements();) {
BootImageMap.Entry entry = e.nextElement();
set.add(entry);
}
for (Iterator<BootImageMap.Entry> i = set.iterator(); i.hasNext();) {
BootImageMap.Entry entry = i.next();
Address data = entry.imageAddress;
out.println(". . data " + VM.addressAsHexString(data) +
" " + entry.jdkObject.getClass());
}
}
out.println();
out.println("EOF-EOF-EOF");
out.flush();
out.close();
}
/**
* Read an integer value from the JTOC
* @param jtocOff offset in JTOC
* @return integer at offset
*/
private static int getIVal(Offset jtocOff) {
int ival;
if (VM.BuildFor32Addr) {
ival = Statics.getSlotContentsAsInt(jtocOff);
} else {
ival = (int)Statics.getSlotContentsAsLong(jtocOff); // just a cookie
}
return ival;
}
/**
* Read a reference from the JTOC
* @param jtocOff offset in JTOC
* @param fatalIfNotFound whether to terminate on failure
* @return address of object or zero if not found
*/
private static Address getReferenceAddr(Offset jtocOff, boolean fatalIfNotFound) {
int ival = getIVal(jtocOff);
if (ival != 0) {
Object jdkObject = BootImageMap.getObject(ival);
if (jdkObject instanceof RuntimeTable) {
jdkObject = ((RuntimeTable<?>)jdkObject).getBacking();
}
return BootImageMap.getImageAddress(jdkObject, fatalIfNotFound);
} else {
return Address.zero();
}
}
/**
* Get the size of the object in the boot image
* @param type of object
* @param obj we want the size of
* @return size of object
*/
private static int getSize(RVMType type, Object obj) {
if (type.isArrayType()) {
if (obj instanceof RuntimeTable) {
obj = ((RuntimeTable<?>)obj).getBacking();
} else if (obj instanceof CodeArray) {
obj = ((CodeArray)obj).getBacking();
}
if (!obj.getClass().isArray()) {
fail("This should be an array " + obj.getClass() + " " + type);
}
return type.asArray().getInstanceSize(Array.getLength(obj));
} else {
return type.asClass().getInstanceSize();
}
}
private static final HashMap<RVMType, Integer> typeSizes = new HashMap<RVMType, Integer>();
/**
* Get the number of non-final references of the object in the boot image
* @param type of object
* @param obj we want the size of
* @return number of non-final references
*/
private static int getNumberOfNonFinalReferences(RVMType type, Object obj) {
if (type.isArrayType()) {
if (type.asArray().getElementType().isReferenceType()) {
if (obj instanceof RuntimeTable) {
obj = ((RuntimeTable<?>)obj).getBacking();
} else if (obj instanceof CodeArray) {
obj = ((CodeArray)obj).getBacking();
}
if (!obj.getClass().isArray()) {
fail("This should be an array " + obj.getClass() + " " + type);
}
return Array.getLength(obj);
} else {
return 0;
}
} else {
Integer size = typeSizes.get(type);
if (size == null) {
// discount final references that aren't part of the boot image
size = type.asClass().getNumberOfNonFinalReferences();
typeSizes.put(type, size);
}
return size;
}
}
/**
* Get the number of non-final references of the object in the boot image
* @param type of object
* @param obj we want the size of
* @return number of non-final references
*/
private static int getNumberOfReferences(RVMType type, Object obj) {
if (type.isArrayType()) {
if (type.asArray().getElementType().isReferenceType()) {
if (obj instanceof RuntimeTable) {
obj = ((RuntimeTable<?>)obj).getBacking();
} else if (obj instanceof CodeArray) {
obj = ((CodeArray)obj).getBacking();
}
if (!obj.getClass().isArray()) {
fail("This should be an array " + obj.getClass() + " " + type);
}
return Array.getLength(obj);
} else {
return 0;
}
} else {
Integer size = typeSizes.get(type);
if (size == null) {
// discount final references that aren't part of the boot image
size = type.asClass().getReferenceOffsets().length;
typeSizes.put(type, size);
}
return size;
}
}
}