Package net.janino

Source Code of net.janino.JavaSourceClassLoader

/*
* Janino - An embedded Java[TM] compiler
* Copyright (C) 2001-2004 Arno Unkrig
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information:
*   Arno Unkrig
*   Ferdinand-Miller-Platz 10
*   80335 Muenchen
*   Germany
*   http://www.janino.net
*   maintainer@janino.net
*/

package net.janino;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import net.janino.util.ClassFile;
import net.janino.util.TunnelException;
import net.janino.util.enum.EnumeratorFormatException;
import net.janino.util.resource.DirectoryResourceFinder;
import net.janino.util.resource.PathResourceFinder;
import net.janino.util.resource.ResourceFinder;

/**
* A {@link ClassLoader} that, unlike usual {@link ClassLoader}s,
* does not load byte code, but reads Java<sup>TM</sup> source code and then scans, parses,
* compiles and loads it into the virtual machine.
*/
public class JavaSourceClassLoader extends ClassLoader {

    /**
     * Read Java<sup>TM</sup> source code for a given class name, scan, parse, compile and load
     * it into the virtual machine, and invoke its "main()" method.
     * <p>
     * Usage is as follows:
     * <pre>
     *   java [ <i>java-option</i> ] net.janino.JavaSourceClassLoader [ <i>option</i> ] ... <i>class-name</i> [ <i>arg</i> ] ...
     *     <i>java-option</i> Any valid option for the Java Virtual Machine (e.g. "-classpath <i>colon-separated-list-of-class-directories</i>")
     *     <i>option</i>:
     *       -sourcepath <i>colon-separated-list-of-source-directories</i>
     *       -encoding <i>character-encoding</i>
     * </pre>
     */
    public static void main(String[] args) {
        File[]               optionalSourcePath = { new File(".") };
        String               optionalCharacterEncoding = null;
        DebuggingInformation debuggingInformation = DebuggingInformation.LINES.add(DebuggingInformation.SOURCE);

        // Scan command line options.
        int i;
        for (i = 0; i < args.length; ++i) {
            String arg = args[i];
            if (!arg.startsWith("-")) break;

            if ("-sourcepath".equals(arg)) {
                optionalSourcePath = PathResourceFinder.parsePath(args[++i]);
            } else
            if ("-encoding".equals(arg)) {
                optionalCharacterEncoding = args[++i];
            } else
            if (arg.equals("-g")) {
                debuggingInformation = DebuggingInformation.ALL;
            } else
            if (arg.equals("-g:none")) {
                debuggingInformation = DebuggingInformation.NONE;
            } else
            if (arg.startsWith("-g:")) {
                try {
                    debuggingInformation = new DebuggingInformation(arg.substring(3).toUpperCase());
                } catch (EnumeratorFormatException ex) {
                    debuggingInformation = DebuggingInformation.NONE;
                }
            } else
            if ("-help".equals(arg)) {
                System.out.println("Usage:");
                System.out.println("  java [ <java-option> ] " + JavaSourceClassLoader.class.getName() + " [ <option>] ... <class-name> [ <arg> ] ...");
                System.out.println("Load a Java class by name and invoke its \"main(String[])\" method,");
                System.out.println("pass");
                System.out.println("  <java-option> Any valid option for the Java Virtual Machine (e.g. \"-classpath <dir>\")");
                System.out.println("  <option>:");
                System.out.println("    -sourcepath <" + File.pathSeparator + "-separated-list-of-source-directories>");
                System.out.println("    -encoding <character-encoding>");
                System.out.println("    -g                     Generate all debugging info");
                System.out.println("    -g:none                Generate no debugging info");
                System.out.println("    -g:{lines,vars,source} Generate only some debugging info");
                System.exit(0);
            } else
            {
                System.err.println("Invalid command line option \"" + arg + "\"; try \"-help\"");
                System.exit(1);
            }
        }

        // Determine class name.
        if (i == args.length) {
            System.err.println("No class name given, try \"-help\"");
            System.exit(1);
        }
        String className = args[i++];

        // Determine arguments passed to "main()".
        String[] mainArgs = new String[args.length - i];
        System.arraycopy(args, i, mainArgs, 0, args.length - i);

        // Set up a JavaSourceClassLoader.
        ClassLoader cl = new JavaSourceClassLoader(
            ClassLoader.getSystemClassLoader(),
            optionalSourcePath,
            optionalCharacterEncoding,
            debuggingInformation
        );

        // Load the given class.
        Class clazz;
        try {
            clazz = cl.loadClass(className);
        } catch (ClassNotFoundException ex) {
            System.err.println("Loading class \"" + className + "\": " + ex.getMessage());
            System.exit(1);
            return; // NEVER REACHED
        }

        // Find its "main" method.
        Method mainMethod;
        try {
            mainMethod = clazz.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            System.err.println("Class \"" + className + "\" has not public method \"main(String[])\".");
            System.exit(1);
            return; // NEVER REACHED
        }

        // Invoke the "main" method.
        try {
            mainMethod.invoke(null, new Object[] { mainArgs });
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * Set up a {@link JavaSourceClassLoader} that finds Java<sup>TM</sup> source code in a file
     * that resides in either of the directories specified by the given source path.
     *
     * @param parentClassLoader See {@link ClassLoader}
     * @param optionalSourcePath A collection of directories that are searched for Java<sup>TM</sup> source files in the given order
     * @param optionalCharacterEncoding The encoding of the Java<sup>TM</sup> source files (<code>null</code> for platform default encoding)
     */
    public JavaSourceClassLoader(
        ClassLoader          parentClassLoader,
        File[]               optionalSourcePath,
        String               optionalCharacterEncoding,
        DebuggingInformation debuggingInformation
    ) {
        super(parentClassLoader);

        // Process the source path.
        ResourceFinder sourceFinder = (
            optionalSourcePath == null ?
            (ResourceFinder) new DirectoryResourceFinder(new File(".")) :
            (ResourceFinder) new PathResourceFinder(optionalSourcePath)
        );

        this.iClassLoader = new JavaSourceIClassLoader(
            sourceFinder,
            optionalCharacterEncoding,
            this.uncompiledCompilationUnits,
            new ClassLoaderIClassLoader(parentClassLoader)
        );

        this.debuggingInformation = debuggingInformation;
    }

    /**
     * Implementation of {@link ClassLoader#findClass(String)}.
     * @throws ClassNotFoundException
     * @throws TunnelException wraps a {@link Scanner.ScanException}
     * @throws TunnelException wraps a {@link Parser.ParseException}
     * @throws TunnelException wraps a {@link Java.CompileException}
     * @throws TunnelException wraps a {@link IOException}
     */
    protected Class findClass(String name) throws ClassNotFoundException {

        // Check precompiled classes (classes that were parsed and compiled, but were not yet needed).
        ClassFile cf = (ClassFile) this.precompiledClasses.get(name);

        if (cf == null) {

            // Check parsed, but uncompiled compilation units.
            Java.CompilationUnit compilationUnitToCompile = null;
            for (Iterator i = this.uncompiledCompilationUnits.iterator(); i.hasNext();) {
                Java.CompilationUnit cu = (Java.CompilationUnit) i.next();
                if (cu.findClass(name) != null) {
                    compilationUnitToCompile = cu;
                    break;
                }
            }

            // Find and parse the right compilation unit.
            if (compilationUnitToCompile == null) {

                // Find and parse the right compilation unit, and add it to
                // "this.uncompiledCompilationUnits".
                if (this.iClassLoader.loadIClass(Descriptor.fromClassName(name)) == null) throw new ClassNotFoundException(name);

                for (Iterator i = this.uncompiledCompilationUnits.iterator(); i.hasNext();) {
                    Java.CompilationUnit cu = (Java.CompilationUnit) i.next();
                    if (cu.findClass(name) != null) {
                        compilationUnitToCompile = cu;
                        break;
                    }
                }
                if (compilationUnitToCompile == null) throw new RuntimeException(); // SNO: Loading IClass does not parse the CU that defines the IClass.
            }

            // Compile the compilation unit.
            ClassFile[] cfs;
            try {
                cfs = compilationUnitToCompile.compile(this.iClassLoader, this.debuggingInformation);
            } catch (Java.CompileException e) {
                throw new TunnelException(e);
            }

            // Now that the CU is compiled, remove it from the set of uncompiled CUs.
            this.uncompiledCompilationUnits.remove(compilationUnitToCompile);

            // Get the generated class file.
            for (int i = 0; i < cfs.length; ++i) {
                if (cfs[i].getThisClassName().equals(name)) {
                    if (cf != null) throw new RuntimeException(); // SNO: Multiple CFs with the same name.
                    cf = cfs[i];
                } else {
                    if (this.precompiledClasses.containsKey(cfs[i].getThisClassName())) throw new TunnelException(new Java.CompileException("Class or interface \"" + name + "\" is defined in more than one compilation unit", null));
                    this.precompiledClasses.put(cfs[i].getThisClassName(), cfs[i]);
                }
            }
            if (cf == null) throw new RuntimeException(); // SNO: Compilation of CU does not generate CF with requested name.
        }

        if (cf == null) throw new ClassNotFoundException(name);

        // Store the class file's byte code into a byte array.
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            cf.store(bos);
        } catch (IOException ex) {
            throw new RuntimeException(); // SNO: ByteArrayOutputStream thows IOException,
        }
        byte[] ba = bos.toByteArray();

        // Load the byte code into the virtual machine.
        return this.defineClass(name, ba, 0, ba.length);
    }

    private final IClassLoader         iClassLoader;
    private final DebuggingInformation debuggingInformation;

    /**
     * Collection of parsed, but uncompiled compilation units.
     */
    private final Set uncompiledCompilationUnits = new HashSet(); // Java.CompilationUnit

    /**
     * Map of classes that were parsed and compiled into class files, but not yet loaded into the
     * virtual machine.
     */
    private final Map precompiledClasses = new HashMap(); // class name => Java.ClassFile
}
TOP

Related Classes of net.janino.JavaSourceClassLoader

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.