Package org.openquark.cal.internal.machine.lecc

Source Code of org.openquark.cal.internal.machine.lecc.LECCModule$ClassNameMapper

/*
* 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.
*/


/*
* LECCModule.java
* Created: Feb 28, 2005
* By: Raymond Cypher
*/

package org.openquark.cal.internal.machine.lecc;

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

import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.DeserializationHelper;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.Packager;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.internal.machine.MachineFunctionImpl;
import org.openquark.cal.internal.machine.lecc.JavaPackager.LECCMachineFunction;
import org.openquark.cal.internal.runtime.lecc.LECCMachineConfiguration;
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;
import org.openquark.cal.machine.GeneratedCodeInfo;
import org.openquark.cal.machine.MachineFunction;
import org.openquark.cal.machine.Module;
import org.openquark.cal.machine.ProgramResourceLocator;
import org.openquark.cal.machine.ProgramResourceRepository;
import org.openquark.cal.runtime.ExecutionContext;
import org.openquark.cal.services.ResourcePath;


/**
* The version of Module specific to the lecc machine.
* @author RCypher
* Created: May 14, 2004
*/
public final class LECCModule extends Module {
   
    private static final int serializationSchema = 0;
   
    /** A lock for access to the {@link #classLoader} field. */
    // Note: this is a separate lock from the one in the superclass. In fact, if the classLoader field is guarded by
    // the same lock as the one used for guarding the function name maps in the superclass, deadlock may occur,
    // as follows:
    // Thread 1:
    //   LECCModule.resetCachedResults() [acquires lock for the LECCModule (classloader access)]
    //   -> CALClassLoader.resetCachedResults()
    //   -> Class.getMethod()
    //   -> ...
    //   -> Class.getDeclaredMethods0() [needs lock for the CALClassLoader, for classloading purposes]
    // Thread 2:
    //   ClassLoader.loadClassInternal() [acquires lock for the CALClassLoader]
    //   -> CALClassLoader.loadClass()
    //   -> ...
    //   -> CALClassLoader.getBytecodeForClassInternal()
    //   -> ...
    //   -> LECCModule.getFunction()
    //   -> Module.getFunction() [needs lock for the LECCModule (function name maps access)]
    private final int[] classLoaderLock = new int[0];
   
    /** The class loader associated with this module. */
    /* @GuardedBy("classLoaderLock") */
    private CALClassLoader classLoader;
   
    private ProgramResourceRepository resourceRepository;
   
    /** Whether class data should be retrieved from the repository. 
     * If false, class data will be dynamically generated.  If null, this value hasn't been calculated. */
    private Boolean shouldLookupClassData;

    /**
     * The instance of ClassNameMapper used for mapping an unqualified
     * function or type name to a name which is suitable for use in Java source
     * or the file system.
     */
    private final ClassNameMapper classNameMapper = new ClassNameMapper();
      
    private final Map<String, FunctionGroupInfo> functionNameToFunctionGroup = new HashMap<String, FunctionGroupInfo>();
   
    private boolean coarseGrouping = false;
   
    /** (String -> MachineFunction) the lifted functions created from
     * let variable definitions.*/
    private final Map<String, MachineFunction> liftedFunctionMap = new HashMap<String, MachineFunction>();
   

    /**
     * Constructor for a LECCModule.
     * Note that setGeneratedCodeInfo() should be called when this module has been compiled/loaded.
     *
     * @param moduleName
     * @param foreignClassLoader
     * @param resourceRepository
     */
    LECCModule (ModuleName moduleName, ClassLoader foreignClassLoader, ProgramResourceRepository resourceRepository) {
        super (moduleName, foreignClassLoader);
        this.resourceRepository = resourceRepository;
    }
   
    /**
     * Private constructor used by deserialization code.
     * @param moduleName
     * @param foreignClassLoader the classloader to use to resolve foreign classes for the module.
     * @param generatedCodeInfo The GeneratedCodeInfo for the persisted form of this module.
     */
    private LECCModule (ModuleName moduleName, ClassLoader foreignClassLoader, GeneratedCodeInfo generatedCodeInfo) {
        super (moduleName, foreignClassLoader, generatedCodeInfo);
    }
   
