/* * 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); } } } }