Package org.openquark.cal.machine

Source Code of org.openquark.cal.machine.Module

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* Module.java
* Creation date: (March 6, 2000)
* By: Luke Evans
*/
package org.openquark.cal.machine;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.internal.machine.MachineFunctionImpl;
import org.openquark.cal.internal.machine.lecc.LECCModule;
import org.openquark.cal.internal.serialization.ModuleSerializationTags;
import org.openquark.cal.internal.serialization.RecordInputStream;
import org.openquark.cal.internal.serialization.RecordOutputStream;
import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo;


/**
* Warning- this class should only be used by the CAL runtime implementation. It is not part of the
* external API of the CAL platform.
* <P>
* Module collects together machine specific information about the contents of
* a module and associates it with the MoudleTypeInfo for that module.
* Modules contain the executable program elements and have a name.
* Creation date: (3/6/00 9:48:37 AM)
* @author LWE
*/
abstract public class Module {
   
    public static final String COMPILED_MODULE_SUFFIX = "cmi";
   
    /**
     * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by
     * the {@link #loadGeneratedCodeInfo} method.
     */
    private static final short[] GENERATED_CODE_INFO_RECORD_TAGS = new short[] {
        ModuleSerializationTags.LECC_GENERATED_CODE_INFO,
        ModuleSerializationTags.G_GENERATED_CODE_INFO
    };

    private static final int moduleSerializationSchema = 0;
    private static final int importSerializationSchema = 1;
    private static final int generatedCodeInfoSerializationSchema = 0;
   
    /**
     * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by
     * the {@link #load} method.
     */
    private static final short[] MODULE_RECORD_TAGS = new short[] {
        ModuleSerializationTags.LECC_MODULE,
        ModuleSerializationTags.G_MODULE
    };
   
    /** A lock for guarding access to {@link #functionNameToFunctionMap} and {@link #adjunctFunctionNameToFunctionMap}. */
    // Note: this lock is private, and is not meant to be used by subclasses for their own locking needs.
    private final int[] lock = new int[0];

    /** Information about the public entities exported by the module */
    private final ModuleTypeInfo moduleTypeInfo;
 
    /** (String -> MachineFunction) the program entities */
    /* @GuardedBy("lock") */
    private final Map<String, MachineFunction> functionNameToFunctionMap;

    /** (String -> MachineFunction) any adjunct entities in this module. */
    /* @GuardedBy("lock") */
    private transient Map<String, MachineFunction> adjunctFunctionNameToFunctionMap;
   
    /** The classloader to use to resolve foreign classes for this module. */
    private final ClassLoader foreignClassLoader;

    /** The GeneratedCodeInfo for the persisted form of this module.
     *  This is set under these circumstances:
     *  1) During deserialization, this is set directly in the constructor.
     *  2) During compilation, this is set when CodeGenerator.GeneratedCodeInfo.write() is called.
     * 
     *  ie. if this module has been loaded or compiled to a persisted form, this member should always be set.
     *  Otherwise null.
     */
    private GeneratedCodeInfo generatedCodeInfo;
   
    /**
     * Constructor for a Module.
     *
     * @param name the name of the module
     * @param foreignClassLoader the classloader to use to resolve foreign classes for the module.
     */
    protected Module(ModuleName name, ClassLoader foreignClassLoader) {
        if (name == null || foreignClassLoader == null) {
            throw new NullPointerException();
        }
       
        this.foreignClassLoader = foreignClassLoader;
        functionNameToFunctionMap = new HashMap<String, MachineFunction>();
        adjunctFunctionNameToFunctionMap = new HashMap<String, MachineFunction>();
        moduleTypeInfo = new ModuleTypeInfo(name, this);
    }

    /**
     * Constructor for a Module.
     *
     * @param name the name of the module
     * @param foreignClassLoader the classloader to use to resolve foreign classes for the module.
     * @param generatedCodeInfo the CodeGenerator.GeneratedCodeInfo associated with this module, or null if none.
     */
    protected Module(ModuleName name, ClassLoader foreignClassLoader, GeneratedCodeInfo generatedCodeInfo) {
        this(name, foreignClassLoader);
        this.generatedCodeInfo = generatedCodeInfo;
    }
   
    public final void clearAdjunctFunctions () {
        synchronized (lock) {
            adjunctFunctionNameToFunctionMap = new HashMap<String, MachineFunction>();
        }
    }
   
    /**
     * Add a MachineFunction to this Module.
     * New function objects replace old ones with the same name.
     * @param machineFunction the label to add
     */
    public final void addFunction(MachineFunction machineFunction) {       
        synchronized (lock) {
            if (machineFunction.isForAdjunct()) {
                adjunctFunctionNameToFunctionMap.put(machineFunction.getName(), machineFunction);
            } else {
                functionNameToFunctionMap.put(machineFunction.getName(), machineFunction);
            }
        }
    }
   