    /**
     * Build up a set of all the modules upon which this Module depends, both directly and indirectly,
     * including the current module.
     * @return the set of modules upon which this Module depends, including the current module.
     */
    private Set<LECCModule> getDependeeModuleSet() {
        Set<LECCModule> dependeeModuleSet = new HashSet<LECCModule>();
        buildDependeeModuleSet(getModuleTypeInfo(), new HashSet<ModuleName>(), dependeeModuleSet);
       
        return dependeeModuleSet;
    }
   
    /**
     * Build up a set of all the modules upon which this Module depends, both directly and indirectly.
     * @param currentModuleTypeInfo
     * @param moduleNames (Set of ModuleName) the set of module names already traversed (including the current one).
     * @param moduleSet
     */
    private void buildDependeeModuleSet (ModuleTypeInfo currentModuleTypeInfo, Set<ModuleName> moduleNames, Set<LECCModule> moduleSet) {
        // Check whether this module has already been traversed..
        if (!moduleNames.add(currentModuleTypeInfo.getModuleName())) {
            return;
        }
        moduleSet.add((LECCModule)currentModuleTypeInfo.getModule());
       
        // Call recursively on imported modules.
        for (int i = 0; i < currentModuleTypeInfo.getNImportedModules(); ++i) {
            ModuleTypeInfo importedModuleTypeInfo = currentModuleTypeInfo.getNthImportedModule(i);
            buildDependeeModuleSet (importedModuleTypeInfo, moduleNames, moduleSet);
        }
    }
   
    CALClassLoader getClassLoader() {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                if (resourceRepository == null) {
                    return null;
                }
                Set<LECCModule> dependeeModuleSet = getDependeeModuleSet();
                List<LECCModule> dependeeModuleList = new ArrayList<LECCModule>(dependeeModuleSet);

                // Use the CALClassLoader factory method to create a class loader instance.
                ProgramResourceLocator moduleFolderLocator = new ProgramResourceLocator.Folder(getName(), ResourcePath.EMPTY_PATH);
                classLoader = CALClassLoader.makeCALClassLoader(resourceRepository, moduleFolderLocator, getForeignClassLoader(), this, dependeeModuleList);
            }

