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

Source Code of org.openquark.cal.internal.machine.lecc.LECCJavaSourceGenerator

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


/*
* LECCJavaSourceGenerator.java
* Creation date: Oct 18, 2006.
* By: Edward Lam
*/
package org.openquark.cal.internal.machine.lecc;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.TypeConstructor;
import org.openquark.cal.internal.javamodel.BytecodeDebuggingUtilities;
import org.openquark.cal.internal.javamodel.JavaClassRep;
import org.openquark.cal.internal.javamodel.JavaGenerationException;
import org.openquark.cal.internal.javamodel.JavaSourceGenerator;
import org.openquark.cal.internal.javamodel.JavaStatement.JavaDocComment;
import org.openquark.cal.internal.machine.CodeGenerationException;
import org.openquark.cal.internal.machine.lecc.LECCModule.FunctionGroupInfo;
import org.openquark.cal.internal.runtime.lecc.LECCMachineConfiguration;
import org.openquark.cal.machine.ProgramResourceLocator;
import org.openquark.cal.machine.ProgramResourceRepository;
import org.openquark.cal.machine.StatusListener;
import org.openquark.cal.services.ResourcePath;
import org.openquark.util.FileSystemHelper;
import org.openquark.util.TextEncodingUtilities;


/**
* @author Edward Lam, Raymond Cypher
*/
public class LECCJavaSourceGenerator extends JavaGenerator {

    /**
     * Calls a helper method which performs various checks on the generated bytecode (i.e. the bytecode produced by
     * javac from compiling the generated java source). This is mostly of use when comparing the bytecode produced
     * by javac with the bytecode produced by our direct bytecode generators.
     * Individual tests and debug output can be turned on by setting the boolean variables in the debugGeneratedBytecode method below.
     * Note that these tests slow down bytecode generation considerably.
     */
    private static final boolean DEBUG_GENERATED_BYTECODE = false;   
   
    /** The method object used to invoke the javac compiler on the generated sources. */
    private static Method compileMethod;
    static {
        // Initialize the compileMethod field.

        // First we need to get the compiler class.  i.e. com.sun.tools.javac.Main
        Class<?> compilerClass = null;
       
        // Start by trying to get the class from the current class loader.
        try {
            compilerClass = JavaSourceGenerator.class.getClassLoader().loadClass ("com.sun.tools.javac.Main");
        } catch (ClassNotFoundException e) {
            // The class is not on the current classpath.  Try to locate tools.jar based on the jre home
            // directory and use a URLClassLoader.
            String homeDirectory = System.getProperty("java.home");
            if (homeDirectory != null) {
                // In the standard JDC file layout tools.jar will be in the lib directory that is at the
                // same level as the JRE home directory.
               
                String fileSeparator = System.getProperty("file.separator", "\\");
                // Trim the 'jre' off the home directory.
                homeDirectory = homeDirectory.substring(0, homeDirectory.lastIndexOf(fileSeparator));
               
                // Create the file for tools.jar and check if it exists.
                File toolsFile = new File(homeDirectory + fileSeparator + "lib" + fileSeparator + "tools.jar");
                if (FileSystemHelper.fileExists(toolsFile)) {
                    try {
                        // Since we've located tools.jar create a URLClassLoader with tools.jar as its path and
                        // attempt to load the Main class.
                        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{toolsFile.toURL()}, JavaSourceGenerator.class.getClassLoader());
                        compilerClass = urlClassLoader.loadClass("com.sun.tools.javac.Main");
                    } catch (MalformedURLException e2) {
                        // Simply fall through leaving compilerClass as null.
                    } catch (ClassNotFoundException e2) {
                        // Simply fall through leaving compilerClass as null.
                    }
                }
            }
        }

