/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.build;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.tools.ant.Project;
import org.jnode.assembler.Label;
import org.jnode.assembler.NativeStream;
import org.jnode.assembler.UnresolvedObjectRefException;
import org.jnode.assembler.NativeStream.ObjectRef;
import org.jnode.assembler.x86.X86BinaryAssembler;
import org.jnode.bootlog.BootLog;
import org.jnode.bootlog.BootLogInstance;
import org.jnode.emu.naming.BasicNameSpace;
import org.jnode.naming.InitialNaming;
import org.jnode.plugin.PluginDescriptor;
import org.jnode.plugin.PluginException;
import org.jnode.plugin.PluginRegistry;
import org.jnode.plugin.model.Factory;
import org.jnode.plugin.model.PluginDescriptorModel;
import org.jnode.plugin.model.PluginJar;
import org.jnode.plugin.model.PluginRegistryModel;
import org.jnode.util.NumberUtils;
import org.jnode.vm.BaseVmArchitecture;
import org.jnode.vm.JvmType;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VirtualMemoryRegion;
import org.jnode.vm.VmImpl;
import org.jnode.vm.VmSystemClassLoader;
import org.jnode.vm.bytecode.BytecodeParser;
import org.jnode.vm.classmgr.Modifier;
import org.jnode.vm.classmgr.ObjectLayout;
import org.jnode.vm.classmgr.VmArray;
import org.jnode.vm.classmgr.VmArrayClass;
import org.jnode.vm.classmgr.VmClassType;
import org.jnode.vm.classmgr.VmCompiledCode;
import org.jnode.vm.classmgr.VmField;
import org.jnode.vm.classmgr.VmIsolatedStatics;
import org.jnode.vm.classmgr.VmMethodCode;
import org.jnode.vm.classmgr.VmNormalClass;
import org.jnode.vm.classmgr.VmSharedStatics;
import org.jnode.vm.classmgr.VmStaticField;
import org.jnode.vm.classmgr.VmStatics;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.compiler.NativeCodeCompiler;
import org.jnode.vm.facade.Vm;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.memmgr.HeapHelper;
import org.jnode.vm.memmgr.VmHeapManager;
import org.jnode.vm.objects.BootableHashMap;
import org.jnode.vm.objects.VmSystemObject;
import org.jnode.vm.scheduler.VmProcessor;
import org.vmmagic.unboxed.UnboxedObject;
/**
* Build the boot image from an assembler compiled bootstrap (in ELF format)
* combined with the precompiled Java classes.
*/
public abstract class AbstractBootImageBuilder extends AbstractPluginsTask {
protected static final Label bootHeapEnd = new Label("$$bootHeapEnd");
protected static final Label bootHeapStart = new Label("$$bootHeapStart");
/**
* System property set to indicate build time.
*/
public static final String BUILDTIME_PROPERTY = "org.jnode.buildtime";
protected static final Label imageEnd = new Label("$$image_end");
protected static final Label initialStack = new Label("$$initialStack");
protected static final Label initialStackPtr = new Label(
"$$initialStackPtr");
private static final String zero8 = "00000000";
private static final String zero16 = zero8 + zero8;
/**
* Set of objects that should not yet be emitted.
*/
private final Set<Object> blockedObjects = new HashSet<Object>();
private VmSystemClassLoader clsMgr;
/**
* Classname/packagename of those classes/packages that need highly
* optimized compilation.
*/
private final HashSet<String> compileHighOptLevelPackages = new HashSet<String>();
private final HashSet<String> preloadPackages = new HashSet<String>();
protected boolean debug = true;
private File debugFile;
private File destFile;
private String jnodeCompiler;
private File kernelFile;
private Set<String> legalInstanceClasses;
private File listFile;
private File coreClassListFile;
private int totalHighMethods;
private int totalHighMethodSize;
private int totalLowMethods;
private int totalLowMethodSize;
private String version;
/**
* Plugin id of the memory manager plugin.
*/
private String memMgrPluginId;
/**
* Nano-kernel source information.
*/
private final AsmSourceInfo asmSourceInfo;
/**
* Enable the compilation of the nano-kernel source via jnasm.
*/
private boolean enableJNasm = false;
/**
* Construct a new BootImageBuilder.
*/
public AbstractBootImageBuilder() {
asmSourceInfo = new AsmSourceInfo();
legalInstanceClasses = setupLegalInstanceClasses();
}
/**
* Create the kernel-sources element.
*
* @return the element created
*/
public AsmSourceInfo createNanokernelsources() {
return asmSourceInfo;
}
protected final void addCompileHighOptLevel(String name) {
compileHighOptLevelPackages.add(name);
}
protected final void addPreloadPackage(String name) {
preloadPackages.add(name);
}
protected void cleanup() {
clsMgr = null;
blockedObjects.clear();
}
/**
* Compile the methods in the given class to native code.
*
* @param os
* @param arch
* @throws ClassNotFoundException
*/
private final void compileClasses(NativeStream os, BaseVmArchitecture arch)
throws ClassNotFoundException {
final NativeCodeCompiler[] compilers = arch.getCompilers();
final int optLevel = compilers.length - 1;
// Use the most optimizing compiler here
final NativeCodeCompiler compiler = compilers[optLevel];
int oldCount;
int newCount;
boolean again;
do {
again = false;
oldCount = clsMgr.getLoadedClassCount();
for (VmType<?> vmClass : clsMgr.getLoadedClasses()) {
vmClass.link();
final boolean compHigh = isCompileHighOptLevel(vmClass);
try {
if (!vmClass.isCpRefsResolved() && compHigh) {
// log("Resolving CP of " + vmClass.getName(),
// Project.MSG_VERBOSE);
vmClass.resolveCpRefs(/*clsMgr*/);
again = true;
}
final int mcnt;
final int startLength = os.getLength();
if (compHigh) {
log("Full Compile " + vmClass.getName(),
Project.MSG_VERBOSE);
mcnt = vmClass.compileBootstrap(compiler, os, optLevel);
totalHighMethods += mcnt;
totalHighMethodSize += (os.getLength() - startLength);
} else {
log("Min. Compile " + vmClass.getName(),
Project.MSG_VERBOSE);
mcnt = vmClass.compileBootstrap(compilers[0], os, 0);
totalLowMethods += mcnt;
totalLowMethodSize += (os.getLength() - startLength);
}
again |= (mcnt > 0);
} catch (Throwable ex) {
throw new BuildException("Compile of " + vmClass.getName()
+ " failed", ex);
}
if (!vmClass.isCompiled()) {
throw new BuildException(
"Class should have been compiled by now");
}
}
newCount = clsMgr.getLoadedClassCount();
if (false) {
log("oldCount " + oldCount + ", newCount " + newCount,
Project.MSG_INFO);
}
} while ((oldCount != newCount) || again);
log("End of compileClasses", Project.MSG_VERBOSE);
}
/**
* Copy the jnode.jar file into a byte array that is added to the java
* image.
*
* @param blockedObjects
* @param piRegistry
* @return The loaded resource names
* @throws BuildException
*/
protected final Collection<String> copyJarFile(Set<Object> blockedObjects, PluginRegistryModel piRegistry)
throws BuildException {
final BootableHashMap<String, byte[]> resources = new BootableHashMap<String, byte[]>();
// try {
// final JarFile jar = new JarFile(jarFile);
// for (Enumeration< ? > e = jar.entries(); e.hasMoreElements();) {
// final JarEntry entry = (JarEntry) e.nextElement();
// final byte[] data = read(jar.getInputStream(entry));
// resources.put(entry.getName().intern(), data);
// }
// } catch (IOException ex) {
// throw new BuildException(ex);
// }
// Load all resources of all plugins
for (PluginDescriptor descr : piRegistry) {
if (!descr.isSystemPlugin()) {
throw new BuildException("Non system plugin found " + descr.getId());
}
final PluginJar piJar = ((PluginDescriptorModel) descr).getJarFile();
log("Plugin: " + descr.getId() + piJar.resourceNames().size());
for (String name : piJar.resourceNames()) {
final ByteBuffer buf = piJar.getResourceAsBuffer(name);
final byte[] data = new byte[buf.limit()];
buf.get(data);
resources.put(name.intern(), data);
// log(" " + name);
}
piJar.clearResources();
}
blockedObjects.add(resources);
clsMgr.setSystemRtJar(resources);
return Collections.unmodifiableCollection(resources.keySet());
}
/**
* Copy the jnode.jar file into a byte array that is added to the java
* image.
*
* @param piRegistry
* @return The loaded resource names
* @throws BuildException
*/
protected final Map<String, byte[]> loadSystemResource(PluginRegistryModel piRegistry)
throws BuildException {
final BootableHashMap<String, byte[]> resources = new BootableHashMap<String, byte[]>();
// Load all resources of all plugins
for (PluginDescriptor descr : piRegistry) {
if (!descr.isSystemPlugin()) {
throw new BuildException("Non system plugin found " + descr.getId());
}
final PluginJar piJar = ((PluginDescriptorModel) descr).getJarFile();
// log("Plugin: " + descr.getId() + piJar.resourceNames().size());
for (String name : piJar.resourceNames()) {
final ByteBuffer buf = piJar.getResourceAsBuffer(name);
final byte[] data = new byte[buf.limit()];
buf.get(data);
resources.put(name.intern(), data);
// log(" " + name);
}
piJar.clearResources();
}
return resources;
}
/**
* Copy the kernel native code into the native stream.
*
* @param os
* @throws BuildException
*/
protected abstract void copyKernel(NativeStream os) throws BuildException;
/**
* Compile the kernel native code into the native stream.
*
* @param os
* @throws BuildException
*/
protected void compileKernel(NativeStream os, AsmSourceInfo sourceInfo) throws BuildException {
// TODO be implemented by Levente
throw new BuildException("Not implemented");
}
/**
* Create the initial stack space.
*
* @param os
* @param stackLabel Label to the start of the stack space (low address)
* @param stackPtrLabel Label to the initial stack pointer (on x86 high address)
* @throws BuildException
* @throws ClassNotFoundException
* @throws UnresolvedObjectRefException
*/
protected abstract void createInitialStack(NativeStream os,
Label stackLabel, Label stackPtrLabel) throws BuildException,
ClassNotFoundException, UnresolvedObjectRefException;
/**
* Create a platform specific native stream.
*
* @return NativeStream
*/
protected abstract NativeStream createNativeStream();
/**
* Create the default processor for this architecture.
*
* @return The processor
* @throws BuildException
*/
protected abstract VmProcessor createProcessor(VmImpl vm, VmSharedStatics statics,
VmIsolatedStatics isolatedStatics) throws BuildException;
private final void doExecute() throws BuildException {
setupCompileHighOptLevelPackages();
debug = (getProject().getProperty("jnode.debug") != null);
final long lmKernel = kernelFile.lastModified();
final long lmDest = destFile.lastModified();
final long lmPIL = getPluginListFile().lastModified();
if (version == null) {
throw new BuildException("Version property must be set");
}
if (memMgrPluginId == null) {
throw new BuildException("Memory manager plugin Id must be set");
}
final PluginList piList;
final long lmPI;
final URL memMgrPluginURL;
try {
log("plugin-list: " + getPluginListFile(), Project.MSG_DEBUG);
piList = getPluginList();
memMgrPluginURL = piList.createPluginURL(memMgrPluginId, version);
lmPI = Math.max(piList.lastModified(), memMgrPluginURL.openConnection().getLastModified());
} catch (PluginException ex) {
throw new BuildException(ex);
} catch (IOException ex) {
throw new BuildException(ex);
}
if ((lmKernel < lmDest) && (lmPIL < lmDest) && (lmPI < lmDest)) {
// No need to do anything, skip
return;
}
if (debugFile != null) {
debugFile.delete();
}
try {
System.getProperties().setProperty(BUILDTIME_PROPERTY, "1");
// Load the plugin descriptors
final PluginRegistryModel piRegistry;
piRegistry = Factory.createRegistry(piList.getPluginList());
// Load the memory management plugin
piRegistry.loadPlugin(memMgrPluginURL, true);
// Test the set of system plugins
testPluginPrerequisites(piRegistry);
// Load all resources
final Map<String, byte[]> resources = loadSystemResource(piRegistry);
/* Now create the processor */
final BaseVmArchitecture arch = getArchitecture();
final NativeStream os = createNativeStream();
clsMgr = new VmSystemClassLoader(null/*classesURL*/, arch,
new BuildObjectResolver(os, this));
blockedObjects.add(clsMgr);
blockedObjects.add(clsMgr.getSharedStatics());
blockedObjects.add(clsMgr.getSharedStatics().getTable());
blockedObjects.add(clsMgr.getIsolatedStatics());
blockedObjects.add(clsMgr.getIsolatedStatics().getTable());
blockedObjects.add(resources);
clsMgr.setSystemRtJar(resources);
// Initialize the statics table.
initializeStatics(clsMgr.getSharedStatics());
if (debug) {
log("Building in DEBUG mode", Project.MSG_WARN);
}
// Create the VM
final VmImpl vm = new VmImpl(version, arch, clsMgr.getSharedStatics(), debug, clsMgr, piRegistry);
blockedObjects.add(vm);
blockedObjects.add(VmUtils.getVm().getCompiledMethods());
final VmProcessor proc = createProcessor(vm, clsMgr.getSharedStatics(),
clsMgr.getIsolatedStatics());
log("Building for " + proc.getCPUID());
final Label clInitCaller = new Label("$$clInitCaller");
VmType<?> systemClasses[] = VmType.initializeForBootImage(clsMgr);
for (int i = 0; i < systemClasses.length; i++) {
clsMgr.addLoadedClass(systemClasses[i].getName(),
systemClasses[i]);
}
// First copy the native kernel file
if (enableJNasm) {
compileKernel(os, asmSourceInfo);
} else {
copyKernel(os);
}
os.setObjectRef(bootHeapStart);
// Setup a call to our first java method
initImageHeader(os, clInitCaller, vm, piRegistry);
// Create the initial stack
createInitialStack(os, initialStack, initialStackPtr);
/* Now load the classes */
loadClass(VmMethodCode.class);
loadClass(Unsafe.class);
loadClass(VmSystemClassLoader.class);
loadClass(VmType[].class);
loadClass(Vm.class);
loadClass(VirtualMemoryRegion.class).link();
vm.getHeapManager().loadClasses(clsMgr);
loadClass(VmHeapManager.class);
loadClass(VmSharedStatics.class);
loadClass(VmIsolatedStatics.class);
loadClass(VmUtils.getVm().getHeapManager().getClass());
loadClass(HeapHelper.class);
loadClass("org.jnode.vm.HeapHelperImpl");
loadClass(VmUtils.getVm().getCompiledMethods().getClass());
loadClass(VmCompiledCode[].class);
loadSystemClasses(resources.keySet());
/* Now emit the processor */
os.getObjectRef(proc);
/* Let the compilers load its native symbol offsets */
final NativeCodeCompiler[] cmps = arch.getCompilers();
for (int i = 0; i < cmps.length; i++) {
final NativeCodeCompiler cmp = cmps[i];
cmp.initialize(clsMgr);
os.getObjectRef(cmp);
}
/* Let the test compilers load its native symbol offsets */
final NativeCodeCompiler[] testCmps = arch.getTestCompilers();
if (testCmps != null) {
for (int i = 0; i < testCmps.length; i++) {
final NativeCodeCompiler cmp = testCmps[i];
cmp.initialize(clsMgr);
os.getObjectRef(cmp);
}
}
log("Compiling using " + cmps[0].getName() + " and "
+ cmps[cmps.length - 1].getName() + " compilers");
// Initialize the IMT compiler.
arch.getIMTCompiler().initialize(clsMgr);
// Load the jarfile as byte-array
// copyJarFile(blockedObjects, piRegistry);
// Now emit all object images to the actual image
emitObjects(os, arch, blockedObjects, false);
// Disallow the loading of new classes
clsMgr.setFailOnNewLoad(true);
emitObjects(os, arch, blockedObjects, false);
// Emit the vm
log("Emit vm", Project.MSG_VERBOSE);
blockedObjects.remove(vm);
emitObjects(os, arch, blockedObjects, false);
// Twice, this is intended!
emitObjects(os, arch, blockedObjects, false);
// Emit the compiled method list
log("Emit compiled methods", Project.MSG_VERBOSE);
blockedObjects.remove(VmUtils.getVm().getCompiledMethods());
final int compiledMethods = VmUtils.getVm().getCompiledMethods().size();
emitObjects(os, arch, blockedObjects, false);
// Twice, this is intended!
emitObjects(os, arch, blockedObjects, false);
/* Set the bootclasses */
log("prepare bootClassArray", Project.MSG_VERBOSE);
final VmType<?> bootClasses[] = clsMgr.prepareAfterBootstrap();
os.getObjectRef(bootClasses);
emitObjects(os, arch, blockedObjects, false);
// Twice, this is intended!
emitObjects(os, arch, blockedObjects, false);
// Emit the classmanager
log("Emit clsMgr", Project.MSG_VERBOSE);
// Turn auto-compilation on
clsMgr.setCompileRequired();
blockedObjects.remove(clsMgr);
emitObjects(os, arch, blockedObjects, false);
// Twice, this is intended!
emitObjects(os, arch, blockedObjects, false);
// Emit the statics table
log("Emit statics", Project.MSG_VERBOSE);
blockedObjects.remove(clsMgr.getSharedStatics());
blockedObjects.remove(clsMgr.getIsolatedStatics());
emitObjects(os, arch, blockedObjects, true);
// Twice, this is intended!
emitObjects(os, arch, blockedObjects, true);
// Emit the remaining objects
log("Emit rest; blocked=" + blockedObjects, Project.MSG_VERBOSE);
emitObjects(os, arch, null, true);
// Verify no methods have been compiled after we wrote the
// CompiledCodeList.
if (VmUtils.getVm().getCompiledMethods().size() != compiledMethods) {
throw new BuildException(
"Method have been compiled after CompiledCodeList was written.");
}
/* Write static initializer code */
emitStaticInitializerCalls(os, bootClasses, clInitCaller);
// This is the end of the image
X86BinaryAssembler.ObjectInfo dummyObjectAtEnd =
os.startObject(loadClass(VmMethodCode.class));
pageAlign(os);
dummyObjectAtEnd.markEnd();
os.setObjectRef(imageEnd);
os.setObjectRef(bootHeapEnd);
/* Link all native symbols */
linkNativeSymbols(os);
// Patch multiboot header
patchHeader(os);
// Store the image
storeImage(os);
// Generate the listfile
printLabels(os, bootClasses, clsMgr.getSharedStatics());
logLargeClasses(bootClasses);
// Generate debug info
for (int i = 0; i < cmps.length; i++) {
cmps[i].dumpStatistics();
}
final int bootHeapSize = os.getObjectRef(bootHeapEnd).getOffset()
- os.getObjectRef(bootHeapStart).getOffset();
final int bootHeapBitmapSize = (bootHeapSize / ObjectLayout.OBJECT_ALIGN) >> 3;
log("Boot heap size " + (bootHeapSize >>> 10) + "K bitmap size "
+ (bootHeapBitmapSize >>> 10) + "K");
log("Shared statics");
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
clsMgr.getSharedStatics().dumpStatistics(out);
log("Isolated statics");
clsMgr.getIsolatedStatics().dumpStatistics(out);
VmUtils.dumpStatistics(out);
logStatistics(os);
BytecodeParser.dumpStatistics();
log("Optimized methods : " + totalHighMethods + ", avg size "
+ (totalHighMethodSize / totalHighMethods) + ", tot size "
+ totalHighMethodSize);
log("Ondemand comp. methods: " + totalLowMethods + ", avg size "
+ (totalLowMethodSize / totalLowMethods) + ", tot size "
+ totalLowMethodSize);
log("Done.");
os.clear();
} catch (Throwable ex) {
ex.printStackTrace();
throw new BuildException(ex);
}
}
/**
* Emit all objects to the native stream that have not yet been emitted to
* this stream.
*
* @param os
* @param arch
* @param blockObjects
* @throws BuildException
*/
private final void emitObjects(NativeStream os, BaseVmArchitecture arch,
Set<Object> blockObjects, boolean skipCopyStatics)
throws BuildException {
log("Emitting objects", Project.MSG_DEBUG);
PrintWriter debugOut = null;
final TreeSet<String> emittedClassNames = new TreeSet<String>();
try {
if (debug) {
debugOut = new PrintWriter(new FileWriter(debugFile, true));
}
final ObjectEmitter emitter = new ObjectEmitter(clsMgr, os,
debugOut, legalInstanceClasses);
final long start = System.currentTimeMillis();
int cnt = 0;
int lastUnresolved = -1;
int loops = 0;
while (true) {
loops++;
compileClasses(os, arch);
if (!skipCopyStatics) {
copyStaticFields(clsMgr, clsMgr.getSharedStatics(), clsMgr
.getIsolatedStatics(), os, emitter);
}
final Collection<ObjectRef> objectRefs = new ArrayList<ObjectRef>(
os.getObjectRefs());
int unresolvedFound = 0; // Number of unresolved references
// found in the following
// loop
int emitted = 0; // Number of emitted objects in the
// following
// loop
for (Iterator<ObjectRef> i = objectRefs.iterator(); i.hasNext();) {
X86BinaryAssembler.ObjectRef ref = i
.next();
if (!ref.isResolved()) {
final Object obj = ref.getObject();
if (!(obj instanceof Label)) {
unresolvedFound++;
if (obj instanceof VmType) {
final VmType<?> vmtObj = (VmType<?>) obj;
vmtObj.link();
if (!vmtObj.isCompiled()) {
compileClasses(os, arch);
}
}
boolean skip;
if (blockObjects == null) {
skip = false;
} else {
skip = blockObjects.contains(obj);
}
/*
* if (obj instanceof VmMethod) { final VmMethod
* mObj = (VmMethod)obj; if (!mObj.hasNativeCode()) {
* compileClasses(os, arch); } if
* (!mObj.getDeclaringClass().isCompiled()) {
* log("Oops"); }
*/
if (!skip) {
if (blockObjects == null) {
emittedClassNames.add(obj.getClass()
.getName());
// log("emitObject " +
// obj.getClass().getName());
}
// if (obj != skipMe) {
emitter.emitObject(obj);
emitted++;
X86BinaryAssembler.ObjectRef newRef = os
.getObjectRef(obj);
if (ref != newRef) {
throw new RuntimeException(
"Object has changed during emitObject! type="
+ obj.getClass().getName());
}
if (!ref.isResolved()) {
throw new RuntimeException("Unresolved reference to object " + ((obj == null) ?
"null" : obj.getClass().getName()));
}
}
}
}
}
if (unresolvedFound == lastUnresolved) {
if (unresolvedFound == 0) {
break;
}
if (blockedObjects != null) {
if (unresolvedFound == (emitted + blockObjects.size())) {
// log("UnresolvedFound " + unresolvedFound + ",
// emitted " + emitted + ",blocked " +
// blockObjects.size());
break;
}
if ((emitted == 0) && !blockObjects.isEmpty()) {
break;
}
}
}
lastUnresolved = unresolvedFound;
cnt += emitted;
}
final long end = System.currentTimeMillis();
log("Emitted " + cnt + " objects, took " + (end - start) + "ms in "
+ loops + " loops");
if (debugOut != null) {
debugOut.close();
debugOut = null;
}
if (blockObjects == null) {
log("Emitted classes: " + emittedClassNames, Project.MSG_INFO);
}
} catch (ClassNotFoundException ex) {
throw new BuildException(ex);
} catch (IOException ex) {
throw new BuildException(ex);
}
}
protected abstract void emitStaticInitializerCalls(NativeStream os,
VmType<?>[] bootClasses, Object clInitCaller)
throws ClassNotFoundException;
public final void execute() throws BuildException {
try {
InitialNaming.setNameSpace(new BasicNameSpace());
BootLogInstance.set(new BootLog() {
@Override
public void warn(String msg) {
System.out.println(msg);
}
@Override
public void warn(String msg, Throwable ex) {
System.out.println(msg);
ex.printStackTrace(System.out);
}
@Override
public void setDebugOut(PrintStream out) {
// ignore
}
@Override
public void info(String msg, Throwable ex) {
System.out.println(msg);
ex.printStackTrace(System.out);
}
@Override
public void info(String msg) {
System.out.println(msg);
}
@Override
public void fatal(String msg, Throwable ex) {
System.out.println(msg);
ex.printStackTrace(System.out);
}
@Override
public void fatal(String msg) {
System.out.println(msg);
}
@Override
public void error(String msg, Throwable ex) {
System.out.println(msg);
ex.printStackTrace(System.out);
}
@Override
public void error(String msg) {
System.out.println(msg);
}
@Override
public void debug(String msg, Throwable ex) {
System.out.println(msg);
ex.printStackTrace(System.out);
}
@Override
public void debug(String msg) {
System.out.println(msg);
}
});
// Create the image
doExecute();
// Remove all garbage objects
cleanup();
System.gc();
// Make sure that all finalizers are called, in order to remove tmp
// files.
Runtime.getRuntime().runFinalization();
} catch (BuildException be) {
be.printStackTrace();
throw be;
} catch (Throwable t) {
t.printStackTrace();
throw new BuildException(t);
}
}
/**
* Gets the target architecture.
*
* @return The target architecture
* @throws BuildException
*/
protected abstract BaseVmArchitecture getArchitecture() throws BuildException;
/**
* Gets the internal class loader.
*
* @return The class loader
*/
public VmSystemClassLoader getClsMgr() {
return clsMgr;
}
/**
* Returns the debugFile.
*
* @return File
*/
public final File getDebugFile() {
return debugFile;
}
/**
* Returns the destFile.
*
* @return File
*/
public final File getDestFile() {
return destFile;
}
/**
* @return Returns the jnodeCompiler.
*/
public final String getJnodeCompiler() {
return jnodeCompiler;
}
/**
* Returns the kernelFile.
*
* @return File
*/
public final File getKernelFile() {
return kernelFile;
}
/**
* Returns the listFile.
*
* @return File
*/
public final File getListFile() {
return listFile;
}
/**
* @return Returns the version.
*/
public final String getVersion() {
return this.version;
}
/**
* Convert a given int to an hexidecimal representation of 8 characters
* long.
*
* @param v
* @return The hex string
*/
protected final String hex(int v) {
String s = Integer.toHexString(v);
return zero8.substring(s.length()) + s;
}
/**
* Convert a given int to an hexidecimal representation of 16 characters
* long.
*
* @param v
* @return The hex string
*/
protected final String hex(long v) {
String s = Long.toHexString(v);
return zero16.substring(s.length()) + s;
}
/**
* Emit code to bootstrap the java image.
*
* @param os
* @param clInitCaller
* @param vm
* @param pluginRegistry
* @throws BuildException
*/
protected abstract void initImageHeader(NativeStream os,
Label clInitCaller, Vm vm, PluginRegistry pluginRegistry)
throws BuildException;
/**
* Should the given type be compiled with the best compiler.
*
* @param vmClass
* @return {@code true} if it should, {@code false} if not.
*/
protected boolean isCompileHighOptLevel(VmType<?> vmClass) {
return vmClass.isArray() || isCompileHighOptLevel(vmClass.getName());
}
private boolean isCompileHighOptLevel(String name) {
if (compileHighOptLevelPackages.contains(name)) {
return true;
}
final int lastDotIdx = name.lastIndexOf('.');
final String pkg = (lastDotIdx > 0) ? name.substring(0, lastDotIdx) : "";
if (compileHighOptLevelPackages.contains(pkg)) {
return true;
}
for (String s : compileHighOptLevelPackages) {
if (s.endsWith("*")) {
if (name.startsWith(s.substring(0, s.length() - 1))) {
return true;
}
}
}
return false;
}
/**
* Link all undefined symbols from the kernel native code.
*
* @param os
* @throws ClassNotFoundException
* @throws UnresolvedObjectRefException
*/
protected abstract void linkNativeSymbols(NativeStream os)
throws ClassNotFoundException, UnresolvedObjectRefException;
/**
* Load a VmClass for a given java.lang.Class.
*
* @param c
* @return The loaded class
* @throws ClassNotFoundException
*/
public final VmType<?> loadClass(Class<?> c) throws ClassNotFoundException {
String name = c.getName();
VmType<?> cls = clsMgr.findLoadedClass(name);
if (cls != null) {
return cls;
} else if (c.isPrimitive()) {
if ("boolean".equals(name)) {
cls = VmType.getPrimitiveClass('Z');
} else if ("byte".equals(name)) {
cls = VmType.getPrimitiveClass('B');
} else if ("char".equals(name)) {
cls = VmType.getPrimitiveClass('C');
} else if ("short".equals(name)) {
cls = VmType.getPrimitiveClass('S');
} else if ("int".equals(name)) {
cls = VmType.getPrimitiveClass('I');
} else if ("float".equals(name)) {
cls = VmType.getPrimitiveClass('F');
} else if ("long".equals(name)) {
cls = VmType.getPrimitiveClass('J');
} else if ("double".equals(name)) {
cls = VmType.getPrimitiveClass('D');
} else {
throw new ClassNotFoundException(name
+ " is not a primitive type");
}
clsMgr.addLoadedClass(name, cls);
return cls;
} else {
return loadClass(name, true);
}
}
/**
* Load a VmClass with a given name.
*
* @param name
* @return The loaded class
* @throws ClassNotFoundException
*/
public final VmType<?> loadClass(String name)
throws ClassNotFoundException {
return loadClass(name, true);
}
/**
* Load a VmClass with a given name.
*
* @param name
* @param resolve
* @return The loaded class
* @throws ClassNotFoundException
*/
public final VmType<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
/*
* if (clsMgr == null) { clsMgr = new VmClassLoader(classesURL);
*/
return clsMgr.loadClass(name, resolve);
}
/**
* Load all classes from the bootjar.
*/
protected final void loadSystemClasses(Collection<String> resourceNames) throws IOException,
ClassNotFoundException {
for (String eName : new ArrayList<String>(resourceNames)) {
if (eName.endsWith(".class")) {
final String cName = eName.substring(0,
eName.length() - ".class".length()).replace('/', '.');
final int lastDotIdx = cName.lastIndexOf('.');
final String pkg = (lastDotIdx > 0) ? cName.substring(0, lastDotIdx) : "";
if (isCompileHighOptLevel(cName) ||
preloadPackages.contains(cName) || preloadPackages.contains(pkg)) {
loadClass(cName, true);
}
}
}
}
protected abstract void logStatistics(NativeStream os);
/**
* Align the stream on a page boundary.
*
* @param os
* @throws BuildException
*/
protected abstract void pageAlign(NativeStream os) throws BuildException;
/**
* Patch any fields in the header, just before the image is written to disk.
*
* @param os
* @throws BuildException
*/
protected abstract void patchHeader(NativeStream os) throws BuildException;
/**
* Print any unresolved labels to the out stream and generate a list file
* for all public labels.
*
* @param os
* @param bootClasses
* @throws BuildException
* @throws UnresolvedObjectRefException
*/
protected final void printLabels(NativeStream os, VmType<?>[] bootClasses,
VmSharedStatics statics) throws BuildException,
UnresolvedObjectRefException {
if (System.getProperty("bootimage.log") == null) {
return;
}
try {
int unresolvedCount = 0;
final PrintWriter w = new PrintWriter(new FileWriter(listFile));
// Print a list of boot classes.
for (int i = 0; i < bootClasses.length; i++) {
final VmType<?> vmClass = bootClasses[i];
w.print("bootclass ");
w.print(i);
w.print(": ");
w.print(vmClass.getName());
if (vmClass instanceof VmClassType) {
final int cnt = ((VmClassType<?>) vmClass)
.getInstanceCount();
if (cnt > 0) {
w.print(", ");
w.print(cnt);
w.print(" instances");
if (vmClass instanceof VmNormalClass) {
long objSize = ((VmNormalClass<?>) vmClass)
.getObjectSize();
long totalSize = objSize * cnt;
w.print(", ");
w.print(objSize);
w.print(" objsize ");
w.print(totalSize);
w.print(" totsize");
if (totalSize > 200000) {
log(vmClass.getName() + " is large ("
+ totalSize + " , #" + cnt + ")",
Project.MSG_WARN);
}
}
}
}
if (vmClass.isArray()) {
final long len = ((VmArrayClass<?>) vmClass)
.getTotalLength();
if (len > 0) {
w.print(", ");
w.print(len);
w.print(" total length ");
w.print(len
/ ((VmArrayClass<?>) vmClass)
.getInstanceCount());
w.print(" avg length ");
w.print(((VmArrayClass<?>) vmClass)
.getMaximumLength());
w.print(" max length ");
}
}
int cnt = vmClass.getNoInterfaces();
if (cnt > 0) {
w.print(", ");
w.print(cnt);
w.print(" interfaces");
}
w.print(vmClass.isInitialized() ? "" : ", not initialized");
w.println();
}
w.println();
// Print the statics table
final int[] table = (int[]) statics.getTable();
for (int i = 0; i < table.length; i++) {
w.print(NumberUtils.hex((VmArray.DATA_OFFSET + i) << 2));
w.print(":");
w.print(NumberUtils.hex(statics.getType(i), 2));
w.print("\t");
w.print(NumberUtils.hex(table[i]));
w.println();
}
// Look for unresolved labels and put all resolved
// label into the sorted map. This will be used later
// to print to the listing file.
final Collection<? extends ObjectRef> xrefs = os.getObjectRefs();
final SortedMap<Integer, ObjectRef> map = new TreeMap<Integer, ObjectRef>();
for (ObjectRef ref : xrefs) {
if (!ref.isResolved()) {
StringBuffer buf = new StringBuffer();
buf.append(" $" + Integer.toHexString(ref.getOffset()));
buf.append("\t" + ref.getObject());
System.err.println("Unresolved label " + buf.toString());
unresolvedCount++;
} else {
map.put(new Integer(ref.getOffset()), ref);
}
}
if (unresolvedCount > 0) {
throw new BuildException("There are " + unresolvedCount
+ " unresolved labels");
} // Print the
// listing
// file.
for (ObjectRef ref : map.values()) {
final Object object = ref.getObject();
w.print('$');
w.print(hex(ref.getOffset() + os.getBaseAddr()));
w.print('\t');
w.print(object);
w.print(" (");
if (object instanceof VmSystemObject) {
final String info = ((VmSystemObject) object)
.getExtraInfo();
if (info != null) {
w.print(info);
w.print(", ");
}
}
w.print(object.getClass().getName());
w.println(')');
}
w.close();
} catch (IOException ex) {
throw new BuildException("Writing list", ex);
}
}
/**
* Print any unresolved labels to the out stream and generate a list file
* for all public labels.
*
* @param bootClasses
* @throws BuildException
* @throws UnresolvedObjectRefException
*/
protected final void logLargeClasses(VmType<?>[] bootClasses) {
final Comparator<Long> reverseComp = Collections.reverseOrder();
final TreeMap<Long, VmType<?>> sortedTypes = new TreeMap<Long, VmType<?>>(reverseComp);
for (VmType<?> vmType : bootClasses) {
if (vmType instanceof VmNormalClass) {
final VmNormalClass<?> nc = (VmNormalClass<?>) vmType;
final long objSize = nc.getObjectSize();
final int cnt = nc.getInstanceCount();
final long totalSize = objSize * cnt;
sortedTypes.put(totalSize, nc);
} else if (vmType.isArray()) {
final VmArrayClass<?> ac = (VmArrayClass<?>) vmType;
final long len = ac.getTotalLength();
final int typeSize = ac.getComponentType().getTypeSize();
sortedTypes.put(len * typeSize, ac);
}
}
int cnt = 1;
log("Large classes:");
for (Map.Entry<Long, VmType<?>> entry : sortedTypes.entrySet()) {
log(" " + entry.getValue().getName() + " " + NumberUtils.toBinaryByte(entry.getKey()));
if (++cnt > 10) {
return;
}
}
}
/**
* Sets the debugFile.
*
* @param debugFile The debugFile to set
*/
public final void setDebugFile(File debugFile) {
this.debugFile = debugFile;
}
/**
* Sets the destFile.
*
* @param destFile The destFile to set
*/
public final void setDestFile(File destFile) {
this.destFile = destFile;
}
/**
* @param jnodeCompiler The jnodeCompiler to set.
*/
public final void setJnodeCompiler(String jnodeCompiler) {
this.jnodeCompiler = jnodeCompiler;
}
/**
* Sets the kernelFile.
*
* @param kernelFile The kernelFile to set
*/
public final void setKernelFile(File kernelFile) {
this.kernelFile = kernelFile;
}
/**
* Sets the listFile.
*
* @param listFile The listFile to set
*/
public void setListFile(File listFile) {
this.listFile = listFile;
}
/**
* Sets the core class list file.
*
* @param coreClassListFile The coreClassListFile to set
*/
public void setCoreClassListFile(File coreClassListFile) {
this.coreClassListFile = coreClassListFile;
}
protected void setupCompileHighOptLevelPackages() {
addCompileHighOptLevel(loadClassList(coreClassListFile));
for (NativeCodeCompiler compiler : getArchitecture().getCompilers()) {
for (String packageName : compiler.getCompilerPackages()) {
addCompileHighOptLevel(packageName);
}
}
}
protected void addCompileHighOptLevel(List<String> classNames) {
for (String className : classNames) {
addCompileHighOptLevel(className);
}
}
protected List<String> loadClassList(File file) {
ArrayList<String> classNames = new ArrayList<String>();
FileReader fr;
try {
fr = new FileReader(file);
} catch (IOException ex) {
throw new BuildException("Cannot open '" + file + "'", ex);
}
try {
BufferedReader br = new BufferedReader(fr);
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#") || line.startsWith("/")) {
continue;
}
classNames.add(line);
}
} catch (IOException ex) {
throw new BuildException("Error reading '" + file + "'", ex);
} finally {
try {
fr.close();
} catch (IOException ex) {
// ignore
}
}
return classNames;
}
/**
* Create a set of the names of those classes that can be safely
* instantiated during the boot process (and written as instance to the boot
* image). Usually java.xxx classes cannot be used, since Sun may have
* implemented them different from our implementation. If the implementation
* is difference, the image will contain incorrect fiels and values.
*
* @return Set<String>
*/
protected Set<String> setupLegalInstanceClasses() {
final HashSet<String> set = new HashSet<String>();
set.add("java.lang.Integer");
set.add("java.lang.Long");
set.add("java.lang.Float");
set.add("java.lang.Double");
set.add("java.lang.String");
set.add("org.jnode.util.Logger");
return set;
}
/**
* @param version The version to set.
*/
public final void setVersion(String version) {
this.version = version;
}
/**
* Save the native stream to destFile.
*
* @param os
* @throws BuildException
*/
protected void storeImage(NativeStream os) throws BuildException {
try {
log("Creating image");
FileOutputStream fos = new FileOutputStream(destFile);
fos.write(os.getBytes(), 0, os.getLength());
fos.close();
} catch (IOException ex) {
throw new BuildException(ex);
}
}
protected void copyStaticFields(VmSystemClassLoader cl,
VmSharedStatics sharedStatics, VmIsolatedStatics isolatedStatics,
NativeStream os, ObjectEmitter emitter)
throws ClassNotFoundException {
for (VmType<?> type : cl.getLoadedClasses()) {
final String name = type.getName();
final int cnt = type.getNoDeclaredFields();
if ((cnt > 0) && !name.startsWith("java.")) {
final Class<?> javaType = Class.forName(type.getName());
try {
final FieldInfo fieldInfo = emitter.getFieldInfo(javaType);
final Field[] jdkFields = fieldInfo.getJdkStaticFields();
final int max = jdkFields.length;
for (int k = 0; k < max; k++) {
final Field jdkField = jdkFields[k];
if (jdkField != null) {
final VmField f = fieldInfo.getJNodeStaticField(k);
if (!f.isTransient()) {
try {
copyStaticField(type, f, jdkField,
sharedStatics, isolatedStatics, os,
emitter);
} catch (IllegalAccessException ex) {
throw new BuildException(ex);
}
}
}
}
if (name.startsWith("org.mmtk.") || type.isEnum()) {
type.setAlwaysInitialized();
}
} catch (JNodeClassNotFoundException ex) {
log("JNode class not found " + ex.getMessage());
}
}
}
}
private void copyStaticField(VmType<?> type, VmField f, Field jf,
VmSharedStatics sharedStatics, VmIsolatedStatics isolatedStatics,
NativeStream os, ObjectEmitter emitter)
throws IllegalAccessException, JNodeClassNotFoundException {
jf.setAccessible(true);
final Object val = jf.get(null);
final int fType = JvmType.SignatureToType(f.getSignature());
final int idx;
final VmStaticField sf = (VmStaticField) f;
final VmStatics statics;
if (sf.isShared()) {
idx = sf.getSharedStaticsIndex();
statics = sharedStatics;
} else {
idx = sf.getIsolatedStaticsIndex();
statics = isolatedStatics;
}
if (f.isPrimitive()) {
if (f.isWide()) {
final long lval;
switch (fType) {
case JvmType.LONG:
lval = ((Long) val).longValue();
break;
case JvmType.DOUBLE:
lval = Double.doubleToRawLongBits(((Double) val)
.doubleValue());
break;
default:
throw new IllegalArgumentException("Unknown wide type "
+ fType);
}
statics.setLong(idx, lval);
} else {
final int ival;
final Class<?> jfType = jf.getType();
if (jfType == boolean.class) {
ival = ((Boolean) val).booleanValue() ? 1 : 0;
} else if (jfType == byte.class) {
ival = ((Byte) val).byteValue();
} else if (jfType == char.class) {
ival = ((Character) val).charValue();
} else if (jfType == short.class) {
ival = ((Short) val).shortValue();
} else if (jfType == int.class) {
ival = ((Number) val).intValue();
} else if (jfType == float.class) {
ival = Float.floatToRawIntBits(((Float) val).floatValue());
} else {
throw new IllegalArgumentException("Unknown wide type "
+ fType);
}
statics.setInt(idx, ival);
}
} else if (f.isAddressType()) {
if (val == null) {
// Just do nothing
} else if (val instanceof UnboxedObject) {
final UnboxedObject uobj = (UnboxedObject) val;
statics.setAddress(idx, uobj);
} else if (val instanceof Label) {
final Label lbl = (Label) val;
statics.setAddress(idx, lbl);
} else {
throw new BuildException("Cannot handle magic type " + val.getClass().getName());
}
} else {
if (!Modifier.isAddressType(f.getSignature())) {
if (val != null) {
emitter.testForValidEmit(val, type.getName());
os.getObjectRef(val);
}
statics.setObject(idx, val);
}
}
}
/**
* Initialize the statics table.
*
* @param statics
*/
protected abstract void initializeStatics(VmSharedStatics statics)
throws BuildException;
/**
* @return Returns the memMgrPluginId.
*/
public final String getMemMgrPluginId() {
return memMgrPluginId;
}
/**
* @param memMgrPluginId The memMgrPluginId to set.
*/
public final void setMemMgrPluginId(String memMgrPluginId) {
this.memMgrPluginId = memMgrPluginId;
}
/**
* @return Returns the enableJNasm.
*/
public final boolean isEnableJNasm() {
return enableJNasm;
}
/**
* @param enableJNasm The enableJNasm to set.
*/
public final void setEnableJNasm(boolean enableJNasm) {
this.enableJNasm = enableJNasm;
}
}