View Javadoc

1   /*
2    * @(#) ClassDescriptor.java Jan 10, 2004
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
9    */
10  package nl.tudelft.simulation.dsol.interpreter.classfile;
11  
12  import java.io.DataInput;
13  import java.io.DataInputStream;
14  import java.io.IOException;
15  import java.lang.reflect.AccessibleObject;
16  import java.util.HashMap;
17  import java.util.Map;
18  
19  /***
20   * A ClassDescriptor <br>
21   * (c) copyright 2003 <a href="http://www.simulation.tudelft.nl">Delft
22   * University of Technology </a>, the Netherlands. <br>
23   * See for project information <a
24   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
25   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
26   * License (GPL) </a>, no warranty <br>
27   * 
28   * @version 1.0 Jan 10, 2004 <br>
29   * @author <a href="http://www.simulation.tudelft.nl/people/jacobs.html">Peter
30   *         Jacobs </a>
31   */
32  public class ClassDescriptor
33  {
34  	/*** the repository which caches descriptors */
35  	private static final Map REPOSITORY = new HashMap();
36  
37  	/*** the constantPool */
38  	private Constant[] constantPool = null;
39  
40  	/*** the localVariables */
41  	private Map methods = new HashMap();
42  
43  	/*** the javaClass we are reading */
44  	private Class javaClass = null;
45  
46  	/***
47  	 * returns the classDescriptor of this class
48  	 * 
49  	 * @param clazz the class the clazz to parse
50  	 * @return ClassDescriptor the descriptor
51  	 * @throws IOException on IO exception
52  	 * @throws ClassNotFoundException if clazz cannot be found
53  	 */
54  	public static ClassDescriptor get(final Class clazz) throws IOException,
55  			ClassNotFoundException
56  	{
57  		synchronized (REPOSITORY)
58  		{
59  			if (REPOSITORY.containsKey(clazz))
60  			{
61  				return (ClassDescriptor) REPOSITORY.get(clazz);
62  			}
63  			ClassDescriptor classDescriptor = new ClassDescriptor(clazz);
64  			REPOSITORY.put(clazz, classDescriptor);
65  			return classDescriptor;
66  		}
67  	}
68  
69  	/***
70  	 * 
71  	 * constructs a new ClassDescriptor
72  	 * 
73  	 * @param javaClass the class to read
74  	 * @throws IOException on IO - failure
75  	 * @throws ClassNotFoundException on incomplete classPath
76  	 */
77  	private ClassDescriptor(final Class javaClass) throws IOException,
78  			ClassNotFoundException
79  	{
80  		super();
81  		this.javaClass = javaClass;
82  		ClassLoader classLoader = javaClass.getClassLoader();
83  		if (classLoader == null)
84  		{
85  			classLoader = ClassLoader.getSystemClassLoader();
86  		}
87  		this.readClass(new DataInputStream(classLoader
88  				.getResourceAsStream(javaClass.getName().replace('.', '/')
89  						+ ".class")));
90  	}
91  
92  	/***
93  	 * returns the methodDescriptor of the method.
94  	 * 
95  	 * @param method the method to resolve.
96  	 * @return its descriptor.
97  	 */
98  	public MethodDescriptor getMethod(final AccessibleObject method)
99  	{
100 		return (MethodDescriptor) this.methods.get(method);
101 	}
102 
103 	/***
104 	 * returns the constantpool of a classfile
105 	 * 
106 	 * @return Constant[] the constantpool
107 	 */
108 	public Constant[] getConstantPool()
109 	{
110 		return this.constantPool;
111 	}
112 
113 	/***
114 	 * returns the methods of this class
115 	 * 
116 	 * @return MethodDescriptor[]
117 	 */
118 	public MethodDescriptor[] getMethods()
119 	{
120 		return (MethodDescriptor[]) this.methods.values().toArray(
121 				new MethodDescriptor[this.methods.size()]);
122 	}
123 
124 	/*** ** PRIVATE PARSING METHODS *** */
125 
126 	/***
127 	 * reads the class
128 	 * 
129 	 * @param dataInput the dataInput
130 	 * @throws IOException on failure
131 	 * @throws ClassNotFoundException on incomplete classPath
132 	 */
133 	private void readClass(final DataInput dataInput) throws IOException,
134 			ClassNotFoundException
135 	{
136 		//We skip the magic(u4) and version(u2,u2)
137 		dataInput.skipBytes(8);
138 
139 		//Now we read the constantPool
140 		this.readConstantPool(dataInput);
141 
142 		//We skip the accessFlag(u2), thisClass(u2),superClass(u2)
143 		dataInput.skipBytes(6);
144 
145 		//We skip the interfaces
146 		int interfacesCount = dataInput.readUnsignedShort();
147 		dataInput.skipBytes(2 * interfacesCount);
148 
149 		//We skip the fields
150 		//AttributeInfo has nameIndex(u2),length(u4)
151 		int fieldCount = dataInput.readUnsignedShort();
152 		for (int i = 0; i < fieldCount; i++)
153 		{
154 			//fieldInfo has accessFlag(u2),name(u2),descriptor(u2)
155 			dataInput.skipBytes(6);
156 			int attributeCount = dataInput.readUnsignedShort();
157 			for (int j = 0; j < attributeCount; j++)
158 			{
159 				//AttributeInfo has nameIndex(u2)
160 				dataInput.skipBytes(2);
161 				dataInput.skipBytes(dataInput.readInt());
162 			}
163 		}
164 
165 		//Finally we read the methods
166 		int methodCount = dataInput.readUnsignedShort();
167 		for (int i = 0; i < methodCount; i++)
168 		{
169 			MethodDescriptor methodDescriptor = new MethodDescriptor(dataInput,
170 					this.constantPool);
171 			AccessibleObject method = this.parseMethod(methodDescriptor
172 					.getName(), methodDescriptor.getMethodSignature()
173 					.getParameterTypes());
174 			methodDescriptor.setMethod(method);
175 			this.methods.put(method, methodDescriptor);
176 		}
177 	}
178 
179 	/***
180 	 * reads the constantpool from file
181 	 * 
182 	 * @param dataInput the stream
183 	 * @throws IOException on failure
184 	 */
185 	private void readConstantPool(final DataInput dataInput) throws IOException
186 	{
187 		this.constantPool = new Constant[dataInput.readUnsignedShort()];
188 
189 		/*
190 		 * constant_pool[0] is unused by the compiler and may be used freely by
191 		 * the implementation.
192 		 */
193 		for (int i = 1; i < this.constantPool.length; i++)
194 		{
195 			this.constantPool[i] = Constant.readConstant(this.constantPool,
196 					dataInput);
197 
198 			if (this.constantPool[i] instanceof ConstantDouble
199 					|| this.constantPool[i] instanceof ConstantLong)
200 			{
201 				/*
202 				 * Quote from the JVM specification: "All eight byte constants
203 				 * take up two spots in the constant pool. If this is the n'th
204 				 * byte in the constant pool, then the next item will be
205 				 * numbered n+2"
206 				 * 
207 				 * Thus we have to increment the index counter.
208 				 */
209 				i++;
210 			}
211 		}
212 	}
213 
214 	/***
215 	 * parses a methodName and descriptor to an accessibleObject
216 	 * 
217 	 * @param methodName the name of the method
218 	 * @param argumentClasses the argumentClasses
219 	 * @return the AccessibleObject
220 	 */
221 	private AccessibleObject parseMethod(final String methodName,
222 			final Class[] argumentClasses)
223 	{
224 		try
225 		{
226 			if (methodName.equals("<clinit>"))
227 			{
228 				return null;
229 			}
230 			if (!methodName.equals("<init>"))
231 			{
232 				return this.javaClass.getDeclaredMethod(methodName,
233 						argumentClasses);
234 			}
235 			return this.javaClass.getDeclaredConstructor(argumentClasses);
236 		} catch (Exception exception)
237 		{
238 			return null;
239 		}
240 	}
241 }