All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.codehaus.janino.IClassLoader Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version

/*
 * Janino - An embedded Java[TM] compiler
 *
 * Copyright (c) 2001-2010 Arno Unkrig. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *    2. 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.
 *    3. Neither the name of the copyright holder 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 HOLDER 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.
 */

package com.hazelcast.org.codehaus.janino;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.hazelcast.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.org.codehaus.commons.compiler.InternalCompilerException;
import com.hazelcast.org.codehaus.commons.compiler.util.StringUtil;
import com.hazelcast.org.codehaus.commons.compiler.util.resource.JarDirectoriesResourceFinder;
import com.hazelcast.org.codehaus.commons.compiler.util.resource.PathResourceFinder;
import com.hazelcast.org.codehaus.commons.compiler.util.resource.ResourceFinder;
import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;
import com.hazelcast.org.codehaus.janino.IClass.IConstructor;
import com.hazelcast.org.codehaus.janino.IClass.IMethod;

/**
 * Loads an {@link IClass} by type name.
 */
public abstract
class IClassLoader {

    private static final Logger LOGGER = Logger.getLogger(IClassLoader.class.getName());

    // The following are constants, but cannot be declared FINAL, because they are only initialized by
    // "postConstruct()".

    // SUPPRESS CHECKSTYLE MemberName|AbbreviationAsWordInName|JavadocVariable:57

    // Representations of commonly used classes.
    public IClass TYPE_java_lang_annotation_Retention;
    public IClass TYPE_java_lang_AssertionError;
    public IClass TYPE_java_lang_Boolean;
    public IClass TYPE_java_lang_Byte;
    public IClass TYPE_java_lang_Character;
    public IClass TYPE_java_lang_Class;
    public IClass TYPE_java_lang_Cloneable;
    public IClass TYPE_java_lang_Double;
    public IClass TYPE_java_lang_Enum;
    public IClass TYPE_java_lang_Error;
    public IClass TYPE_java_lang_Exception;
    public IClass TYPE_java_lang_Float;
    public IClass TYPE_java_lang_Integer;
    public IClass TYPE_java_lang_Iterable;
    public IClass TYPE_java_lang_Long;
    public IClass TYPE_java_lang_Object;
    public IClass TYPE_java_lang_Override;
    public IClass TYPE_java_lang_RuntimeException;
    public IClass TYPE_java_lang_Short;
    public IClass TYPE_java_lang_String;
    public IClass TYPE_java_lang_StringBuilder;
    public IClass TYPE_java_lang_System;
    public IClass TYPE_java_lang_Throwable;
    public IClass TYPE_java_lang_Void;
    public IClass TYPE_java_io_Serializable;
    public IClass TYPE_java_util_Iterator;

    // Representations of commonly used methods.
    public IMethod           METH_java_lang_Enum__ordinal;
    public IMethod           METH_java_lang_Iterable__iterator;
    public IMethod           METH_java_lang_String__concat__java_lang_String;
    public IMethod           METH_java_lang_String__equals__java_lang_Object;
    public IMethod           METH_java_lang_String__hashCode;
    public IMethod           METH_java_lang_String__valueOf__int;
    public IMethod           METH_java_lang_String__valueOf__long;
    public IMethod           METH_java_lang_String__valueOf__float;
    public IMethod           METH_java_lang_String__valueOf__double;
    public IMethod           METH_java_lang_String__valueOf__char;
    public IMethod           METH_java_lang_String__valueOf__boolean;
    public IMethod           METH_java_lang_String__valueOf__java_lang_Object;
    public IMethod           METH_java_lang_StringBuilder__append__int;
    public IMethod           METH_java_lang_StringBuilder__append__long;
    public IMethod           METH_java_lang_StringBuilder__append__float;
    public IMethod           METH_java_lang_StringBuilder__append__double;
    public IMethod           METH_java_lang_StringBuilder__append__char;
    public IMethod           METH_java_lang_StringBuilder__append__boolean;
    public IMethod           METH_java_lang_StringBuilder__append__java_lang_Object;
    public IMethod           METH_java_lang_StringBuilder__append__java_lang_String;
    public IMethod           METH_java_lang_StringBuilder__toString;
    @Nullable public IMethod METH_java_lang_Throwable__addSuppressed;
    public IMethod           METH_java_util_Iterator__hasNext;
    public IMethod           METH_java_util_Iterator__next;