    /**
     * Fetch a function by name.
     * @param functionName
     * @return the MachineFunction instance for the named function.  Null if it doesn't exist.
     */
    public MachineFunction getFunction (String functionName) {
        // Check functions in the module and adjunct both.
        synchronized (lock) {
            MachineFunction mf = functionNameToFunctionMap.get(functionName);
            if (mf == null) {
                mf = adjunctFunctionNameToFunctionMap.get(functionName);
            }

            return mf;
        }
    }

    /**
     * Fetch a label by name.
     * The label can be in this module, in an imported module, in an import of an imported module, etc.
     * @param functionName - the qualified name of a function
     * @return the MachineFunction instance for the named function.  Null if it doesn't exist.
     */
    public MachineFunction getFunction (QualifiedName functionName) {
        synchronized (lock) {
            if (functionName.getModuleName().equals(getName())) {
                return getFunction(functionName.getUnqualifiedName());
            } else {
                Module m = findModule (functionName.getModuleName());
                if (m != null) {
                    return m.getFunction(functionName);
                }
            }

            // No label or other horridness
            return null;
        }
    }
   
    /**
     *
     * @param moduleName
     * @return The named module, or null if it cannot be referenced from this module
     *   (ie. it is not this module, one of its imports, an import of its imports, etc..)
     */
    public final Module findModule (ModuleName moduleName) {
        if (this.getName().equals (moduleName)) {
            return this;
        }
       
        ModuleTypeInfo foundTypeInfo = getModuleTypeInfo().getDependeeModuleTypeInfo(moduleName);
        return foundTypeInfo == null ? null : foundTypeInfo.getModule();
    }
   
    /**
     * Return a <b>copy</b> of the functions in this module.
     *
     * @return a copy of the functions in this module in a new Collection object.
     */
    public final Collection<MachineFunction> getFunctions () {
        /*
         * THREAD-SAFETY ISSUE:
         * this needs to be a copy, and not an unmodifiable view based on the underlying
         * collection (e.g. via Collections.unmodifiableCollection()), because iterating through
         * such a list is incompatible with concurrent modification (it would result in a
         * java.util.ConcurrentModificationException). Such a scenario will occur
         * with compilations taking place simultaneously on multiple threads.
         */
       
        synchronized (lock) {
            List<MachineFunction> l =  new ArrayList<MachineFunction> (functionNameToFunctionMap.values());
            if(adjunctFunctionNameToFunctionMap.size() > 0) {
                l.addAll(adjunctFunctionNameToFunctionMap.values());
            }

            return l;
        }
    }
   
    /**
     * @return The number of functions currently in this module.
     */
    public final int getNFunctions() {
        synchronized (lock) {
            return functionNameToFunctionMap.size() + adjunctFunctionNameToFunctionMap.size();
        }
    }

    /**
     * Get the name of the module.
     * @return the name of the module
     */
    public final ModuleName getName() {
        return moduleTypeInfo.getModuleName();
    }
  
    public final ModuleTypeInfo getModuleTypeInfo() {
        return moduleTypeInfo;
    }

    /**
     * Returns true if this module depends on the named module.
     * @param moduleName
     * @return boolean
     */
    public final boolean dependsOn (ModuleName moduleName) {
        return (moduleTypeInfo.getImportedModule(moduleName) != null);
    }
   
    /**
     * Disassemble the code in the Program.
     * @return a string representation of the code
     */
    @Override
    public final String toString() {

        StringBuilder sb = new StringBuilder("module ");
        sb.append(getName());
        sb.append('\n');

        // For each code contribution
        synchronized (lock) {
          
            for (final Map.Entry<String, MachineFunction> entry : functionNameToFunctionMap.entrySet()) {
                MachineFunction label = entry.getValue();              
                sb.append (label.toString() + "\n");
            }
        }

        return sb.toString();
    }
   
    /**
     * Write out the imported modules to the RecordOutputStream.
     * @param s
     * @throws IOException
     */
    private void writeImports (RecordOutputStream s) throws IOException {
        // Write out a record that contains the module name
        // and the names of all the modules it depends on.
        s.startRecord(ModuleSerializationTags.MODULE_IMPORTS, importSerializationSchema);
        s.writeModuleName(getName());
        int nImports = moduleTypeInfo.getNImportedModules();
        s.writeInt(nImports);
        for (int i = 0; i < nImports; ++i) {
            ModuleTypeInfo mti = moduleTypeInfo.getNthImportedModule(i);
            s.writeModuleName(mti.getModuleName());
        }
        s.endRecord();
       
    }
   
    /**
     * Write meta information about the serialized module.
     * Timestamp of serialization, information about the generated code (e.g. version), etc.
     * @param s
     * @throws IOException
     */
    private void writeSerializationInfo (RecordOutputStream s) throws IOException {
        // The usual pattern is to write the record for the subclass first, followed by the
        // record for the superclass.  However, in this case we write the superclass record
        // first (ie, SERIALIZATION_INFO before LECC_GENERATED_CODE_INFO or G_GENERATED_CODE_INFO)
        // because CompiledModuleSourceDefinition.getSerializedTimestamp needs to be able to skip
        // straight to the SERIALIZATION_INFO record.
        //
        // So, if you change the format of the SERIALIZATION_INFO record, you will probably need to change
        // CompiledModuleSourceDefinition.getSerializedTimestamp and LECCModule.readLeccSpecificSerializationInfo
        // as well.
        s.startRecord(ModuleSerializationTags.SERIALIZATION_INFO, generatedCodeInfoSerializationSchema);
        s.writeLong(System.currentTimeMillis());
        writeMachineSpecificSerializationInfo(s);
        s.endRecord();
    }

    /**
     * Write out machine-specific meta information about the serialized module
     * @param s
     * @throws IOException
     */
    abstract protected void writeMachineSpecificSerializationInfo(RecordOutputStream s) throws IOException;
   
    /**
     * Write out the content specific to the Module class.
     * @param s
     * @throws IOException
     */
    protected void writeContent (RecordOutputStream s) throws IOException {
        s.startRecord (ModuleSerializationTags.MODULE, moduleSerializationSchema);
        moduleTypeInfo.write (s);
       
        synchronized (lock) {
            s.writeInt(functionNameToFunctionMap.size());
          
            for (final Map.Entry<String, MachineFunction> entry : functionNameToFunctionMap.entrySet()) {
               
                MachineFunctionImpl mf = (MachineFunctionImpl)entry.getValue();
                mf.write (s);
            }
        }
        s.endRecord ();
    }
   
    /**
     * Write this Module instance out to the RecordOutputStream.
     * @param s
     * @throws IOException
     */
    public void write (RecordOutputStream s) throws IOException {
        // Write out the information about the generated code in
        // this module.
        writeSerializationInfo(s);
       
        // Write out the imports information.
        writeImports(s);
       
        // Now delegate to the derived class.
        writeActual(s);
    }
   
    /**
     * Write out the Module instance.
     * @param s
     * @throws IOException
     */
    abstract protected void writeActual (RecordOutputStream s) throws IOException;
   
    /**
     * Read the contents of this Module, specific to the Module class, from the RecordInputStream.
     * The read position will be before the Module record header.
     * @param s
     * @param otherModules
     * @param msgLogger the logger to which to log deserialization messages.
     * @throws IOException
     */
    protected void readContent (RecordInputStream s, Map<ModuleName, Module> otherModules, CompilerMessageLogger msgLogger) throws IOException {
       
        // At this point we should be at the beginning of the Module record.
        RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.MODULE);
        if (rhi == null) {
            throw new IOException ("Unable to find Module record.");
        }
        if (rhi.getSchema() > moduleSerializationSchema) {
            throw new IOException("Saved schema is greather than current schema in Module.");
        }
       
       
        moduleTypeInfo.readContent(s, otherModules, msgLogger);
       
        synchronized (lock) {
            int nFunctions = s.readInt();
            for (int i = 0; i < nFunctions; ++i) {
                MachineFunction mf = loadMachineFunction(s, getModuleTypeInfo(), msgLogger);
                functionNameToFunctionMap.put(mf.getName(), mf);
            }
        }
       
