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: import org.codehaus.janino.util.ClassFile;
039:
040: /**
041: * A wrapper object that turns a {@link ClassFile} object into a
042: * {@link IClass}.
043: */
044: public class ClassFileIClass extends IClass {
045: private static final boolean DEBUG = false;
046:
047: private final ClassFile classFile;
048: private final IClassLoader iClassLoader;
049: private final short accessFlags;
050:
051: private final Map resolvedFields = new HashMap(); // FieldInfo => IField
052:
053: /**
054: * @param classFile Source of data
055: * @param iClassLoader {@link IClassLoader} through which to load other classes
056: */
057: public ClassFileIClass(ClassFile classFile,
058: IClassLoader iClassLoader) {
059: this .classFile = classFile;
060: this .iClassLoader = iClassLoader;
061:
062: // Determine class access flags.
063: this .accessFlags = classFile.accessFlags;
064: }
065:
066: // Implement IClass.
067:
068: protected IConstructor[] getDeclaredIConstructors2() {
069: List iConstructors = new ArrayList();
070:
071: for (Iterator it = this .classFile.methodInfos.iterator(); it
072: .hasNext();) {
073: ClassFile.MethodInfo mi = (ClassFile.MethodInfo) it.next();
074: IInvocable ii;
075: try {
076: ii = this .resolveMethod(mi);
077: } catch (ClassNotFoundException ex) {
078: throw new RuntimeException(ex.getMessage());
079: }
080: if (ii instanceof IConstructor)
081: iConstructors.add(ii);
082: }
083:
084: return (IConstructor[]) iConstructors
085: .toArray(new IConstructor[iConstructors.size()]);
086: }
087:
088: protected IMethod[] getDeclaredIMethods2() {
089: List iMethods = new ArrayList();
090:
091: for (Iterator it = this .classFile.methodInfos.iterator(); it
092: .hasNext();) {
093: ClassFile.MethodInfo mi = (ClassFile.MethodInfo) it.next();
094:
095: // Skip JDK 1.5 synthetic methods (e.g. those generated for
096: // covariant return values).
097: if ((mi.getAccessFlags() & Mod.SYNTHETIC) != 0)
098: continue;
099:
100: IInvocable ii;
101: try {
102: ii = this .resolveMethod(mi);
103: } catch (ClassNotFoundException ex) {
104: throw new RuntimeException(ex.getMessage());
105: }
106: if (ii instanceof IMethod)
107: iMethods.add(ii);
108: }
109:
110: return (IMethod[]) iMethods
111: .toArray(new IMethod[iMethods.size()]);
112: }
113:
114: protected IField[] getDeclaredIFields2() {
115: IField[] ifs = new IClass.IField[this .classFile.fieldInfos
116: .size()];
117: for (int i = 0; i < this .classFile.fieldInfos.size(); ++i) {
118: try {
119: ifs[i] = this
120: .resolveField((ClassFile.FieldInfo) this .classFile.fieldInfos
121: .get(i));
122: } catch (ClassNotFoundException ex) {
123: throw new RuntimeException(ex.getMessage());
124: }
125: }
126: return ifs;
127: }
128:
129: protected IClass[] getDeclaredIClasses2() throws CompileException {
130: ClassFile.InnerClassesAttribute ica = this .classFile
131: .getInnerClassesAttribute();
132: if (ica == null)
133: return new IClass[0];
134:
135: List ices = ica.getEntries(); // ClassFile.InnerClassAttribute.Entry
136: List res = new ArrayList(); // IClass
137: for (Iterator it = ices.iterator(); it.hasNext();) {
138: ClassFile.InnerClassesAttribute.Entry e = (ClassFile.InnerClassesAttribute.Entry) it
139: .next();
140: if (e.outerClassInfoIndex == this .classFile.this Class) {
141: try {
142: res.add(this .resolveClass(e.innerClassInfoIndex));
143: } catch (ClassNotFoundException ex) {
144: throw new CompileException(ex.getMessage(), null);
145: }
146: }
147: }
148: return (IClass[]) res.toArray(new IClass[res.size()]);
149: }
150:
151: protected IClass getDeclaringIClass2() throws CompileException {
152: ClassFile.InnerClassesAttribute ica = this .classFile
153: .getInnerClassesAttribute();
154: if (ica == null)
155: return null;
156:
157: List ices = ica.getEntries(); // ClassFile.InnerClassAttribute.Entry
158: for (Iterator it = ices.iterator(); it.hasNext();) {
159: ClassFile.InnerClassesAttribute.Entry e = (ClassFile.InnerClassesAttribute.Entry) it
160: .next();
161: if (e.innerClassInfoIndex == this .classFile.this Class) {
162: // Is this an anonymous class?
163: if (e.outerClassInfoIndex == 0)
164: return null;
165: try {
166: return this .resolveClass(e.outerClassInfoIndex);
167: } catch (ClassNotFoundException ex) {
168: throw new CompileException(ex.getMessage(), null);
169: }
170: }
171: }
172: return null;
173: }
174:
175: protected IClass getOuterIClass2() throws CompileException {
176: ClassFile.InnerClassesAttribute ica = this .classFile
177: .getInnerClassesAttribute();
178: if (ica == null)
179: return null;
180:
181: List ices = ica.getEntries(); // ClassFile.InnerClassAttribute.Entry
182: for (Iterator it = ices.iterator(); it.hasNext();) {
183: ClassFile.InnerClassesAttribute.Entry e = (ClassFile.InnerClassesAttribute.Entry) it
184: .next();
185: if (e.innerClassInfoIndex == this .classFile.this Class) {
186: if (e.outerClassInfoIndex == 0) {
187:
188: // Anonymous class or local class.
189: // TODO: Determine enclosing instance of anonymous class or local class
190: return null;
191: } else {
192:
193: // Member type.
194: if ((e.innerClassAccessFlags & Mod.STATIC) != 0)
195: return null;
196: try {
197: return this .resolveClass(e.outerClassInfoIndex);
198: } catch (ClassNotFoundException ex) {
199: throw new CompileException(ex.getMessage(),
200: null);
201: }
202: }
203: }
204: }
205: return null;
206: }
207:
208: protected IClass getSuperclass2() throws CompileException {
209: if (this .classFile.super class == 0)
210: return null;
211: try {
212: return this .resolveClass(this .classFile.super class);
213: } catch (ClassNotFoundException e) {
214: throw new CompileException(e.getMessage(), null);
215: }
216: }
217:
218: public Access getAccess() {
219: return ClassFileIClass.accessFlags2Access(this .accessFlags);
220: }
221:
222: public boolean isFinal() {
223: return (this .accessFlags & Mod.FINAL) != 0;
224: }
225:
226: protected IClass[] getInterfaces2() throws CompileException {
227: return this .resolveClasses(this .classFile.interfaces);
228: }
229:
230: public boolean isAbstract() {
231: return (this .accessFlags & Mod.ABSTRACT) != 0;
232: }
233:
234: protected String getDescriptor2() {
235: return Descriptor.fromClassName(this .classFile
236: .getThisClassName());
237: }
238:
239: public boolean isInterface() {
240: return (this .accessFlags & Mod.INTERFACE) != 0;
241: }
242:
243: public boolean isArray() {
244: return false;
245: }
246:
247: public boolean isPrimitive() {
248: return false;
249: }
250:
251: public boolean isPrimitiveNumeric() {
252: return false;
253: }
254:
255: protected IClass getComponentType2() {
256: return null;
257: }
258:
259: public void resolveHalf() throws ClassNotFoundException {
260:
261: // Resolve superclass.
262: this .resolveClass(this .classFile.super class);
263:
264: // Resolve interfaces.
265: for (int i = 0; i < this .classFile.interfaces.length; ++i) {
266: this .resolveClass(this .classFile.interfaces[i]);
267: }
268:
269: // Resolve constructors and methods.
270: for (int i = 0; i < this .classFile.methodInfos.size(); ++i) {
271: this
272: .resolveMethod((ClassFile.MethodInfo) this .classFile.methodInfos
273: .get(i));
274: }
275:
276: // Process fields.
277: for (int i = 0; i < this .classFile.fieldInfos.size(); ++i) {
278: this
279: .resolveField((ClassFile.FieldInfo) this .classFile.fieldInfos
280: .get(i));
281: }
282: }
283:
284: public void resolveAllClasses() throws ClassNotFoundException {
285: for (short i = 0; i < this .classFile.constantPool.size(); ++i) {
286: ClassFile.ConstantPoolInfo cpi = this .classFile
287: .getConstantPoolInfo(i);
288: if (cpi instanceof ClassFile.ConstantClassInfo) {
289: this .resolveClass(i);
290: } else if (cpi instanceof ClassFile.ConstantNameAndTypeInfo) {
291: short descriptorIndex = ((ClassFile.ConstantNameAndTypeInfo) cpi)
292: .getDescriptorIndex();
293: String descriptor = this .classFile
294: .getConstantUtf8(descriptorIndex);
295: if (descriptor.charAt(0) == '(') {
296: MethodDescriptor md = new MethodDescriptor(
297: descriptor);
298: this .resolveClass(md.returnFD);
299: for (int j = 0; j < md.parameterFDs.length; ++j)
300: this .resolveClass(md.parameterFDs[j]);
301: } else {
302: this .resolveClass(descriptor);
303: }
304: }
305: }
306: }
307:
308: /**
309: * @param index Index of the CONSTANT_Class_info to resolve (JVMS 4.4.1)
310: */
311: private IClass resolveClass(short index)
312: throws ClassNotFoundException {
313: if (ClassFileIClass.DEBUG)
314: System.out.println("index=" + index);
315: return this .resolveClass(Descriptor
316: .fromInternalForm(this .classFile
317: .getConstantClassName(index)));
318: }
319:
320: private IClass resolveClass(String descriptor)
321: throws ClassNotFoundException {
322: if (ClassFileIClass.DEBUG)
323: System.out.println("descriptor=" + descriptor);
324:
325: IClass result = (IClass) this .resolvedClasses.get(descriptor);
326: if (result != null)
327: return result;
328:
329: result = this .iClassLoader.loadIClass(descriptor);
330: if (result == null)
331: throw new ClassNotFoundException(descriptor);
332:
333: this .resolvedClasses.put(descriptor, result);
334: return result;
335: }
336:
337: private final Map resolvedClasses = new HashMap(); // String descriptor => IClass
338:
339: private IClass[] resolveClasses(short[] ifs)
340: throws CompileException {
341: IClass[] result = new IClass[ifs.length];
342: for (int i = 0; i < result.length; ++i) {
343: try {
344: result[i] = this .resolveClass(ifs[i]);
345: } catch (ClassNotFoundException e) {
346: throw new CompileException(e.getMessage(), null);
347: }
348: }
349: return result;
350: }
351:
352: /**
353: * Turn a {@link ClassFile.MethodInfo} into an {@link IInvocable}. This includes the checking and the
354: * removal of the magic first parameter of an inner class constructor.
355: *
356: * @param methodInfo
357: * @throws ClassNotFoundException
358: */
359: private IInvocable resolveMethod(
360: final ClassFile.MethodInfo methodInfo)
361: throws ClassNotFoundException {
362: IInvocable result = (IInvocable) this .resolvedMethods
363: .get(methodInfo);
364: if (result != null)
365: return result;
366:
367: // Determine method name.
368: final String name = this .classFile.getConstantUtf8(methodInfo
369: .getNameIndex());
370:
371: // Determine return type.
372: MethodDescriptor md = new MethodDescriptor(this .classFile
373: .getConstantUtf8(methodInfo.getDescriptorIndex()));
374: final IClass returnType = this .resolveClass(md.returnFD);
375:
376: // Determine parameter types.
377: final IClass[] parameterTypes = new IClass[md.parameterFDs.length];
378: for (int i = 0; i < parameterTypes.length; ++i)
379: parameterTypes[i] = this .resolveClass(md.parameterFDs[i]);
380:
381: // Determine thrown exceptions.
382: IClass tes[] = null;
383: ClassFile.AttributeInfo[] ais = methodInfo.getAttributes();
384: for (int i = 0; i < ais.length; ++i) {
385: ClassFile.AttributeInfo ai = ais[i];
386: if (ai instanceof ClassFile.ExceptionsAttribute) {
387: short[] teis = ((ClassFile.ExceptionsAttribute) ai)
388: .getExceptionIndexes();
389: tes = new IClass[teis.length];
390: for (int j = 0; j < teis.length; ++j)
391: tes[j] = this .resolveClass(teis[j]);
392: }
393: }
394: final IClass thrownExceptions[] = tes == null ? new IClass[0]
395: : tes;
396:
397: // Determine access.
398: final Access access = ClassFileIClass
399: .accessFlags2Access(methodInfo.getAccessFlags());
400:
401: if (name.equals("<init>")) {
402: result = new IClass.IConstructor() {
403: public IClass[] getParameterTypes()
404: throws CompileException {
405:
406: // Process magic first parameter of inner class constructor.
407: IClass outerIClass = ClassFileIClass.this
408: .getOuterIClass();
409: if (outerIClass != null) {
410: if (parameterTypes.length < 1)
411: throw new RuntimeException(
412: "Inner class constructor lacks magic first parameter");
413: if (parameterTypes[0] != outerIClass)
414: throw new RuntimeException(
415: "Magic first parameter of inner class constructor has type \""
416: + parameterTypes[0]
417: .toString()
418: + "\" instead of that of its enclosing instance (\""
419: + outerIClass.toString()
420: + "\")");
421: IClass[] tmp = new IClass[parameterTypes.length - 1];
422: System.arraycopy(parameterTypes, 1, tmp, 0,
423: tmp.length);
424: return tmp;
425: }
426:
427: return parameterTypes;
428: }
429:
430: public IClass[] getThrownExceptions()
431: throws CompileException {
432: return thrownExceptions;
433: }
434:
435: public Access getAccess() {
436: return access;
437: }
438: };
439: } else {
440: result = new IClass.IMethod() {
441: public String getName() {
442: return name;
443: }
444:
445: public IClass getReturnType() throws CompileException {
446: return returnType;
447: }
448:
449: public boolean isStatic() {
450: return (methodInfo.getAccessFlags() & Mod.STATIC) != 0;
451: }
452:
453: public boolean isAbstract() {
454: return (methodInfo.getAccessFlags() & Mod.ABSTRACT) != 0;
455: }
456:
457: public IClass[] getParameterTypes()
458: throws CompileException {
459: return parameterTypes;
460: }
461:
462: public IClass[] getThrownExceptions()
463: throws CompileException {
464: return thrownExceptions;
465: }
466:
467: public Access getAccess() {
468: return access;
469: }
470: };
471: }
472: this .resolvedMethods.put(methodInfo, result);
473: return result;
474: }
475:
476: private final Map resolvedMethods = new HashMap(); // MethodInfo => IInvocable
477:
478: private IField resolveField(final ClassFile.FieldInfo fieldInfo)
479: throws ClassNotFoundException {
480: IField result = (IField) this .resolvedFields.get(fieldInfo);
481: if (result != null)
482: return result;
483:
484: // Determine field name.
485: final String name = this .classFile.getConstantUtf8(fieldInfo
486: .getNameIndex());
487:
488: // Determine field type.
489: final String descriptor = this .classFile
490: .getConstantUtf8(fieldInfo.getDescriptorIndex());
491: final IClass type = this .resolveClass(descriptor);
492:
493: // Determine optional "constant value" of the field (JLS2 15.28, bullet
494: // 12). If a field has a "ConstantValue" attribute, we assume that it
495: // has a constant value. Notice that this assumption is not always
496: // correct, because typical Java<sup>TM</sup> compilers do not
497: // generate a "ConstantValue" attribute for fields like
498: // "int RED = 0", because "0" is the default value for an integer
499: // field.
500: ClassFile.ConstantValueAttribute cva = null;
501: ClassFile.AttributeInfo[] ais = fieldInfo.getAttributes();
502: for (int i = 0; i < ais.length; ++i) {
503: ClassFile.AttributeInfo ai = ais[i];
504: if (ai instanceof ClassFile.ConstantValueAttribute) {
505: cva = (ClassFile.ConstantValueAttribute) ai;
506: break;
507: }
508: }
509:
510: Object ocv = null;
511: if (cva != null) {
512: ClassFile.ConstantPoolInfo cpi = this .classFile
513: .getConstantPoolInfo(cva.getConstantValueIndex());
514: if (cpi instanceof ClassFile.ConstantValuePoolInfo) {
515: ocv = ((ClassFile.ConstantValuePoolInfo) cpi)
516: .getValue(this .classFile);
517: } else {
518: throw new RuntimeException(
519: "Unexpected constant pool info type \""
520: + cpi.getClass().getName() + "\"");
521: }
522: }
523: final Object optionalConstantValue = ocv;
524:
525: // Determine access.
526: final Access access = ClassFileIClass
527: .accessFlags2Access(fieldInfo.getAccessFlags());
528:
529: result = new IField() {
530: public Object getConstantValue() throws CompileException {
531: return optionalConstantValue;
532: }
533:
534: public String getName() {
535: return name;
536: }
537:
538: public IClass getType() throws CompileException {
539: return type;
540: }
541:
542: public boolean isStatic() {
543: return (fieldInfo.getAccessFlags() & Mod.STATIC) != 0;
544: }
545:
546: public Access getAccess() {
547: return access;
548: }
549: };
550: this .resolvedFields.put(fieldInfo, result);
551: return result;
552: }
553:
554: private static Access accessFlags2Access(short accessFlags) {
555: return ((accessFlags & Mod.PUBLIC) != 0 ? Access.PUBLIC
556: : (accessFlags & Mod.PROTECTED) != 0 ? Access.PROTECTED
557: : (accessFlags & Mod.PRIVATE) != 0 ? Access.PRIVATE
558: : Access.DEFAULT);
559: }
560: }
|