            return classLoader;
        }
    }
   
   
    void resetCachedResults (ExecutionContext context) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return;
            }
            classLoader.resetCachedResults(context);
        }
    }
   
    /**
     * Discard the cached CAFs and the class loader for this module.
     * @param context
     */
    void resetMachineState(ExecutionContext context) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return;
            }
            classLoader.resetCachedResults(context);
            // Discard the class loader. This will unload all classes in the module and adjunct.
            classLoader = null;
        }
    }
   
    /**
     * Discard previously loaded classes for this module.
     * @param forAdjunct - if true indicates that only classes for the adjunct should be discarded.
     */
    void resetClassLoader (boolean forAdjunct) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return;
            }
            if (forAdjunct) {
                classLoader = CALClassLoader.resetAdjunctClasses(classLoader);
            } else {
                // Simply discard the class loader.  This will unload all classes in the module
                // and adjunct.
                classLoader = null;
            }
        }
    }
   
    /**
     * @param forAdjunct
     * @return the number of classes loaded by the associated class loader.
     */
    int getNClassesLoaded(boolean forAdjunct) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return 0;
            } else {
                return classLoader.getNClassesLoaded(forAdjunct);
            }
        }
    }
   
    /**
     * @param forAdjunct
     * @return the number of bytes loaded by the associated class loader.
     */
    int getNClassBytesLoaded(boolean forAdjunct) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return 0;
            } else {
                return classLoader.getNClassBytesLoaded(forAdjunct);
            }
        }
    }
   
    /**
     * @param forAdjunct
     * @return number of milliseconds spent in generating class file data by the associated class loader.
     */
    long getGenerateClassDataTimeMS(boolean forAdjunct) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return 0;
            } else {
                return classLoader.getGenerateClassDataTimeMS(forAdjunct);
            }
        }
    }
   
   
    /**
     * @param forAdjunct
     * @return number of milliseconds spent in looking up class file data by the associated class loader.
     */
    long getLookupClassDataTimeMS(boolean forAdjunct) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return 0;
            } else {
                return classLoader.getLookupClassDataTimeMS(forAdjunct);
            }
        }
    }
   
    /**
     * @param forAdjunct
     * @return number of milliseconds spent in findClass() by the associated class loader.
     */
    long getFindClassTimeMS(boolean forAdjunct) {
        synchronized (classLoaderLock) {
            if (classLoader == null) {
                return 0;
            } else {
                return classLoader.getFindClassTimeMS(forAdjunct);
            }
        }
    }
   
    /**
     * Write out this instance of LECCModule to the RecordOutputStream.
     * @param s
     * @throws IOException
     */
    @Override
    protected void writeActual (RecordOutputStream s) throws IOException {
        s.startRecord (ModuleSerializationTags.LECC_MODULE, serializationSchema);
        s.writeModuleName(getName());
        classNameMapper.write(s);
        super.writeContent (s);
        s.endRecord ();
    }
   
    /** {@inheritDoc} */
    @Override
    protected void writeMachineSpecificSerializationInfo(RecordOutputStream s) throws IOException {
        CodeGenerator.GeneratedCodeInfo codeInfo = CodeGenerator.getNewCodeInfo(this);
        codeInfo.write(s);
       
        // Update the module's GeneratedCodeInfo.
        setGeneratedCodeInfo(codeInfo);
    }
   
    /**
     * Read meta information about the serialized module for the lecc machine
     * @param s
     * @return GeneratedCodeInfo instance filled in from a SerializationInfo record
     *          read from s.
     * @throws IOException
     */
    static CodeGenerator.GeneratedCodeInfo readLeccSpecificSerializationInfo(RecordInputStream s) throws IOException {
        RecordInputStream.RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.SERIALIZATION_INFO);
        if (rhi == null) {
            throw new IOException ("Unable to find serialization info");
        }
       
        // Skip the non-machine-specific data
        s.readLong(); // timestamp
       
        // read our own part of the record
        CodeGenerator.GeneratedCodeInfo codeInfo = CodeGenerator.GeneratedCodeInfo.load(s);
       
        s.skipRestOfRecord();
        return codeInfo;
    }
   
    /**
     * Read the contents of the LECCModule from the RecordInputStream.
     * The read position will be after the initial members needed to
     * construct this LECCModule instance have been read.
     * @param s
     * @param schema
     * @param otherModules
     * @param msgLogger the logger to which to log deserialization messages.
     * @throws IOException
     */
    protected void readContent (RecordInputStream s, int schema, Map<ModuleName, Module> otherModules, CompilerMessageLogger msgLogger) throws IOException {
        classNameMapper.readContent(s);
       
        // Read the base class content.
        super.readContent(s, otherModules, msgLogger);
       
        DeserializationHelper.checkSerializationSchema(schema, serializationSchema, getName(), "LECCModule", msgLogger);
       
        // There's no more members specific to LECCModule to be read.
        // Always skip to the end of the record, we might be reading a record produced
        // by a later version of the code which has additional info in it.
        s.skipRestOfRecord();
    }
   
    /**
     * Read the generated code info from the RecordInputStream.
     * The read position will be after the members needed to construct the generated code info for this module.
     * @param s
     * @param schema
     * @return the generated code info for this module.
     * @throws IOException if there was a problem read the generated code info from this module.
     */
    public static GeneratedCodeInfo loadCodeInfo(RecordInputStream s, short schema)
    throws IOException {
       
        return CodeGenerator.GeneratedCodeInfo.load(s, schema);
    }
   
    /**
     * Load an instance of LECCModule from the RecordInputStream.
     * Assumes that the read position is after the LECCModule record header.
     * @param s
     * @param schema
     * @param otherModules
     * @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 LECCModule, or null if there was a problem reading the module.
     * @throws IOException if any internal errors occurred while loading the module.
     */
    public final static Module load (RecordInputStream s, int schema, Map<ModuleName, Module> otherModules, ClassLoader foreignClassLoader,
            org.openquark.cal.machine.GeneratedCodeInfo generatedCodeInfo, CompilerMessageLogger msgLogger) throws IOException {
       
        int nErrorsBeforeLoad = msgLogger.getNErrors();
       
        // Read in the name and create the module instance.
        ModuleName moduleName = s.readModuleName();
       
        LECCModule newModule = new LECCModule (moduleName, foreignClassLoader, generatedCodeInfo);
       
        // Instruct the module to load the rest of its contents.
        newModule.readContent(s, schema, otherModules, msgLogger);
       
        // If there were any problems reading the module, don't return the module.
        if (msgLogger.getNErrors() != nErrorsBeforeLoad) {
            return null;
        }
       
        return newModule;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean mustLoadExpressions() {
        // if we retrieve class data from the repository, we don't load expressions.
        return !shouldLookupClassData() || Packager.getOptimizerLevel() > 0;
    }
   
    /**
     * @return whether the class data should be retrieved from the repository.
     * If false, the class data should instead be dynamically generated on demand.
     *
     * This value is calculated for the classloader the first time this method is called.  Subsequent calls return the same value.
     */
    public boolean shouldLookupClassData() {

        if (shouldLookupClassData == null) {

            /*
             * Lookup class data from disk if:
             *   Static runtime, and GeneratedCodeInfo for the module is compatible with current settings.
             *   Dynamic runtime, .lc files are available, other GeneratedCodeInfo for the module is compatible with current settings, and module is in a car.
             *
             * Generate class data dynamically if:
             *   Static runtime, and GeneratedCodeInfo for the module is not compatible with current settings.
             *   Dynamic runtime, and one of:
             *     no car
             *     car with no .lc files (car module not static)
             *     car with .lc files, but GeneratedCodeInfo incompatible.
             */

            boolean boolValue;
            if (LECCMachineConfiguration.isLeccRuntimeStatic()) {
                // Static runtime
                // Lookup class data unless it's out of date.
                GeneratedCodeInfo gci = getGeneratedCodeInfo();
                boolValue = (gci == null) ? true : gci.isCompatibleWithCurrentConfiguration();
               
                if (!boolValue) {
                    // The generated code info indicates that the classes in the repository are incompatible with the current configuration.
                    // This should never happen for a static runtime.
                    throw new IllegalStateException("The existing runtime classes are not compatible with the current static configuration.");
                }

            } else {
                // Dynamic runtime.
                // Lookup class data if all of the following are true:
                //  1) Generated code info indicates that it's statically generated.
                //  2) Other Generated code info members are compatible with current settings.
                CodeGenerator.GeneratedCodeInfo generatedCodeInfo = (CodeGenerator.GeneratedCodeInfo)getGeneratedCodeInfo();

                boolValue = generatedCodeInfo != null &&
                    generatedCodeInfo.isStaticRuntime()
                    && generatedCodeInfo.isCompatibleWithCurrentConfiguration() ;
            }

            this.shouldLookupClassData = Boolean.valueOf(boolValue);
        }

        return shouldLookupClassData.booleanValue();
    }

    /**
     * @return the instance of ClassNameMapper used for mapping an unnqualified
     * function or type name to a name which is suitable for use in Java source
     * or the file system.
     */
    ClassNameMapper getClassNameMapper() {
        return classNameMapper;
    }
   
    /**
     * @return Returns whether the resource repoository is set.
     */
    boolean hasRepository() {
        return resourceRepository != null;
    }

    /**
     * @param resourceRepository The resource repository to set.
     */
    void setRepository(ProgramResourceRepository resourceRepository) {
        this.resourceRepository = resourceRepository;
    }
   
    void addLiftedLetVarFunction(MachineFunction mf, FunctionGroupInfo fgi) {
        liftedFunctionMap.put(mf.getName(), mf);
        functionNameToFunctionGroup.put(mf.getName(), fgi);
    }
   
    /**
     * Fetch a function by name.
     * @param functionName
     * @return the MachineFunction instance for the named function.  Null if it doesn't exist.    
     */
    @Override
    public MachineFunction getFunction (String functionName) {
        MachineFunction mf = super.getFunction(functionName);
       
        // Check to see if the function is a lifted let variable function.
        if (mf == null) {
            mf = liftedFunctionMap.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.
     */
    @Override
    public MachineFunction getFunction (QualifiedName functionName) {
        MachineFunction mf = super.getFunction(functionName);
       
        // Check to see if the function is a lifted let variable function.
        if (mf == null && functionName.getModuleName().equals(getName())) {
            mf = liftedFunctionMap.get(functionName.getUnqualifiedName());
        }

        return mf;
    }
   
    /**
     * 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
     */
    @Override
    protected MachineFunction loadMachineFunction(RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        // Load the record header and determine which actual class we are loading.
        RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.LECC_MACHINE_FUNCTION);
        if (rhi == null) {
            throw new IOException ("Unable to find record header for LECCMachineFunction.");
        }
       
        LECCMachineFunction mf = new LECCMachineFunction();
        mf.readContent(s, rhi.getSchema(), mti, msgLogger);
        s.skipRestOfRecord();
       
        return mf;
    }
   
    /**
     * Clear the entry in the functionNameToFunctionGroup map
     * associated with the provided name.
     * @param name
     */
    void clearFunctionGroupInfo (String name) {
        functionNameToFunctionGroup.remove(name);
    }
   
    /**
     * Associate a function group with a function.
     * @param functionName
     * @param fgi
     */
    void setFunctionGroupInfo (String functionName, FunctionGroupInfo fgi) {
        functionNameToFunctionGroup.put(functionName, fgi);
    }
   
    /**
     * Retrieve the function group info for the named function.
     * If the function is in a different module the call will
     * be passed to the appropriate module..
     * @param functionName
     * @return FunctionGroupInfo if successful, null otherwise.
     */
    FunctionGroupInfo getFunctionGroupInfo (QualifiedName functionName) {
        MachineFunction mf = getFunction(functionName);
        if (mf != null) {
            return getFunctionGroupInfo(mf);
        }
       
        if (functionName.getModuleName().equals(getName())) {
            return functionNameToFunctionGroup.get(functionName.getUnqualifiedName());
        }
        return null;
    }

   
    /**
     * Retrieve the function group info for the provided Function.
     * If the function is in a different module the call will
     * be passed to the appropriate module..
     * @param mf - MachineFunction of interest.
     * @return FunctionGroupInfo if successful, null otherwise.
     */
    FunctionGroupInfo getFunctionGroupInfo (MachineFunction mf) {
        if (mf.getQualifiedName().getModuleName().equals(getName())) {
           
            FunctionGroupInfo fgi = functionNameToFunctionGroup.get(mf.getName());
            if (fgi == null) {
                if (coarseGrouping) {
                   
                    if (mf.isPrimitiveFunction() ||
                        mf.isDataConstructor() ||
                        mf.isForAdjunct()) {

                        List<MachineFunction> machineFunctions = new ArrayList<MachineFunction>();
                        machineFunctions.add(mf);
                       
                        fgi = new FunctionGroupInfo(this, machineFunctions);
                    } else {
                        // Group the non-primitive functions into a single group.
                        
                        List<MachineFunction> machineFunctions = new ArrayList<MachineFunction>();
                       
                        for (final MachineFunction mf2 : getFunctions()) {
                           
                            if (mf2.isDataConstructor()) {
                                continue;
                            }
   
                            if (mf2.getAliasOf() != null || mf2.getLiteralValue() != null) {
                                continue;
                            }
                           
                            // Check to see if this is a primitive function.
                            // Functions marked as primitive can be one of two things:
                            // 1) a primitive function implemented as a machine specific operator
                            // 2) a primitive function for which a hard coded machine specific implementation is provided.
                            // If the function falls into category two we don't want to generate anything for it.
                            if (mf2.isPrimitiveFunction()) {
                                continue;
                            }
                           
                            if (mf.isForAdjunct() != mf2.isForAdjunct()) {
                                continue;
                            }
                           
                            machineFunctions.add(mf2);
                        }
                       
                        fgi = new LECCModule.FunctionGroupInfo(this, machineFunctions);
                        for (final MachineFunction mf2 : machineFunctions) {                          
                            functionNameToFunctionGroup.put(mf2.getName(), fgi);
                        }
                    }
                } else {
                    List<MachineFunction> machineFunctions = new ArrayList<MachineFunction>();
                    Set<String> connectedComponents = mf.getStronglyConnectedComponents();
                    for (final String functionName : connectedComponents) {
                        MachineFunction mf2 = getFunction(functionName);
                        machineFunctions.add(mf2);
                    }
                   
                    fgi = new LECCModule.FunctionGroupInfo(this, machineFunctions);
                    for (final MachineFunction mf2 : machineFunctions) {                      
                        functionNameToFunctionGroup.put(mf2.getName(), fgi);
                    }
                }
            }
           
            return fgi;
        } else {
            LECCModule m = (LECCModule)findModule (mf.getQualifiedName().getModuleName());
            if (m != null) {
                return m.getFunctionGroupInfo(mf);
            }
        }

        // No label or other horridness
        return null;

    }

    /**
     * Class used to provide information about grouped functions.
     * Functions are grouped for optimal generation of java code.
     * Minimally, closely connected functions will always be in
     * a function group.  Beyond that clients can construct various
     * grouping scenarios based on function usage, etc.
     * @author rcypher
     *
     */
    static class FunctionGroupInfo {
        /** (String -> MachineFunction) the functions in this group. */
        private final Map<String, MachineFunction> machineFunctions = new LinkedHashMap<String, MachineFunction>();

        /** (String -> MachineFunction) the lifted letvar definition functions in this group. */
        private final Map<String, MachineFunction> liftedLetVarMachineFunctions = new LinkedHashMap<String, MachineFunction>();
       
        /** The module containing this function group. */
        private final LECCModule module;
       
        /** Number of CAFs in ths group. */
        private int nCAFs = 0;
       
        /** Number of zero arity functions which are not CAFs. */
        private int nZeroArityFunctions = 0;
       
        /** (Integer -> Integer) function arity to number of
         * functions in the group with that arity. */
        private Map<Integer, Integer> arityToCount = new HashMap<Integer, Integer>();
       
        /** (String -> Integer) Map of function name to ordinal within the group. */
        private Map<String, Integer> nameToOrdinal = new HashMap<String, Integer>();

        /** (Integer -> String) Map of function ordinal within group to name. */
        private Map<Integer, String> ordinalToName = new HashMap<Integer, String>();
       
        /** Name of this function group.  Used to name the corresponding java class. */
        private String functionGroupName = null;
       
        /** Qualified name of this function group. Used to name the corresponding java class. */
        private QualifiedName functionGroupQualifiedName = null;
       
        /** String -> (Set of String).  Function name to let variable
         * functions lifted from the named function.
         */
        private final Map<String, Set<String>> functionNameToLiftedFunctions = new HashMap<String, Set<String>>();
       
        private FunctionGroupInfo (LECCModule module, List<MachineFunction> machineFunctions) {
            Collections.sort(machineFunctions, new MachineFunctionComparator());
            for (final MachineFunction mf : machineFunctions) {              
                addFunction(mf);
            }
           
            this.module = module;
        }
       
        boolean contains (String functionName) {
            return machineFunctions.get(functionName) != null ||
                   liftedLetVarMachineFunctions.get(functionName) != null;
        }

        /**
         * Add a lifted let variable definition function.
         * @param letVarFunction
         * @param originatingFunction
         */
        void addLiftedLetVarFunction (MachineFunction letVarFunction, String originatingFunction) {
            this.liftedLetVarMachineFunctions.put(letVarFunction.getName(), letVarFunction);
            Set<String> cl = this.functionNameToLiftedFunctions.get(originatingFunction);
            if (cl == null) {
                cl = new HashSet<String>();
                this.functionNameToLiftedFunctions.put(originatingFunction, cl);
            }
            cl.add(letVarFunction.getName());
        }
       
        /**
         * @param originatingFunction
         * @return Set of String or null
         */
        Set<String> getLiftedFunctionsFor(String originatingFunction) {
            return this.functionNameToLiftedFunctions.get(originatingFunction);
        }
       
        private void addFunction (MachineFunction mf) {
            this.machineFunctions.put(mf.getName(), mf);
            if (mf.isCAF()) {
                nCAFs++;
            } else
                if (mf.getArity() == 0) {
                    nZeroArityFunctions++;
                }

            Integer arity = Integer.valueOf(mf.getArity());
            Integer count = arityToCount.get(arity);
            if (count == null) {
                count = Integer.valueOf(1);
            } else {
                count = Integer.valueOf(count.intValue()+1);
            }
            arityToCount.put(arity, count);
           
            Integer index = Integer.valueOf(machineFunctions.size()-1);
           
            nameToOrdinal.put(mf.getName(), index);
            ordinalToName.put(index, mf.getName());
           
            if (index.intValue() == 0) {
                functionGroupName = mf.getName();
                functionGroupQualifiedName = mf.getQualifiedName();
            }
        }
       
        int getNFunctions() {
            return machineFunctions.size();
        }
       
        boolean includesCAFs () {
           return nCAFs > 0;
        }
       
        boolean includesZeroArityFunctions () {
            return nZeroArityFunctions > 0;
        }
       
        void setCodeGenerated (boolean b) {
            for (final MachineFunction machineFunction : machineFunctions.values()) {
                MachineFunctionImpl mf = (MachineFunctionImpl)machineFunction;
                mf.setCodeGenerated(b);
            }           
        }
       
        /**
         * @return Collection of MachineFunction
         */
        Collection<MachineFunction> getTopLevelCALFunctions () {
            return Collections.unmodifiableCollection(machineFunctions.values());
        }
       
        /**
         * @return Collection of MachineFunction
         */
        Collection<MachineFunction> getLiftedLetVarDefFunctions () {
            return Collections.unmodifiableCollection(liftedLetVarMachineFunctions.values());
        }
       
        MachineFunction getMachineFunction (String name) {
            MachineFunction mf =  machineFunctions.get(name);
            if (mf == null) {
                mf = liftedLetVarMachineFunctions.get(name);
            }
            return mf;
        }
       
        String getFunctionGroupName() {
            return functionGroupName;
        }
       
        QualifiedName getFunctionGroupQualifiedName() {
            return functionGroupQualifiedName;
        }
       
        /**
         * If this class represents just one CAL function we can just name the
         * function 'f...'. Otherwise we need to differentiate between the 'f'
         * methods for the different CAL functions by prefixing the CAL function
         * name. If this class represents more than one CAL function a 'f'
         * method will be generated which will switch to the appropriate ..._f
         * method based on the SC tag.
         */
        String getFNamePrefix(String scName) {

            if (machineFunctions.size() > 1) {
                return CALToJavaNames.cleanSCName(scName) + "_";
            }
            return "";
        }
       
        String getFnNamePrefix (String scName) {
            return getFNamePrefix(scName);  
        }
       
       
        /**        
         * @return function arity to number of functions in the group with that arity.
         */
        Map<Integer, Integer> getArityToCountMap () {
            return arityToCount;
        }
       
        int getNCAFs () {return nCAFs;}
        int getNZeroArityFunctions ()  {return nZeroArityFunctions;}
       
        int getFunctionIndex (String scName) {
            Integer index = nameToOrdinal.get(scName);
            if (index == null) {
                throw new NullPointerException("Invalid SC name " + scName + " in FunctionGroupInfo.getFunctionIndex().");
            }
           
            return index.intValue();
        }
       
        String getFunctionNameFromIndex (int index) {
            String name = ordinalToName.get(Integer.valueOf(index));
            if (name == null) {
                throw new NullPointerException("Invalid SC ordinal " + index + " in FunctionGroupInfo.getFunctionIndex().");
            }
           
            return name;
        }
       
        LECCModule getModule ()  {
            return module;
        }
       
        @Override
        public String toString () {
            StringBuilder sb = new StringBuilder();
            sb.append(getFunctionGroupName());
            sb.append(": ");
            for (final String functionName : machineFunctions.keySet()) {
                sb.append(functionName);
                sb.append(", ");
            }
            sb.append ("nCafs = " + nCAFs);
            return sb.toString();
        }
       
        private static class MachineFunctionComparator implements Comparator<MachineFunction> {
            /** {@inheritDoc}*/
            public int compare (MachineFunction mf1, MachineFunction mf2) {
                int compare = mf1.getArity() - mf2.getArity();
                if (compare == 0) {
                    compare = (mf1).getName().compareTo((mf2).getName());
                }
                return compare;
            }
                     
            public boolean equals (Object o1, Object o2) {
                if (((MachineFunction)o1).getArity() == ((MachineFunction)o2).getArity()) {
                    return ((MachineFunction)o1).getName().equals(((MachineFunction)o2).getName());
                }
                return false;
            }           
        }
    }

    /**
     * Encapsulates the one-to-one mapping from an unqualified function or type
     * name to a name which is suitable for use in Java source or the file
     * system.
     *
     * Note that this class is thread-safe, with the exception that to atomically
     * query the existing mapping and then add a new mapping, the instance
     * must be explicitly synchronized, surrounding both the
     * {@link LECCModule.ClassNameMapper#getOriginalName}/{@link LECCModule.ClassNameMapper#getFixedName}
     * call and the {@link LECCModule.ClassNameMapper#addMapping} call.
     *
     * @author Joseph Wong
     * @author Edward Lam, Raymond Cypher (original code in CALToJavaNames)
     */
    static final class ClassNameMapper {
       
        /**
         * A Map<String, String> mapping original names to their corresponding fixed names.
         * This map is kept in sync with {@link LECCModule.ClassNameMapper#fixedNamesToOriginalNames}.
         */
        private final Map<String, String> originalNamesToFixedNames = new HashMap<String, String>();
       
        /**
         * A Map<String, String> mapping fixed names to their corresponding original names.
         * This map is kept in sync with {@link LECCModule.ClassNameMapper#originalNamesToFixedNames}.
         */
        private final Map<String, String> fixedNamesToOriginalNames = new HashMap<String, String>();
       
        /**
         * Returns the fixed name corresponding to the given original name.
         * @param originalName
         * @return the fixed name corresponding to originalName.
         */
        synchronized String getFixedName(String originalName) {
            return originalNamesToFixedNames.get(originalName);
        }
       
        /**
         * Returns the original name corresponding to the given fixed name.
         * @param fixedName
         * @return the original name corresponding to fixedName.
         */
        synchronized String getOriginalName(String fixedName) {
            return fixedNamesToOriginalNames.get(fixedName);
        }
       
        /**
         * Adds an original name to fixed name mapping. Note that to atomically query the mapping
         * and add a new one as necessary, one needs to synchronize on the instance of {@link LECCModule.ClassNameMapper}
         * explicitly.
         *
         * @param originalName
         * @param fixedName
         */
        synchronized void addMapping(String originalName, String fixedName) {
            originalNamesToFixedNames.put(originalName, fixedName);
            fixedNamesToOriginalNames.put(fixedName, originalName);
        }
       
        /**
         * Writes out this instance to the RecordOutputStream.
         * @param s the RecordOutputStream.
         * @throws IOException
         */
        private synchronized void write(RecordOutputStream s) throws IOException {
            s.writeIntCompressed(originalNamesToFixedNames.size());
            for (final Map.Entry<String, String> entry : originalNamesToFixedNames.entrySet()) {              
                s.writeUTF(entry.getKey());
                s.writeUTF(entry.getValue());
            }
        }
       
        /**
         * Replaces the state of this instance with the state read from the RecordInputStream.
         * @param s the RecordInputStream.
         * @throws IOException
         */
        private synchronized void readContent(RecordInputStream s) throws IOException {
            originalNamesToFixedNames.clear();
            fixedNamesToOriginalNames.clear();
           
            int nEntries = s.readIntCompressed();
            for (int i = 0; i < nEntries; i++) {
                String originalName = s.readUTF();
                String fixedName = s.readUTF();
                originalNamesToFixedNames.put(originalName, fixedName);
                fixedNamesToOriginalNames.put(fixedName, originalName);
            }
        }
    }


}
TOP

Related Classes of org.openquark.cal.internal.machine.lecc.LECCModule$ClassNameMapper

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.