    // Representations of commonly used constructors.
    public IConstructor CTOR_java_lang_StringBuilder__java_lang_String;

    /**
     * @param parentIClassLoader {@code null} iff this {@link IClassLoader} has no parent
     */
    @SuppressWarnings("null") public
    IClassLoader(@Nullable IClassLoader parentIClassLoader) {
        this.parentIClassLoader = parentIClassLoader;
    }

    public IClassLoader
    getParentIClassLoader() { return this.parentIClassLoader; }

    /**
     * This method must be called by the constructor of the derived class. (The reason being is that this
     * method invokes abstract {@link #loadIClass(String)} which will not work until the derived class is constructed.)
     */
    protected final void
    postConstruct() {
        try {
            this.TYPE_java_lang_annotation_Retention = this.requireType(Descriptor.JAVA_LANG_ANNOTATION_RETENTION);
            this.TYPE_java_lang_AssertionError       = this.requireType(Descriptor.JAVA_LANG_ASSERTIONERROR);
            this.TYPE_java_lang_Boolean              = this.requireType(Descriptor.JAVA_LANG_BOOLEAN);
            this.TYPE_java_lang_Byte                 = this.requireType(Descriptor.JAVA_LANG_BYTE);
            this.TYPE_java_lang_Character            = this.requireType(Descriptor.JAVA_LANG_CHARACTER);
            this.TYPE_java_lang_Class                = this.requireType(Descriptor.JAVA_LANG_CLASS);
            this.TYPE_java_lang_Cloneable            = this.requireType(Descriptor.JAVA_LANG_CLONEABLE);
            this.TYPE_java_lang_Double               = this.requireType(Descriptor.JAVA_LANG_DOUBLE);
            this.TYPE_java_lang_Enum                 = this.requireType(Descriptor.JAVA_LANG_ENUM);
            this.TYPE_java_lang_Error                = this.requireType(Descriptor.JAVA_LANG_ERROR);
            this.TYPE_java_lang_Exception            = this.requireType(Descriptor.JAVA_LANG_EXCEPTION);
            this.TYPE_java_lang_Float                = this.requireType(Descriptor.JAVA_LANG_FLOAT);
            this.TYPE_java_lang_Integer              = this.requireType(Descriptor.JAVA_LANG_INTEGER);
            this.TYPE_java_lang_Iterable             = this.requireType(Descriptor.JAVA_LANG_ITERABLE);
            this.TYPE_java_lang_Long                 = this.requireType(Descriptor.JAVA_LANG_LONG);
            this.TYPE_java_lang_Object               = this.requireType(Descriptor.JAVA_LANG_OBJECT);
            this.TYPE_java_lang_Override             = this.requireType(Descriptor.JAVA_LANG_OVERRIDE);
            this.TYPE_java_lang_RuntimeException     = this.requireType(Descriptor.JAVA_LANG_RUNTIMEEXCEPTION);
            this.TYPE_java_lang_Short                = this.requireType(Descriptor.JAVA_LANG_SHORT);
            this.TYPE_java_lang_String               = this.requireType(Descriptor.JAVA_LANG_STRING);
            this.TYPE_java_lang_StringBuilder        = this.requireType(Descriptor.JAVA_LANG_STRINGBUILDER);
            this.TYPE_java_lang_System               = this.requireType(Descriptor.JAVA_LANG_SYSTEM);
            this.TYPE_java_lang_Throwable            = this.requireType(Descriptor.JAVA_LANG_THROWABLE);
            this.TYPE_java_lang_Void                 = this.requireType(Descriptor.JAVA_LANG_VOID);
            this.TYPE_java_io_Serializable           = this.requireType(Descriptor.JAVA_IO_SERIALIZABLE);
            this.TYPE_java_util_Iterator             = this.requireType(Descriptor.JAVA_UTIL_ITERATOR);

            // SUPPRESS CHECKSTYLE LineLength:24
            this.METH_java_lang_Enum__ordinal                           = IClassLoader.requireMethod(this.TYPE_java_lang_Enum,          "ordinal");
            this.METH_java_lang_Iterable__iterator                      = IClassLoader.requireMethod(this.TYPE_java_lang_Iterable,      "iterator");
            this.METH_java_lang_String__concat__java_lang_String        = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "concat",   this.TYPE_java_lang_String);
            this.METH_java_lang_String__equals__java_lang_Object        = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "equals",   this.TYPE_java_lang_Object);
            this.METH_java_lang_String__hashCode                        = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "hashCode");
            this.METH_java_lang_String__valueOf__int                    = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  IClass.INT);
            this.METH_java_lang_String__valueOf__long                   = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  IClass.LONG);
            this.METH_java_lang_String__valueOf__float                  = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  IClass.FLOAT);
            this.METH_java_lang_String__valueOf__double                 = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  IClass.DOUBLE);
            this.METH_java_lang_String__valueOf__char                   = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  IClass.CHAR);
            this.METH_java_lang_String__valueOf__boolean                = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  IClass.BOOLEAN);
            this.METH_java_lang_String__valueOf__java_lang_Object       = IClassLoader.requireMethod(this.TYPE_java_lang_String,        "valueOf",  this.TYPE_java_lang_Object);
            this.METH_java_lang_StringBuilder__append__int              = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   IClass.INT);
            this.METH_java_lang_StringBuilder__append__long             = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   IClass.LONG);
            this.METH_java_lang_StringBuilder__append__float            = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   IClass.FLOAT);
            this.METH_java_lang_StringBuilder__append__double           = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   IClass.DOUBLE);
            this.METH_java_lang_StringBuilder__append__char             = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   IClass.CHAR);
            this.METH_java_lang_StringBuilder__append__boolean          = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   IClass.BOOLEAN);
            this.METH_java_lang_StringBuilder__append__java_lang_Object = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   this.TYPE_java_lang_Object);
            this.METH_java_lang_StringBuilder__append__java_lang_String = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "append",   this.TYPE_java_lang_String);
            this.METH_java_lang_StringBuilder__toString                 = IClassLoader.requireMethod(this.TYPE_java_lang_StringBuilder, "toString");
            this.METH_java_lang_Throwable__addSuppressed                = IClassLoader.getMethod(this.TYPE_java_lang_Throwable, "addSuppressed", this.TYPE_java_lang_Throwable);
            this.METH_java_util_Iterator__hasNext                       = IClassLoader.requireMethod(this.TYPE_java_util_Iterator,      "hasNext");
            this.METH_java_util_Iterator__next                          = IClassLoader.requireMethod(this.TYPE_java_util_Iterator,      "next");

            // SUPPRESS CHECKSTYLE LineLength:1
            this.CTOR_java_lang_StringBuilder__java_lang_String = IClassLoader.requireConstructor(this.TYPE_java_lang_StringBuilder, this.TYPE_java_lang_String);

        } catch (Exception e) {
            throw new InternalCompilerException("Cannot load simple types", e);
        }
    }

    private IClass
    requireType(String descriptor) {

        IClass result;
        try {
            result = this.loadIClass(descriptor);
        } catch (ClassNotFoundException cnfe) {
            throw new AssertionError(cnfe);
        }

        if (result != null) return result;

        throw new AssertionError("Required type \"" + descriptor + "\" not found");
    }

    /**
     * @return {@code null} iff the declaringType does not declare a method with that name and parameter
     *         types
     */
    @Nullable private static IMethod
    getMethod(IClass declaringType, String name, IClass... parameterTypes) {

        try {
            return declaringType.findIMethod(name, parameterTypes);
        } catch (CompileException ce) {
            throw new AssertionError(ce);
        }
    }

    /**
     * @throws AssertionError The declaringType does not declare a method with that name and parameter
     *         types
     */
    private static IMethod
    requireMethod(IClass declaringType, String name, IClass... parameterTypes) {

        IMethod result = IClassLoader.getMethod(declaringType, name, parameterTypes);

        if (result == null) {
            throw new AssertionError("Required method \"" + name + "\" not found in \"" + declaringType + "\"");
        }

        return result;
    }

    private static IConstructor
    requireConstructor(IClass declaringType, IClass... parameterTypes) {

        IConstructor result;
        try {
            result = declaringType.findIConstructor(parameterTypes);
        } catch (CompileException ce) {
            throw new AssertionError(ce);
        }

        if (result != null) return result;

        throw new AssertionError("Required constructor not found in \"" + declaringType + "\"");
    }

    /**
     * Gets an {@link IClass} by field descriptor.
     *
     * @param fieldDescriptor         E.g. 'Lpkg1/pkg2/Outer$Inner;'
     * @return                        {@code null} if an {@link IClass} could not be loaded
     * @throws ClassNotFoundException An exception was raised while loading the {@link IClass}
     */
    @Nullable public final IClass
    loadIClass(String fieldDescriptor) throws ClassNotFoundException {
        IClassLoader.LOGGER.entering(null, "loadIClass", fieldDescriptor);

        if (Descriptor.isPrimitive(fieldDescriptor)) {
            return (
                fieldDescriptor.equals(Descriptor.VOID)    ? IClass.VOID :
                fieldDescriptor.equals(Descriptor.BYTE)    ? IClass.BYTE :
                fieldDescriptor.equals(Descriptor.CHAR)    ? IClass.CHAR :
                fieldDescriptor.equals(Descriptor.DOUBLE)  ? IClass.DOUBLE :
                fieldDescriptor.equals(Descriptor.FLOAT)   ? IClass.FLOAT :
                fieldDescriptor.equals(Descriptor.INT)     ? IClass.INT :
                fieldDescriptor.equals(Descriptor.LONG)    ? IClass.LONG :
                fieldDescriptor.equals(Descriptor.SHORT)   ? IClass.SHORT :
                fieldDescriptor.equals(Descriptor.BOOLEAN) ? IClass.BOOLEAN :
                null
            );
        }

        // Ask parent IClassLoader first.
        @SuppressWarnings("null") boolean hasParentIClassLoader = this.parentIClassLoader != null;
        if (hasParentIClassLoader) {
            IClass res = this.parentIClassLoader.loadIClass(fieldDescriptor);
            if (res != null) return res;
        }

        // We need to synchronize here because "unloadableIClasses" and "loadedIClasses" are unsynchronized
        // containers. Also, "findIClass()" is not thread safe.
        IClass result;

        synchronized (this) {

            // Class could not be loaded before?
            if (this.unloadableIClasses.contains(fieldDescriptor)) return null;

            // Class already loaded?
            result = (IClass) this.loadedIClasses.get(fieldDescriptor);
            if (result != null) return result;

            // Special handling for array types.
            if (Descriptor.isArrayReference(fieldDescriptor)) {

                // Load the component type.
                IClass componentIClass = this.loadIClass(
                    Descriptor.getComponentDescriptor(fieldDescriptor)
                );
                if (componentIClass == null) return null;

                // Now get and define the array type.
                IClass arrayIClass = componentIClass.getArrayIClass(this.TYPE_java_lang_Object);
                this.loadedIClasses.put(fieldDescriptor, arrayIClass);
                return arrayIClass;
            }

            // Load the class through the {@link #findIClass(String)} method implemented by the derived class.
            // By contract, {@link findIClass(String)} must invoke {@link #defineIClass(IClass)}!
            IClassLoader.LOGGER.log(Level.FINE, "About to call \"findIClass({0})\"", fieldDescriptor);
            result = this.findIClass(fieldDescriptor);
            if (result == null) {
                if (this.loadedIClasses.containsKey(fieldDescriptor)) {
                    throw new InternalCompilerException((
                        "\"findIClass(\""
                        + fieldDescriptor
                        + "\")\" called \"defineIClass()\", but returned null!?"
                    ));
                }
                this.unloadableIClasses.add(fieldDescriptor);
                return null;
            }
            if (!this.loadedIClasses.containsKey(fieldDescriptor)) {
                throw new InternalCompilerException((
                    "\"findIClass(\""
                    + fieldDescriptor
                    + "\")\" did not call \"defineIClass()\"!?"
                ));
            }
        }

        if (!result.getDescriptor().equalsIgnoreCase(fieldDescriptor)) {
            throw new InternalCompilerException(
                "\"findIClass()\" returned \""
                + result.getDescriptor()
                + "\" instead of \""
                + fieldDescriptor
                + "\""
            );
        }

        IClassLoader.LOGGER.exiting(null, "loadIClass", result);
        return result;
    }

    /**
     * Finds a new {@link IClass} by descriptor and calls {@link #defineIClass(IClass)}.
     * 

* Similar {@link java.lang.ClassLoader#findClass(java.lang.String)}, this method must *

*
    *
  • Get an {@link IClass} object from somewhere for the given type
  • *
  • Call {@link #defineIClass(IClass)} with that {@link IClass} object as the argument
  • *
  • Return the {@link IClass} object
  • *
*

* The format of a {@code descriptor} is defined in JVMS 4.3.2. Typical descriptors are: *

*
    *
  • {@code I} (Integer)
  • *
  • {@code Lpkg1/pkg2/Clazz;} (Class declared in package)
  • *
  • {@code Lpkg1/pkg2/Outer$Inner;} Member class
  • *
*

* Notice that this method is never called for array types. *

*

* Notice that this method is never called from more than one thread at a time. In other words, implementations * of this method need not be thread-safe. *

* * @return {@code null} if a class with that descriptor could not be found * @throws ClassNotFoundException An exception was raised while loading the class */ @Nullable protected abstract IClass findIClass(String descriptor) throws ClassNotFoundException; /** * Defines an {@link IClass} in the context of this {@link IClassLoader}. *

* This method should only be called from an implementation of {@link #findIClass(String)}. *

* * @throws InternalCompilerException A different {@link IClass} object is already defined for this type */ protected final void defineIClass(IClass iClass) { String descriptor = iClass.getDescriptor(); IClassLoader.LOGGER.log(Level.FINE, "{0}: Defined type \"{0}\"", descriptor); // Define. IClass prev = (IClass) this.loadedIClasses.put(descriptor, iClass); // Previously defined? if (prev != null) { throw new InternalCompilerException("Non-identical definition of IClass \"" + descriptor + "\""); } } /** * Creates an {@link IClassLoader} that looks for classes in the given "boot class path", then in the given * "extension directories", and then in the given "class path". *

* The default for the {@code bootClassPath} is the path defined in the system property * "sun.boot.class.path", and the default for the {@code extensionDirs} is the path defined in the * "java.ext.dirs" system property. *

*/ public static IClassLoader createJavacLikePathIClassLoader( @Nullable final File[] bootClassPath, @Nullable final File[] extDirs, final File[] classPath ) { ResourceFinder bootClassPathResourceFinder = new PathResourceFinder( bootClassPath == null ? StringUtil.parsePath(System.getProperty("sun.boot.class.path")) : bootClassPath ); ResourceFinder extensionDirectoriesResourceFinder = new JarDirectoriesResourceFinder( extDirs == null ? StringUtil.parsePath(System.getProperty("java.ext.dirs")) : extDirs ); final ResourceFinder classPathResourceFinder = new PathResourceFinder(classPath); // We can load classes through "ResourceFinderIClassLoader"s, which means // they are read into "ClassFile" objects, or we can load classes through // "ClassLoaderIClassLoader"s, which means they are loaded into the JVM. // // In my environment, the latter is slightly faster. No figures about // resource usage yet. // // In applications where the generated classes are not loaded into the // same JVM instance, we should avoid to use the // ClassLoaderIClassLoader, because that assumes that final fields have // a constant value, even if not compile-time-constant but only // initialization-time constant. The classical example is // "File.separator", which is non-blank final, but not compile-time- // constant. IClassLoader icl; icl = new ResourceFinderIClassLoader(bootClassPathResourceFinder, null); icl = new ResourceFinderIClassLoader(extensionDirectoriesResourceFinder, icl); icl = new ResourceFinderIClassLoader(classPathResourceFinder, icl); return icl; } private final IClassLoader parentIClassLoader; private final Map loadedIClasses = new HashMap(); private final Set unloadableIClasses = new HashSet(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy