001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2001-2007, Arno Unkrig
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above
014: * copyright notice, this list of conditions and the following
015: * disclaimer in the documentation and/or other materials
016: * provided with the distribution.
017: * 3. The name of the author may not be used to endorse or promote
018: * products derived from this software without specific prior
019: * written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
032: */
033:
034: package org.codehaus.janino;
035:
036: import java.util.*;
037:
038: /**
039: * Loads an {@link IClass} by type name.
040: */
041: public abstract class IClassLoader {
042: private static final boolean DEBUG = false;
043:
044: public IClass OBJECT;
045: public IClass STRING;
046: public IClass CLASS;
047: public IClass THROWABLE;
048: public IClass RUNTIME_EXCEPTION;
049: public IClass ERROR;
050: public IClass CLONEABLE;
051: public IClass SERIALIZABLE;
052: public IClass BOOLEAN;
053: public IClass BYTE;
054: public IClass CHARACTER;
055: public IClass SHORT;
056: public IClass INTEGER;
057: public IClass LONG;
058: public IClass FLOAT;
059: public IClass DOUBLE;
060:
061: public IClassLoader(IClassLoader optionalParentIClassLoader) {
062: this .optionalParentIClassLoader = optionalParentIClassLoader;
063: }
064:
065: /**
066: * This method must be called by the constructor of the directly derived
067: * class. (The reason being is that this method invokes abstract
068: * {@link #loadIClass(String)} which will not work until the implementing
069: * class is constructed.)
070: */
071: protected final void postConstruct() {
072: try {
073: this .OBJECT = this .loadIClass(Descriptor.OBJECT);
074: this .STRING = this .loadIClass(Descriptor.STRING);
075: this .CLASS = this .loadIClass(Descriptor.CLASS);
076: this .THROWABLE = this .loadIClass(Descriptor.THROWABLE);
077: this .RUNTIME_EXCEPTION = this
078: .loadIClass(Descriptor.RUNTIME_EXCEPTION);
079: this .ERROR = this .loadIClass(Descriptor.ERROR);
080: this .CLONEABLE = this .loadIClass(Descriptor.CLONEABLE);
081: this .SERIALIZABLE = this
082: .loadIClass(Descriptor.SERIALIZABLE);
083: this .BOOLEAN = this .loadIClass(Descriptor.BOOLEAN);
084: this .BYTE = this .loadIClass(Descriptor.BYTE);
085: this .CHARACTER = this .loadIClass(Descriptor.CHARACTER);
086: this .SHORT = this .loadIClass(Descriptor.SHORT);
087: this .INTEGER = this .loadIClass(Descriptor.INTEGER);
088: this .LONG = this .loadIClass(Descriptor.LONG);
089: this .FLOAT = this .loadIClass(Descriptor.FLOAT);
090: this .DOUBLE = this .loadIClass(Descriptor.DOUBLE);
091: } catch (ClassNotFoundException e) {
092: throw new RuntimeException("Cannot load simple types");
093: }
094: }
095:
096: /**
097: * Get an {@link IClass} by field descriptor.
098: *
099: * @return <code>null</code> if an {@link IClass} could not be loaded
100: * @throws {@link ClassNotFoundException} if an exception was raised while loading the {@link IClass}
101: */
102: public final IClass loadIClass(String fieldDescriptor)
103: throws ClassNotFoundException {
104: if (IClassLoader.DEBUG)
105: System.out.println(this + ": Load type \""
106: + fieldDescriptor + "\"");
107:
108: if (Descriptor.isPrimitive(fieldDescriptor)) {
109: return (fieldDescriptor.equals(Descriptor.VOID_) ? IClass.VOID
110: : fieldDescriptor.equals(Descriptor.BYTE_) ? IClass.BYTE
111: : fieldDescriptor.equals(Descriptor.CHAR_) ? IClass.CHAR
112: : fieldDescriptor
113: .equals(Descriptor.DOUBLE_) ? IClass.DOUBLE
114: : fieldDescriptor
115: .equals(Descriptor.FLOAT_) ? IClass.FLOAT
116: : fieldDescriptor
117: .equals(Descriptor.INT_) ? IClass.INT
118: : fieldDescriptor
119: .equals(Descriptor.LONG_) ? IClass.LONG
120: : fieldDescriptor
121: .equals(Descriptor.SHORT_) ? IClass.SHORT
122: : fieldDescriptor
123: .equals(Descriptor.BOOLEAN_) ? IClass.BOOLEAN
124: : null);
125: }
126:
127: // Ask parent IClassLoader first.
128: if (this .optionalParentIClassLoader != null) {
129: IClass res = this .optionalParentIClassLoader
130: .loadIClass(fieldDescriptor);
131: if (res != null)
132: return res;
133: }
134:
135: // We need to synchronize here because "unloadableIClasses" and
136: // "loadedIClasses" are unsynchronized containers.
137: IClass result;
138: synchronized (this ) {
139:
140: // Class could not be loaded before?
141: if (this .unloadableIClasses.contains(fieldDescriptor))
142: return null;
143:
144: // Class already loaded?
145: result = (IClass) this .loadedIClasses.get(fieldDescriptor);
146: if (result != null)
147: return result;
148:
149: // Special handling for array types.
150: if (Descriptor.isArrayReference(fieldDescriptor)) {
151:
152: // Load the component type.
153: IClass componentIClass = this .loadIClass(Descriptor
154: .getComponentDescriptor(fieldDescriptor));
155: if (componentIClass == null)
156: return null;
157:
158: // Now get and define the array type.
159: IClass arrayIClass = componentIClass
160: .getArrayIClass(this .OBJECT);
161: this .loadedIClasses.put(fieldDescriptor, arrayIClass);
162: return arrayIClass;
163: }
164:
165: // Load the class through the {@link #findIClass(String)} method implemented by the
166: // derived class.
167: if (IClassLoader.DEBUG)
168: System.out.println("call IClassLoader.findIClass(\""
169: + fieldDescriptor + "\")");
170: result = this .findIClass(fieldDescriptor);
171: if (result == null) {
172: this .unloadableIClasses.add(fieldDescriptor);
173: return null;
174: }
175: }
176:
177: if (!result.getDescriptor().equalsIgnoreCase(fieldDescriptor))
178: throw new RuntimeException("\"findIClass()\" returned \""
179: + result.getDescriptor() + "\" instead of \""
180: + fieldDescriptor + "\"");
181:
182: if (IClassLoader.DEBUG)
183: System.out.println(this + ": Loaded type \""
184: + fieldDescriptor + "\" as " + result);
185:
186: return result;
187: }
188:
189: /**
190: * Find a new {@link IClass} by descriptor; return <code>null</code> if a class
191: * for that <code>descriptor</code> could not be found.
192: * <p>
193: * Similar {@link java.lang.ClassLoader#findClass(java.lang.String)}, this method
194: * must
195: * <ul>
196: * <li>Get an {@link IClass} object from somewhere for the given type
197: * <li>Call {@link #defineIClass(IClass)} with that {@link IClass} object as
198: * the argument
199: * <li>Return the {@link IClass} object
200: * </ul>
201: * <p>
202: * The format of a <code>descriptor</code> is defined in JVMS 4.3.2. Typical
203: * descriptors are:
204: * <ul>
205: * <li><code>I</code> (Integer)
206: * <li><code>Lpkg1/pkg2/Cls;</code> (Class declared in package)
207: * <li><code>Lpkg1/pkg2/Outer$Inner;</code> Member class
208: * </ul>
209: * Notice that this method is never called for array types.
210: * <p>
211: * Notice that this method is never called from more than one thread at a time.
212: * In other words, implementations of this method need not be synchronized.
213: *
214: * @return <code>null</code> if a class with that descriptor could not be found
215: * @throws ClassNotFoundException if an exception was raised while loading the class
216: */
217: protected abstract IClass findIClass(String descriptor)
218: throws ClassNotFoundException;
219:
220: /**
221: * Define an {@link IClass} in the context of this {@link IClassLoader}.
222: * If an {@link IClass} with that descriptor already exists, a
223: * {@link RuntimeException} is thrown.
224: * <p>
225: * This method should only be called from an implementation of
226: * {@link #findIClass(String)}.
227: *
228: * @throws RuntimeException A different {@link IClass} object is already defined for this type
229: */
230: protected final void defineIClass(IClass iClass) {
231: String descriptor = iClass.getDescriptor();
232:
233: // Already defined?
234: IClass loadedIClass = (IClass) this .loadedIClasses
235: .get(descriptor);
236: if (loadedIClass != null) {
237: if (loadedIClass == iClass)
238: return;
239: throw new RuntimeException(
240: "Non-identical definition of IClass \""
241: + descriptor + "\"");
242: }
243:
244: // Define.
245: this .loadedIClasses.put(descriptor, iClass);
246: if (IClassLoader.DEBUG)
247: System.out.println(this + ": Defined type \"" + descriptor
248: + "\"");
249: }
250:
251: private final IClassLoader optionalParentIClassLoader;
252: private final Map loadedIClasses = new HashMap(); // String descriptor => IClass
253: private final Set unloadableIClasses = new HashSet(); // String descriptor
254: }
|