com.hazelcast.org.codehaus.janino.IClassLoader Maven / Gradle / Ivy
/*
* 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();
}