001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2006, 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:
053: public IClassLoader(IClassLoader optionalParentIClassLoader) {
054: this .optionalParentIClassLoader = optionalParentIClassLoader;
055: }
056:
057: /**
058: * This method must be called by the constructor of the directly derived
059: * class. (The reason being is that this method invokes abstract
060: * {@link #loadIClass(String)} which will not work until the implementing
061: * class is constructed.)
062: */
063: protected final void postConstruct() {
064: try {
065: this .OBJECT = this .loadIClass(Descriptor.OBJECT);
066: this .STRING = this .loadIClass(Descriptor.STRING);
067: this .CLASS = this .loadIClass(Descriptor.CLASS);
068: this .THROWABLE = this .loadIClass(Descriptor.THROWABLE);
069: this .RUNTIME_EXCEPTION = this
070: .loadIClass(Descriptor.RUNTIME_EXCEPTION);
071: this .ERROR = this .loadIClass(Descriptor.ERROR);
072: this .CLONEABLE = this .loadIClass(Descriptor.CLONEABLE);
073: this .SERIALIZABLE = this
074: .loadIClass(Descriptor.SERIALIZABLE);
075: } catch (ClassNotFoundException e) {
076: throw new RuntimeException("Cannot load simple types");
077: }
078: }
079:
080: /**
081: * Get an {@link IClass} by field descriptor.
082: *
083: * @return <code>null</code> if an {@link IClass} could not be loaded
084: * @throws {@link ClassNotFoundException} if an exception was raised while loading the {@link IClass}
085: */
086: public final IClass loadIClass(String fieldDescriptor)
087: throws ClassNotFoundException {
088: if (IClassLoader.DEBUG)
089: System.out.println(this + ": Load type \""
090: + fieldDescriptor + "\"");
091:
092: if (Descriptor.isPrimitive(fieldDescriptor)) {
093: return (fieldDescriptor.equals(Descriptor.VOID) ? IClass.VOID
094: : fieldDescriptor.equals(Descriptor.BYTE) ? IClass.BYTE
095: : fieldDescriptor.equals(Descriptor.CHAR) ? IClass.CHAR
096: : fieldDescriptor
097: .equals(Descriptor.DOUBLE) ? IClass.DOUBLE
098: : fieldDescriptor
099: .equals(Descriptor.FLOAT) ? IClass.FLOAT
100: : fieldDescriptor
101: .equals(Descriptor.INT) ? IClass.INT
102: : fieldDescriptor
103: .equals(Descriptor.LONG) ? IClass.LONG
104: : fieldDescriptor
105: .equals(Descriptor.SHORT) ? IClass.SHORT
106: : fieldDescriptor
107: .equals(Descriptor.BOOLEAN) ? IClass.BOOLEAN
108: : null);
109: }
110:
111: // Ask parent IClassLoader first.
112: if (this .optionalParentIClassLoader != null) {
113: IClass res = this .optionalParentIClassLoader
114: .loadIClass(fieldDescriptor);
115: if (res != null)
116: return res;
117: }
118:
119: // We need to synchronize here because "unloadableIClasses" and
120: // "loadedIClasses" are unsynchronized containers.
121: IClass result;
122: synchronized (this ) {
123:
124: // Class could not be loaded before?
125: if (this .unloadableIClasses.contains(fieldDescriptor))
126: return null;
127:
128: // Class already loaded?
129: result = (IClass) this .loadedIClasses.get(fieldDescriptor);
130: if (result != null)
131: return result;
132:
133: // Special handling for array types.
134: if (Descriptor.isArrayReference(fieldDescriptor)) {
135:
136: // Load the component type.
137: IClass componentIClass = this .loadIClass(Descriptor
138: .getComponentDescriptor(fieldDescriptor));
139: if (componentIClass == null)
140: return null;
141:
142: // Now get and define the array type.
143: IClass arrayIClass = componentIClass
144: .getArrayIClass(this .OBJECT);
145: this .loadedIClasses.put(fieldDescriptor, arrayIClass);
146: return arrayIClass;
147: }
148:
149: // Load the class through the {@link #findIClass(String)} method implemented by the
150: // derived class.
151: if (IClassLoader.DEBUG)
152: System.out.println("call IClassLoader.findIClass(\""
153: + fieldDescriptor + "\")");
154: result = this .findIClass(fieldDescriptor);
155: if (result == null) {
156: this .unloadableIClasses.add(fieldDescriptor);
157: return null;
158: }
159: }
160:
161: if (!result.getDescriptor().equalsIgnoreCase(fieldDescriptor))
162: throw new RuntimeException("\"findIClass()\" returned \""
163: + result.getDescriptor() + "\" instead of \""
164: + fieldDescriptor + "\"");
165:
166: if (IClassLoader.DEBUG)
167: System.out.println(this + ": Loaded type \""
168: + fieldDescriptor + "\" as " + result);
169:
170: return result;
171: }
172:
173: /**
174: * Find a new {@link IClass} by descriptor; return <code>null</code> if a class
175: * for that <code>descriptor</code> could not be found.
176: * <p>
177: * Similar {@link java.lang.ClassLoader#findClass(java.lang.String)}, this method
178: * must
179: * <ul>
180: * <li>Get an {@link IClass} object from somewhere for the given type
181: * <li>Call {@link #defineIClass(IClass)} with that {@link IClass} object as
182: * the argument
183: * <li>Return the {@link IClass} object
184: * </ul>
185: * <p>
186: * The format of a <code>descriptor</code> is defined in JVMS 4.3.2. Typical
187: * descriptors are:
188: * <ul>
189: * <li><code>I</code> (Integer)
190: * <li><code>Lpkg1/pkg2/Cls;</code> (Class declared in package)
191: * <li><code>Lpkg1/pkg2/Outer$Inner;</code> Member class
192: * </ul>
193: * Notice that this method is never called for array types.
194: * <p>
195: * Notice that this method is never called from more than one thread at a time.
196: * In other words, implementations of this method need not be synchronized.
197: *
198: * @return <code>null</code> if a class with that descriptor could not be found
199: * @throws ClassNotFoundException if an exception was raised while loading the class
200: */
201: protected abstract IClass findIClass(String descriptor)
202: throws ClassNotFoundException;
203:
204: /**
205: * Define an {@link IClass} in the context of this {@link IClassLoader}.
206: * If an {@link IClass} with that descriptor already exists, a
207: * {@link RuntimeException} is thrown.
208: * <p>
209: * This method should only be called from an implementation of
210: * {@link #findIClass(String)}.
211: *
212: * @throws RuntimeException A different {@link IClass} object is already defined for this type
213: */
214: protected final void defineIClass(IClass iClass) {
215: String descriptor = iClass.getDescriptor();
216:
217: // Already defined?
218: IClass loadedIClass = (IClass) this .loadedIClasses
219: .get(descriptor);
220: if (loadedIClass != null) {
221: if (loadedIClass == iClass)
222: return;
223: throw new RuntimeException(
224: "Non-identical definition of IClass \""
225: + descriptor + "\"");
226: }
227:
228: // Define.
229: this .loadedIClasses.put(descriptor, iClass);
230: if (IClassLoader.DEBUG)
231: System.out.println(this + ": Defined type \"" + descriptor
232: + "\"");
233: }
234:
235: private final IClassLoader optionalParentIClassLoader;
236: private final Map loadedIClasses = new HashMap(); // String descriptor => IClass
237: private final Set unloadableIClasses = new HashSet(); // String descriptor
238: }
|