        s.skipRestOfRecord();
    }
   
    /**
     * This method is used to delegate loading of MachineFunciton instances
     * to concrete extensions of Module.  The sub-classes of module contain
     * the information about what class to actually load.
     * @param s
     * @param mti
     * @param msgLogger
     * @return an instance of MachineFunction
     * @throws IOException
     */
    protected abstract MachineFunction loadMachineFunction(RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException;
       
   
    /**
     * Read module import information and return a Set of dependee module names.
     * @param s
     * @return Set of ModuleName representing the imported modules.
     * @throws IOException
     */
    public static Set<ModuleName> readDependencies (RecordInputStream s) throws IOException {
        RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.MODULE_IMPORTS);
        if (rhi == null) {
            throw new IOException ("Unable to find module dependencies record.");
        }
        if (rhi.getSchema() > importSerializationSchema) {
            throw new IOException("Saved schema is greather than current schema in Module.");
        }
        if (rhi.getSchema() < importSerializationSchema) {
            throw new IOException("Saved schema is less than current schema in Module. Earlier import serialization schemas are not supported.");
        }
        /* ModuleName moduleName = */ s.readModuleName();
        int nImports = s.readInt();
        Set<ModuleName> set = new HashSet<ModuleName>();
        for (int i = 0; i < nImports; ++i) {
            ModuleName imported = s.readModuleName();
            set.add(imported);
        }
       
        s.skipRestOfRecord();
       
        return set;
    }

    /**
     * @return whether Expressions must be loaded during deserialization.
     * For instance, this will be false if usable generated code exists in the resource repository.
     */
    public abstract boolean mustLoadExpressions();
   
    /**
     * Read the generated code info from the RecordInputStream.
     * @param s
     * @return the module-specific GeneratedCodeInfo.
     * @throws IOException
     */
    public static final GeneratedCodeInfo loadGeneratedCodeInfo(RecordInputStream s) throws IOException {
        // Load the record header and determine which class this is.
        RecordHeaderInfo rhi = s.findRecord(GENERATED_CODE_INFO_RECORD_TAGS);
        if (rhi == null) {
            throw new IOException ("Unable to find module record.");
        }

        if (rhi.getRecordTag() == ModuleSerializationTags.LECC_GENERATED_CODE_INFO) {
            return LECCModule.loadCodeInfo(s, rhi.getSchema())// read the module name and the generated code info.
        } else
            if (rhi.getRecordTag() == ModuleSerializationTags.G_GENERATED_CODE_INFO) {
                throw new IOException ("Loading of g-machine specific compiled modules is not yet supported.");
            } else {
                throw new IOException ("Unexpected record tag found in Module.read: " + rhi.getRecordTag());
            }
    }
   
    /**
     * Load one of the concrete subclasses of Module.
     * @param s
     * @param loadedModules - map of module names to loaded modules.
     * @param foreignClassLoader the classloader to use to resolve foreign classes for the module.
     * @param generatedCodeInfo The GeneratedCodeInfo for the persisted form of this module.
     * @param msgLogger the logger to which to log deserialization messages.
     * This should be used to log user (non-internal) errors only.
     * @return the new Module instance.
     * If there was a problem reading the module, null is returned and errors will be logged to the msg logger.
     *
     * @throws IOException if any internal errors occurred while loading the module.
     */
    public final static Module load (RecordInputStream s, Map<ModuleName, Module> loadedModules, ClassLoader foreignClassLoader,
            GeneratedCodeInfo generatedCodeInfo, CompilerMessageLogger msgLogger) throws IOException {
       
        // Load the record header and determine which class this is.
        RecordHeaderInfo rhi = s.findRecord(MODULE_RECORD_TAGS);
        if (rhi == null) {
            throw new IOException ("Unable to find module record.");
        }
       
        if (rhi.getRecordTag() == ModuleSerializationTags.LECC_MODULE) {
            return LECCModule.load(s, rhi.getSchema(), loadedModules, foreignClassLoader, generatedCodeInfo, msgLogger);
        } else
        if (rhi.getRecordTag() == ModuleSerializationTags.G_MODULE) {
            throw new IOException ("Loading of g-machine specific compiled modules is not yet supported.");
        } else {
            throw new IOException ("Unexpected record tag found in Module.read: " + rhi.getRecordTag());
        }
    }
   
    /**
     * @return a Set of String which contains the names of all direct
     *     and indirect dependee modules.
     */
    public final Set<ModuleName> getDependeeModuleNames () {
        return getDependeeModuleNames (new HashSet<ModuleName>());
    }
   
    /**
     * Build up a list of module names for all direct and
     * indirect dependee modules.
     * @param names - The set of dependee module names thus far
     * @return Set of String, all direct and indirect dependee module names.
     */
    private final Set<ModuleName> getDependeeModuleNames (Set<ModuleName> names) {
        for (int i = 0, n = getModuleTypeInfo().getNImportedModules(); i < n; ++i) {
            ModuleTypeInfo importedMTI = getModuleTypeInfo().getNthImportedModule(i);
            if (!names.contains(importedMTI.getModuleName())) {
                names.add(importedMTI.getModuleName());
                importedMTI.getModule().getDependeeModuleNames(names);
            }
        }
        return names;
    }

   
    /**
     * @return Returns the classloader to use to resolve foreign classes for this module.
     * Never null.
     */
    public final ClassLoader getForeignClassLoader() {
        return foreignClassLoader;
    }
   
    /**
     * @return the generatedCodeInfo, or null if this module has not been persisted to or loaded from the repository.
     */
    public final GeneratedCodeInfo getGeneratedCodeInfo() {
        return generatedCodeInfo;
    }
   
    /**
     * @param generatedCodeInfo the generatedCodeInfo to set
     */
    protected final void setGeneratedCodeInfo(GeneratedCodeInfo generatedCodeInfo) {
        this.generatedCodeInfo = generatedCodeInfo;
    }
}
TOP

Related Classes of org.openquark.cal.machine.Module

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.