Package org.jnode.build

Source Code of org.jnode.build.AbstractBootImageBuilder

/*
* $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&lt;String&gt;
     */
    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;
    }
}
TOP

Related Classes of org.jnode.build.AbstractBootImageBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.