View Javadoc

1   /*
2    * @(#) Interpreter.java Jan 13, 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;
11  
12  import java.io.IOException;
13  import java.io.InputStream;
14  import java.lang.reflect.AccessibleObject;
15  import java.lang.reflect.Constructor;
16  import java.lang.reflect.Method;
17  import java.lang.reflect.Modifier;
18  import java.util.Properties;
19  import java.util.Stack;
20  import java.util.logging.Level;
21  
22  import nl.tudelft.simulation.dsol.interpreter.classfile.ClassDescriptor;
23  import nl.tudelft.simulation.dsol.interpreter.classfile.Constant;
24  import nl.tudelft.simulation.dsol.interpreter.classfile.MethodDescriptor;
25  import nl.tudelft.simulation.dsol.interpreter.operations.InvokeOperation;
26  import nl.tudelft.simulation.dsol.interpreter.operations.JumpOperation;
27  import nl.tudelft.simulation.dsol.interpreter.operations.RET;
28  import nl.tudelft.simulation.dsol.interpreter.operations.RETURN;
29  import nl.tudelft.simulation.dsol.interpreter.operations.ReturnOperation;
30  import nl.tudelft.simulation.dsol.interpreter.operations.VoidOperation;
31  import nl.tudelft.simulation.dsol.interpreter.operations.WIDE;
32  import nl.tudelft.simulation.language.io.URLResource;
33  import nl.tudelft.simulation.language.reflection.ClassUtil;
34  import nl.tudelft.simulation.logger.Logger;
35  
36  /***
37   * A Interpreter <br>
38   * (c) copyright 2003 <a href="http://www.simulation.tudelft.nl">Delft
39   * University of Technology </a>, the Netherlands. <br>
40   * See for project information <a
41   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
42   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
43   * License (GPL) </a>, no warranty <br>
44   * 
45   * @version 1.0 Jan 13, 2004 <br>
46   * @author <a href="http://www.simulation.tudelft.nl/people/jacobs.html">Peter
47   *         Jacobs </a>
48   */
49  public class Interpreter
50  {
51  
52  	/*** Let's resolve the logLevel to use. */
53  	static
54  	{
55  		try
56  		{
57  			InputStream stream = URLResource
58  					.getResourceAsStream("/interpreter.properties");
59  			if (stream != null)
60  			{
61  				Properties properties = new Properties();
62  				properties.load(stream);
63  				Logger.setLogLevel(Level.parse(properties
64  						.getProperty("interpreter.logLevel")));
65  			}
66  		} catch (Exception exception)
67  		{
68  			Logger.warning(MethodDescriptor.class, "<clinit>", exception);
69  		}
70  	}
71  
72  	/***
73  	 * constructs a new Interpreter
74  	 */
75  	protected Interpreter()
76  	{
77  		super();
78  	}
79  
80  	/***
81  	 * creates a frame for a method
82  	 * 
83  	 * @param object the object on which the method must be invoked
84  	 * @param method the method or constructor
85  	 * @param arguments the arguments
86  	 * @return Frame the result
87  	 * @throws ClassNotFoundException whenever the classpath is incomplete
88  	 * @throws IOException on IOException
89  	 */
90  	public static Frame createFrame(final Object object,
91  			final AccessibleObject method, final Object[] arguments)
92  			throws ClassNotFoundException, IOException
93  	{
94  		Object[] args = new Object[0];
95  		if (arguments != null)
96  		{
97  			args = arguments;
98  		}
99  		ClassDescriptor classDescriptor = null;
100 		int modifiers = -1;
101 		Class[] parameterTypes = null;
102 		if (method instanceof Method)
103 		{
104 			parameterTypes = ((Method) method).getParameterTypes();
105 			classDescriptor = ClassDescriptor.get(((Method) method)
106 					.getDeclaringClass());
107 			modifiers = ((Method) method).getModifiers();
108 		} else
109 		{
110 			parameterTypes = ((Constructor) method).getParameterTypes();
111 			classDescriptor = ClassDescriptor.get(((Constructor) method)
112 					.getDeclaringClass());
113 			modifiers = ((Constructor) method).getModifiers();
114 		}
115 		MethodDescriptor methodDescriptor = classDescriptor.getMethod(method);
116 		OperandStack operandStack = new OperandStack(methodDescriptor
117 				.getMaxStack());
118 		LocalVariable[] localVariables = LocalVariable
119 				.newInstance(methodDescriptor.getLocalVariableTable());
120 		int counter = 0;
121 
122 		//If method!=static put object on localVariableTable
123 		if (!Modifier.isStatic(modifiers))
124 		{
125 			localVariables[counter++].setValue(object);
126 		}
127 
128 		// add the call parameters for the method to the stack
129 		for (int i = 0; i < args.length; i++)
130 		{
131 			localVariables[counter++].setValue(arguments[i]);
132 			if ((parameterTypes[i].equals(double.class))
133 					|| (parameterTypes[i].equals(long.class)))
134 			{
135 				counter++;
136 			}
137 		}
138 
139 		if (Logger.getLogLevel().intValue() <= Level.FINER.intValue())
140 		{
141 			String logMessage = methodDescriptor.getMethod().toString()
142 					+ "\n"
143 					+ Operation.toString(methodDescriptor, methodDescriptor
144 							.getOperations());
145 			Logger.finer(Interpreter.class, "createFrame", logMessage);
146 		}
147 
148 		return new Frame(classDescriptor.getConstantPool(), localVariables,
149 				methodDescriptor.getOperations(), operandStack,
150 				methodDescriptor);
151 	}
152 
153 	/***
154 	 * interprets the frameStack
155 	 * 
156 	 * @param frameStack the frameStack of the interpreter
157 	 * @return Object the return value of the invoked method
158 	 * @throws InterpreterException on failure
159 	 */
160 	public static Object interpret(final Stack frameStack)
161 			throws InterpreterException
162 	{
163 		Frame frame = (Frame) frameStack.peek();
164 		OperandStack operandStack = frame.getOperandStack();
165 		Constant[] constantPool = frame.getConstantPool();
166 		LocalVariable[] localVariables = frame.getLocalVariables();
167 		MethodDescriptor methodDescriptor = frame.getMethodDescriptor();
168 		int operationIndex = frame.getReturnPosition();
169 		boolean log = Logger.getLogLevel().intValue() <= Level.FINEST
170 				.intValue();
171 
172 		while (true)
173 		{
174 			Operation operation = frame.getOperations()[operationIndex];
175 
176 			//Let's log
177 			if (log)
178 			{
179 				Logger
180 						.finest(Interpreter.class, "interpret", ""
181 								+ operandStack);
182 				Logger.finest(Interpreter.class, "interpret", ""
183 						+ frameStack.size() + " " + operation);
184 			}
185 
186 			//	WIDE is special. We need to get its target
187 			if (operation instanceof WIDE)
188 			{
189 				operation = ((WIDE) operation).getTarget();
190 			}
191 
192 			//	Void operations are executed and done
193 			if (operation instanceof VoidOperation)
194 			{
195 				((VoidOperation) operation).execute(operandStack, constantPool,
196 						localVariables);
197 				operationIndex++;
198 				continue;
199 			}
200 
201 			//	Invoke operations are executed and done
202 			if (operation instanceof InvokeOperation)
203 			{
204 				Frame childFrame = ((InvokeOperation) operation).execute(frame);
205 				operationIndex++;
206 				if (childFrame != null)
207 				{
208 					frame.setReturnPosition(operationIndex);
209 					if (frame.isPaused())
210 					{
211 						return frame;
212 					}
213 					frame = childFrame;
214 					frameStack.push(frame);
215 					operandStack = frame.getOperandStack();
216 					constantPool = frame.getConstantPool();
217 					localVariables = frame.getLocalVariables();
218 					methodDescriptor = frame.getMethodDescriptor();
219 					operationIndex = 0;
220 				}
221 				continue;
222 			}
223 
224 			//	What to do with jumps
225 			if (operation instanceof JumpOperation)
226 			{
227 				int offset = ((JumpOperation) operation).execute(operandStack,
228 						constantPool, localVariables);
229 				int bytePosition = offset;
230 				if (!(operation instanceof RET))
231 				{
232 					bytePosition = bytePosition
233 							+ methodDescriptor.getBytePosition(operationIndex);
234 				}
235 				operationIndex = methodDescriptor
236 						.getOperationIndex(bytePosition);
237 				continue;
238 			}
239 
240 			//Return operations are executed and returned
241 			if (operation instanceof ReturnOperation)
242 			{
243 				Object result = ((ReturnOperation) operation).execute(frame);
244 
245 				//We destroy this frame
246 				frameStack.pop();
247 
248 				//We take the caller and push
249 				if (frameStack.isEmpty())
250 				{
251 					return result;
252 				}
253 				frame = (Frame) frameStack.peek();
254 				operandStack = frame.getOperandStack();
255 				constantPool = frame.getConstantPool();
256 				localVariables = frame.getLocalVariables();
257 				methodDescriptor = frame.getMethodDescriptor();
258 				operationIndex = frame.getReturnPosition();
259 
260 				if (!(operation instanceof RETURN))
261 				{
262 					operandStack.push(result);
263 				}
264 			}
265 		}
266 	}
267 
268 	/***
269 	 * interpretes the invocation of a method on an object
270 	 * 
271 	 * @param object the object on which the method must be invoked
272 	 * @param methodName the methodName
273 	 * @param arguments the arguments
274 	 * @param argumentTypes the classes of the arguments
275 	 * @return Object the result
276 	 */
277 	public static Object invoke(final Object object, final String methodName,
278 			final Object[] arguments, final Class[] argumentTypes)
279 	{
280 		try
281 		{
282 			AccessibleObject method = null;
283 			if (!methodName.equals("<init>"))
284 			{
285 				if (object instanceof Class)
286 				{
287 					method = ClassUtil.resolveMethod((Class) object,
288 							methodName, argumentTypes);
289 				} else
290 				{
291 					method = ClassUtil.resolveMethod(object.getClass(),
292 							methodName, argumentTypes);
293 				}
294 			} else
295 			{
296 				method = object.getClass()
297 						.getDeclaredConstructor(argumentTypes);
298 			}
299 			return Interpreter.invoke(object, method, arguments);
300 		} catch (Exception exception)
301 		{
302 			throw new InterpreterException(exception);
303 		}
304 	}
305 
306 	/***
307 	 * interpretes the invocation of a method on an object
308 	 * 
309 	 * @param object the object on which the method must be invoked
310 	 * @param method the method
311 	 * @param arguments the arguments
312 	 * @return Object the result
313 	 * @throws InterpreterException on failure
314 	 */
315 	public static Object invoke(final Object object,
316 			final AccessibleObject method, final Object[] arguments)
317 			throws InterpreterException
318 	{
319 		if (Logger.getLogLevel().intValue() <= Level.FINE.intValue())
320 		{
321 			Logger.fine(Interpreter.class, "invoke", method.toString());
322 		}
323 		try
324 		{
325 			if (method instanceof Constructor
326 					&& Modifier.isNative(((Constructor) method).getModifiers()))
327 			{
328 				return ((Constructor) method).newInstance(arguments);
329 			}
330 			if (method instanceof Method
331 					&& Modifier.isNative(((Method) method).getModifiers()))
332 			{
333 				return ((Method) method).invoke(object, arguments);
334 			}
335 			Stack frameStack = new Stack();
336 			frameStack.push(Interpreter.createFrame(object, method, arguments));
337 			//Now we interprete
338 			return Interpreter.interpret(frameStack);
339 		} catch (Exception exception)
340 		{
341 			throw new InterpreterException(exception);
342 		}
343 	}
344 }