        if (compilerClass != null) {
            try {
                // First we try to get the version of compile that takes a PrintWriter to handle output messages.
                LECCJavaSourceGenerator.compileMethod = compilerClass.getMethod("compile", new Class[]{(new String[]{}).getClass(), PrintWriter.class});
            } catch (NoSuchMethodException e) {
                  // Simply fall through and leave compileMethod as null.
            }
        }
    }
   
    /**
     * If javac returns an error when trying to compile generated sources for a module we want to delete all
     * generated files. Otherwise we can get into a state where we assume that an existing java source and
     * the corresponding class file are synchronized when they're actually not.
     * This flag allows the behaviour to be turned off for debugging purposes.  i.e. to examine the generated
     * source that failed to compile.
     */
    private static final boolean DELETE_GENERATED_SOURCES_ON_COMPILATION_ERROR = true;
   
    private static final String EOL = System.getProperty("line.separator");


    private final List<ProgramResourceLocator.File> filesToCompile = new ArrayList<ProgramResourceLocator.File>();
   
    /** The folder in which the java resources for the module should exist. */
    private final ProgramResourceLocator.Folder moduleFolder;
   
    /** The repository for program resources. */
    private final ProgramResourceRepository resourceRepository;
   
    /** (Set of ModuleName) The names of dependee modules (including the current module) in the program. 
     *   This is needed for setting the path used by the java compiler. */
    private final Set<ModuleName> dependeeModuleNames;
   
    private final LECCModule module;
   
    /**
     * Emit an indent.
     * @param sb the StringBuilder to which to add an indent.
     * @param indent the number of indents to add.
     * @return StringBuilder sb, returned for convenience.
     */
    private static StringBuilder emitIndent(StringBuilder sb, int indent) {
        // NOTE: we use the tab character '\t' instead of adding spaces
        // because of memory issues. 
        // When using spaces instead of '\t' some of our generated java source
        // files were large enough to cause out-of-memory errors.
        for (int i = 0; i < indent; i++) {
            sb.append('\t');
        }
        return sb;
    }

    /**
     * Emit an indent, some text, and an EOL.
     * @param sb the StringBuilder to which to add an indent.
     * @param text the text to add.
     * @param indent the number of indents to add.
     */
    private static void emitLine(StringBuilder sb, int indent,String text) {
        emitIndent(sb, indent);
        sb.append(text + EOL);
    }
   
   
    /**
     * Constructor for an LECCJavaSourceGenerator
     * @param module The module for which java classes will be generated.
     * @param resourceRepository The repository for program resources.
     * @param dependeeModuleNames - Set of String. The names of dependee modules, including the current module.
     * @throws IOException if there was a problem creating the repository folder where the source generation files will exist.
     */
    LECCJavaSourceGenerator(LECCModule module, ProgramResourceRepository resourceRepository, Set<ModuleName> dependeeModuleNames) throws IOException {
        this.resourceRepository = resourceRepository;
        if (module == null || dependeeModuleNames == null) {
            throw new IllegalArgumentException ("Unable to create JavaSourceGenerator: null argument.");
        }
        this.module = module;
        this.dependeeModuleNames = dependeeModuleNames;

        this.moduleFolder = CodeGenerator.getModuleResourceFolder(module.getName());
       
        // Ensure the module repository folder exists.
        resourceRepository.ensureFolderExists(moduleFolder);
    }
   
    /** {@inheritDoc} */
    @Override
    void createFunction(FunctionGroupInfo functionGroupInfo, boolean forceWrite, CompilerMessageLogger logger)
            throws CodeGenerationException {
       
        String className = CALToJavaNames.createClassNameFromSC(functionGroupInfo.getFunctionGroupQualifiedName(), module);
        ProgramResourceLocator.File sourceFile = moduleFolder.extendFile(className + ".java");
        ProgramResourceLocator.File classFile = moduleFolder.extendFile(className + ".class");

        //System.out.println(functionGroupInfo.getFunctionGroupName());
       
        boolean sourceExists = resourceRepository.exists(sourceFile);
        boolean fileChange = false;
        if (forceWrite || !sourceExists) {
           
            // Get the sc definition, generate source.
            JavaClassRep classRep = JavaDefinitionBuilder.getSCDefinition(functionGroupInfo, module, getCodeGenerationStats());
            if (classRep.getJavaDoc() == null) {
                classRep.setJavaDoc(new JavaDocComment(getClassJavadocComment()));
            }
           
            try {
                String source =
                    JavaSourceGenerator.generateSourceCode(classRep);
               
                if (!sourceExists) {
                    writeToSourceFile (source, sourceFile);
                    filesToCompile.add(sourceFile);
                    //System.out.println ("adding to compile: " + sourceFile.getName());
                    fileChange = true;
                } else
                if (sourceChanged (source, sourceFile)) {
                    writeToSourceFile (source, sourceFile);
                    filesToCompile.add(sourceFile);
                    //System.out.println ("adding to compile: " + sourceFile.getName());
                    fileChange = true;
                } else
                if (!resourceRepository.exists(classFile)) {
                    filesToCompile.add(sourceFile);
                    //System.out.println ("adding to compile: " + sourceFile.getName());
                    fileChange = true;
                }
            } catch (JavaGenerationException e) {
                throw new CodeGenerationException(e.getLocalizedMessage(), e);
            }
        } else {
            if (!resourceRepository.exists(classFile)) {
                filesToCompile.add(sourceFile);
                //System.out.println ("adding to compile: " + sourceFile.getName());
                fileChange = true;
            }
        }
       
        informStatusListeners(fileChange ? StatusListener.SM_ENTITY_GENERATED_FILE_WRITTEN : StatusListener.SM_ENTITY_GENERATED, functionGroupInfo.getFunctionGroupName());
    }

    /** {@inheritDoc} */
    @Override
    void createTypeDefinition(TypeConstructor typeCons,
            boolean forceWrite, CompilerMessageLogger logger) throws CodeGenerationException {

        String javaTypeConsName = CALToJavaNames.createClassNameFromType(typeCons, module);
        ProgramResourceLocator.File sourceFile = moduleFolder.extendFile(javaTypeConsName + ".java");
        ProgramResourceLocator.File classFile = moduleFolder.extendFile(javaTypeConsName + ".class");
          
        boolean sourceExists = resourceRepository.exists(sourceFile);
        boolean fileChange = false;
        if (forceWrite || !sourceExists) {
            // Get the sc definition, generate source.
            JavaClassRep classRep = JavaDefinitionBuilder.getDataTypeDefinition(typeCons, module, getCodeGenerationStats());
            if (classRep.getJavaDoc() == null) {
                classRep.setJavaDoc(new JavaDocComment(getClassJavadocComment()));
            }

            try {
                String source =
                    JavaSourceGenerator.generateSourceCode(classRep);
               
                if (!sourceExists) {
                    writeToSourceFile (source, sourceFile);
                    //System.out.println ("adding to compile: " + sourceFile.getName());
                    filesToCompile.add(sourceFile);
                    fileChange = true;
                } else
                if (sourceChanged (source, sourceFile)) {
                    writeToSourceFile (source, sourceFile);
                    //System.out.println ("adding to compile: " + sourceFile.getName());
                    filesToCompile.add(sourceFile);
                    fileChange = true;
                } else
                if (!resourceRepository.exists(classFile)) {
                    //System.out.println ("adding to compile: " + sourceFile.getName());
                    filesToCompile.add(sourceFile);
                    fileChange = true;
                }
            } catch (JavaGenerationException e) {
                throw new CodeGenerationException(e.getLocalizedMessage(), e);
            }
        } else {
            if (!resourceRepository.exists(classFile)) {
                //System.out.println ("adding to compile: " + sourceFile.getName());
                filesToCompile.add(sourceFile);
                fileChange = true;
            }
        }
       
        informStatusListeners(fileChange ? StatusListener.SM_ENTITY_GENERATED_FILE_WRITTEN : StatusListener.SM_ENTITY_GENERATED, typeCons.getName().getUnqualifiedName());
    }

    /** {@inheritDoc} */
    @Override
    void wrap() throws CodeGenerationException {

        if (compileMethod == null) {
            throw new CodeGenerationException ("Error compiling generated source code: Unable to locate or access class com.sun.tools.javac.Main.  Generating Java source requires an installed JDK.  Please ensure that tools.jar is on the classpath.");
        }
       
        if (filesToCompile.isEmpty()) {
            return;
        }

      
        try {
            // Let any status listeners know that we're starting the compilation of java sources for this module.
            informStatusListeners(StatusListener.SM_START_COMPILING_GENERATED_SOURCE, module.getName());
           
            // Set up the classpath for compiling this module.
            String fileNames[] = new String [filesToCompile.size()];
            {
                int index = 0;
                for (final ProgramResourceLocator.File fileToCompile : filesToCompile) {
                   
                    File file = resourceRepository.getFile(fileToCompile);
                    if (file == null) {
                        // the fileToCompile refers to a resource that is not backed by the file system (e.g. in a Car)
                        // We do not support Java source generation for modules whose source comes from Car files.
                        throw new CodeGenerationException ("Error compiling generated source code: The location of the source file " + fileToCompile + " does not have a corresponding java.io.File representation (e.g. the module comes from a Car).");
                    }
                   
                    fileNames[index] = file.getPath();
                    index++;
                }
            }
           
            String currentPath = System.getProperty("java.class.path", ".");
            String pathSeparator = System.getProperty ("path.separator", ";");
            String rootPackagePathSegment = LECCMachineConfiguration.ROOT_PACKAGE.replace('.', File.separatorChar);
           
            Set<String> pathAdditions = new HashSet<String>();
            for (final ModuleName moduleName : dependeeModuleNames) {
                // Get module, add its base folder directory to the classpath.
                ProgramResourceLocator.Folder moduleResourceFolder = new ProgramResourceLocator.Folder(moduleName, ResourcePath.EMPTY_PATH);
                File moduleDir = resourceRepository.getFile(moduleResourceFolder);
                if (moduleDir == null) {
                    // the fileToCompile refers to a resource that is not backed by the file system (e.g. in a Car)
                    // We do not support Java source generation for modules whose source comes from Car files.
                    throw new CodeGenerationException ("Error compiling generated source code: The location of the module directory " + moduleResourceFolder + " does not have a corresponding java.io.File representation (e.g. the module comes from a Car).");
                }
               
                String path = moduleDir.getAbsolutePath();
                int index = path.indexOf(rootPackagePathSegment);
                if (index >= 0) {
                    path = path.substring(0, index);
                }
                pathAdditions.add(path);
            }

            for (final String pathAddition : pathAdditions) {
                currentPath += pathSeparator + pathAddition;
            }
           
            // Set up the classpath arguments for the call to 'compile'.
            String args[] = new String [fileNames.length + 6];
            args[0] = "-target";
            args[1] = "1.5";
            args[2] = "-source";
            args[3] = "1.5";
            args[4] = "-classpath";
            args[5] = currentPath;
            System.arraycopy(fileNames, 0, args, 6, fileNames.length);
           
            // Set up a PrintWriter that will write any status messages from the compiler into a string buffer.
            FixedSizeStringWriter sw = new FixedSizeStringWriter(1000);
            PrintWriter pw = new  PrintWriter(sw);
           
            // Invoke the compiler.
            Object compileResult = compileMethod.invoke(null, new Object[]{args, pw});
           
            // Close/flush the PrintWriter.
            pw.close();
           
            // Extract the integer return code from the object result of 'invoke'.
            int returnCode = ((Integer)compileResult).intValue();
           
            if (returnCode != 0) {
                if (DELETE_GENERATED_SOURCES_ON_COMPILATION_ERROR) {
                    // We need to clean out the generated source target directory.  Otherwise we can get into a state where
                    // we assume that an existing java source and the corresponding class file are synchronized when they're
                    // actually not.
                    try {
                        resourceRepository.delete(moduleFolder);
                        // TODO: actually handle this.
                    } catch (IOException ioe) {
                        // There was a problem deleting one or more files.
                        // This used to be just ignored.
                        System.err.println("Problem deleting one or more files: " + ioe);
                    }
                }
               
                // Construct an error message that includes any messages from the javac compiler.
                throw new CodeGenerationException ("Error compiling generated source for module " + module.getName() + ".\n" + sw.toString());
            }

            if (DEBUG_GENERATED_BYTECODE) {
                debugGeneratedBytecode();
            }

        } catch (IllegalAccessException e) {
            throw new CodeGenerationException ("Error compiling generated source code: IllegalAccessException thrown trying to access com.sun.tools.javac.Main.compile.");
        } catch (InvocationTargetException e) {
            throw new CodeGenerationException ("Error compiling generated source code: InvocationTargetException thrown trying to access com.sun.tools.javac.Main.compile. " + e.getLocalizedMessage());
        } finally {
            // Inform any status listeners that compilation of java source for this module is ended.
            informStatusListeners(StatusListener.SM_END_COMPILING_GENERATED_SOURCE, module.getName());
        }
    }

    /**
     * Get a string to represent the javadoc comment for the generated class.
     * @return the javadoc comment.
     */
    private String getClassJavadocComment() {
        File file = resourceRepository.getFile(moduleFolder);
        String fileName;
        if (file != null) {
            fileName = file.toString().replaceAll("\\\\", "/");
        } else {
            fileName = moduleFolder.toString();
        }
        StringBuilder sb = new StringBuilder();
        emitLine(sb, 0, "/**");
        emitLine(sb, 0, " * " + fileName);
        emitLine(sb, 0, " * from CAL module \"" + module.getName() + "\"");
        emitLine(sb, 0, " * created at " + new Date());
        emitLine(sb, 0, " * " + module.getNFunctions() + " CAL symbols defined (supercombinators and constructors) in this module");
        emitLine(sb, 0, " *");
        emitLine(sb, 0, " * This Java source has been automatically generated by the Business Objects CAL Compiler");
        emitLine(sb, 0, " * MODIFICATIONS TO THIS SOURCE MAY BE OVERWRITTEN - DO NOT MODIFY THIS FILE");
        emitLine(sb, 0, " */");
        return sb.toString();
    }
       
    /**
     * Write the given source into the given file.
     * @param source - String.  The generated source.
     * @param targetFile - File.  The target file.
     * @throws CodeGenerationException
     */   
    private void writeToSourceFile(String source, ProgramResourceLocator.File targetFilethrows CodeGenerationException {
        //System.out.println ("writing source file: " + file.getName());
        try {
            resourceRepository.setContents(targetFile, new ByteArrayInputStream(TextEncodingUtilities.getUTF8Bytes(source)));
       
        } catch (FileNotFoundException e) {
            throw new CodeGenerationException ("Unable to find file: " + targetFile.toString());
       
        } catch (IOException e) {
            throw new CodeGenerationException ("Error writing to file: " + targetFile.toString());
        }
    }
   
    /**
     * Determine whether the source code for a given file has changed in a meaningful way.
     * @param newSource the new source code for the give file.
     * @param sourceFile the source file.
     * @return boolean whether the source code has changed.
     */
    private boolean sourceChanged(String newSource, ProgramResourceLocator.File sourceFile) {
        if (!resourceRepository.exists(sourceFile)) {
            return true;
        }
       
        InputStream sourceInputStream = null;
        try {
            sourceInputStream = resourceRepository.getContents(sourceFile);
            Reader frOld = TextEncodingUtilities.makeUTF8Reader(sourceInputStream);

            BufferedReader brOld = new BufferedReader (frOld);
            BufferedReader brNew = new BufferedReader (new StringReader (newSource));
            boolean difference = false;
            while (!difference) {
                String sOld = nextLineOfInterest(brOld);
                String sNew = nextLineOfInterest(brNew);
               
                if ((sOld == null && sNew != null) || (sNew == null && sOld != null)) {
                    return true;
                }
               
                if (sOld == null) {
                    break;
                }
               
                if (!sOld.equals (sNew)) {
                    return true;
                }
            }
            brOld.close();
            frOld.close();
       
        } catch (IOException e) {
            return true;
       
        } finally {
            if (sourceInputStream != null) {
                try {
                    sourceInputStream.close();
                } catch (IOException e) {
                }
            }
        }
       
        return false;
    }
   
    /**
     * Helper method for sourceChanged().
     *   Get the next line which isn't a comment.
     * @param br the reader from which to get the line.
     * @return the next line, or null if there is no next line.
     * @throws IOException
     */
    private String nextLineOfInterest (BufferedReader br) throws IOException {
        String line = null;
        boolean readAgain = true;
        boolean inComment = false;
       
        while (readAgain) {
            readAgain = false;
            line = br.readLine ();
            if (line == null) {
                return null;
            }
           
            line = line.trim();
            if (inComment) {
                readAgain = true;
                if (line.endsWith("*/")) {
                    inComment = false;
                }
            } else
            if (line.startsWith("/*")) {
                inComment = true;
                readAgain = true;
            } else
            if (line.equals("")) {
                readAgain = true;
            } else
            if (line.startsWith("//")) {
                readAgain = true;
            }
        }
       
        return line;
    }

    /**
     * Called if the static flag JavaSourceGenerator.DEBUG_GENERATED_BYTECODE is set.
     *
     * Performs various checks on the generated bytecode. Most notable is a byte code verifier.
     * Individual tests and debug output can be turned on by setting the boolean variables in the
     * method body below. Note that these tests slow down bytecode generation considerably.
     *
     * Note: path names below are Windows only and you'll have to adjust according to your own machine.
     * This is for debug purposes only.
     *
     * In this case, the generated bytecode is produced by javac compiling the java source code generated
     * by the JavaSourceGenerator. Thus, the bytecode is bound to be "correct"! The purpose here is mainly
     * to allow comparison between the bytecode generated by javac and the bytecode generated by our
     * direct-to-bytecode generators such as the ASM generator.
     *   
     */
    private void debugGeneratedBytecode() {
       
        ProgramResourceLocator[] members = resourceRepository.getMembers(moduleFolder);
       
        for (int fileN = 0, nFiles = members.length; fileN < nFiles; ++fileN) {
           
            ProgramResourceLocator member = members[fileN];
            String name = member.getName();
           
            // Skip anything which isn't a class file.
            if (!(member instanceof ProgramResourceLocator.File) || !name.toLowerCase().endsWith(".class")) {
                continue;
            }
            ProgramResourceLocator.File classFileLocator = (ProgramResourceLocator.File)member;
           
            byte[] bytecode;
            InputStream classFileContents = null;
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                classFileContents = resourceRepository.getContents(classFileLocator);
                byte[] buf = new byte[4096];
               
                while (true) {
                    int bytesRead = classFileContents.read(buf);
                    if (bytesRead < 0) {
                        break;
                    }
                   
                    baos.write(buf, 0, bytesRead);
                }
               
                bytecode = baos.toByteArray();
               
            } catch (IOException ioe) {
                throw new RuntimeException("Could not read the class file " + member.getName() + ".");
               
            } finally {
                if (classFileContents != null) {
                    try {
                        classFileContents.close();
                    } catch (IOException e) {
                    }
                }
            }
                            
            final boolean dumpAsmifiedText = true;
            final boolean dumpDisassembledText = true;
            final boolean verifyClassFileFormat = true;           
           
            //javac generates many debug op codes, such as line number annotations. For comparison with the asm
            //generated byte codes we can skip these.
            final boolean skipDebugOpCodes = true;
           
            final boolean skipInnerClassAttributes = false;
                 
            String classFileName = member.getName();           
            String className = classFileName.substring(0, classFileName.length() - ".class".length());
            String moduleName = moduleFolder.getModuleName().toString().replaceAll("_", "__").replace('.', '_');          
                   
            if (dumpAsmifiedText) {
                String asmifierDumpPath = "d:\\dev\\asmifierOutput\\javaSource\\" + moduleName + "\\" + className + ".txt"
                BytecodeDebuggingUtilities.dumpAsmifiedText(asmifierDumpPath, bytecode, skipDebugOpCodes, skipInnerClassAttributes);
            }
           
            if (dumpDisassembledText) {
                String disassembyDumpPath = "d:\\dev\\disassembly\\javaSource\\" + moduleName + "\\" + className + ".txt";
                BytecodeDebuggingUtilities.dumpDisassembledText(disassembyDumpPath, bytecode, skipDebugOpCodes, skipInnerClassAttributes);
            }
           
            if (verifyClassFileFormat) {
                BytecodeDebuggingUtilities.verifyClassFileFormat(moduleName + "\\" + className + ".class", bytecode);
            }                             
        }             
    }
      
   
    /**
     * A character stream that collects its output in a limited size string buffer, which can
     * then be used to construct a string.
     * This class will silently ignore requests to write after the maximum size is reached.
     * <p>
     * Closing a <tt>FixedSizeStringWriter</tt> has no effect. The methods in this class
     * can be called after the stream has been closed without generating an
     * <tt>IOException</tt>.
     */
    private static class FixedSizeStringWriter extends StringWriter {

        private final int maxSize;
       
        /**
         * Create a new string writer, using the default initial
         * string-buffer size.
         *
         * @param maxSize
         */
        FixedSizeStringWriter(int maxSize) {
            super();
            this.maxSize = maxSize;
        }

        /**
         * Create a new string writer, using the specified initial string-buffer
         * size.
         *
         * @param initialSize
         *            an int specifying the initial size of the buffer.
         * @param maxSize
         */
        FixedSizeStringWriter(int initialSize, int maxSize) {
            super(initialSize);
            this.maxSize = maxSize;
        }

        private boolean canWrite () {
            return getBuffer().length() < maxSize;
        }
       
        /**
         * Write a single character.
         *
         * @param c
         */
        @Override
        public void write(int c) {
            if (canWrite()) {
                super.write(c);
            }
        }

        /**
         * Write a portion of an array of characters.
         *
         * @param cbuf
         *            Array of characters
         * @param off
         *            Offset from which to start writing characters
         * @param len
         *            Number of characters to write
         */
        @Override
        public void write(char cbuf[], int off, int len) {
            if (canWrite()) {
                super.write(cbuf, off, len);
            }
        }

        /**
         * Write a string.
         * @param str
         */
        @Override
        public void write(String str) {
            if (canWrite()) {
                super.write(str);
            }
        }

        /**
         * Write a portion of a string.
         *
         * @param str
         *            String to be written
         * @param off
         *            Offset from which to start writing characters
         * @param len
         *            Number of characters to write
         */
        @Override
        public void write(String str, int off, int len) {
            if (canWrite()) {
                super.write(str, off, len);
            }
        }
    }
   
}
TOP

Related Classes of org.openquark.cal.internal.machine.lecc.LECCJavaSourceGenerator

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.