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.io.*;
037:
038: import org.codehaus.janino.Java.Rvalue;
039: import org.codehaus.janino.Parser.ParseException;
040: import org.codehaus.janino.Scanner.ScanException;
041: import org.codehaus.janino.util.PrimitiveWrapper;
042:
043: /**
044: * An engine that evaluates expressions in Java<sup>TM</sup> bytecode.
045: * <p>
046: * The syntax of the expression to compile is that of a Java<sup>TM</sup> expression, as defined
047: * in the <a href="http://java.sun.com/docs/books/jls/second_edition">Java Language Specification,
048: * 2nd edition</a>, section
049: * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#44393">15</a>.
050: * Notice that a Java<sup>TM</sup> expression does not have a concluding semicolon.
051: * <p>
052: * Example:<pre>
053: * a + 7 * b</pre>
054: * (Notice that this expression refers to two parameters "a" and "b", as explained below.)
055: * <p>
056: * The expression may optionally be preceeded with a sequence of import directives like
057: * <pre>
058: * import java.text.*;
059: * new DecimalFormat("####,###.##").format(10200020.345345)
060: * </pre>
061: * (Notice that the import directive is concluded with a semicolon, while the expression is not.)
062: * This feature is not available if you compile many expressions at a time (see below).
063: * <p>
064: * The expression evaluator is implemented by creating and compiling a temporary compilation unit
065: * defining one class with one static method with one RETURN statement.
066: * <p>
067: * To set up an {@link ExpressionEvaluator} object, proceed as follows:
068: * <ol>
069: * <li>
070: * Create the {@link ExpressionEvaluator} using {@link #ExpressionEvaluator()}
071: * <li>
072: * Configure the {@link ExpressionEvaluator} by calling any of the following methods:
073: * <ul>
074: * <li>{@link #setExpressionType(Class)}
075: * <li>{@link org.codehaus.janino.ScriptEvaluator#setParameters(String[], Class[])}
076: * <li>{@link org.codehaus.janino.ScriptEvaluator#setThrownExceptions(Class[])}
077: * <li>{@link org.codehaus.janino.SimpleCompiler#setParentClassLoader(ClassLoader)}
078: * <li>{@link org.codehaus.janino.ClassBodyEvaluator#setDefaultImports(String[])}
079: * </ul>
080: * <li>
081: * Call any of the {@link org.codehaus.janino.Cookable#cook(Scanner)} methods to scan,
082: * parse, compile and load the expression into the JVM.
083: * </ol>
084: * After the {@link ExpressionEvaluator} object is set up, the expression can be evaluated as
085: * often with different parameter values (see {@link #evaluate(Object[])}). This evaluation is
086: * very fast, compared to the compilation.
087: * <p>
088: * Less common methods exist that allow for the specification of the name of the generated class,
089: * the class it extends, the interfaces it implements, the name of the method that executes the
090: * expression, the exceptions that this method (i.e. the expression) is allowed to throw, and the
091: * {@link ClassLoader} that is used to define the generated class and to load classes referenced by
092: * the expression.
093: * <p>
094: * Alternatively, a number of "convenience constructors" exist that execute the steps described
095: * above instantly.
096: * <p>
097: * If you want to compile many expressions at the same time, you have the option to cook an
098: * <i>array</i> of expressions in one {@link ExpressionEvaluator} by using the following methods:
099: * <ul>
100: * <li>{@link #setMethodNames(String[])}
101: * <li>{@link #setParameters(String[][], Class[][])}
102: * <li>{@link #setExpressionTypes(Class[])}
103: * <li>{@link #setStaticMethod(boolean[])}
104: * <li>{@link #setThrownExceptions(Class[][])}
105: * <li>{@link #cook(Scanner[])}
106: * <li>{@link #evaluate(int, Object[])}
107: * </ul>
108: * Notice that these methods have array parameters in contrast to their one-expression brethren.
109: * <p>
110: * Notice that for <i>functionally</i> identical {@link ExpressionEvaluator}s,
111: * {@link java.lang.Object#equals(java.lang.Object)} will return <code>true</code>. E.g. "a+b" and
112: * "c + d" are functionally identical if "a" and "c" have the same type, and so do "b" and "d".
113: * <p>
114: * If the parameter and return types of the expression are known at compile time, then a "fast"
115: * expression evaluator can be instantiated through
116: * {@link #createFastExpressionEvaluator(String, Class, String[], ClassLoader)}. Expression
117: * evaluation is faster than through {@link #evaluate(Object[])}, because it is not done through
118: * reflection but through direct method invocation.
119: * <p>
120: * Example:
121: * <pre>
122: * public interface Foo {
123: * int bar(int a, int b);
124: * }
125: * ...
126: * Foo f = (Foo) ExpressionEvaluator.createFastExpressionEvaluator(
127: * "a + b", // expression to evaluate
128: * Foo.class, // interface that describes the expression's signature
129: * new String[] { "a", "b" }, // the parameters' names
130: * (ClassLoader) null // Use current thread's context class loader
131: * );
132: * System.out.println("1 + 2 = " + f.bar(1, 2)); // Evaluate the expression
133: * </pre>
134: * Notice: The <code>interfaceToImplement</code> must either be declared <code>public</code>,
135: * or with package scope in the root package (i.e. "no" package).
136: * <p>
137: * On my system (Intel P4, 2 GHz, MS Windows XP, JDK 1.4.1), expression "x + 1"
138: * evaluates as follows:
139: * <table>
140: * <tr><td></td><th>Server JVM</th><th>Client JVM</th></td></tr>
141: * <tr><td>Normal EE</td><td>23.7 ns</td><td>64.0 ns</td></tr>
142: * <tr><td>Fast EE</td><td>31.2 ns</td><td>42.2 ns</td></tr>
143: * </table>
144: * (How can it be that interface method invocation is slower than reflection for
145: * the server JVM?)
146: * <p>
147: * The expression may refer to a set of parameters with the given
148: * <code>parameterNames</code> and <code>parameterTypes</code>.
149: * <p>
150: * <code>parameterNames</code> and <code>parameterTypes</code> must have the
151: * same number of elements.
152: * <p>
153: * The parameters and/or the return value can be of primitive type, e.g.
154: * {@link Double#TYPE}.
155: * <p>
156: * The <code>optionalClassLoader</code> serves two purposes:
157: * <ul>
158: * <li>It is used to look for classes referenced by the script.
159: * <li>It is used to load the generated Java<sup>TM</sup> class
160: * into the JVM; directly if it is a subclass of {@link
161: * ByteArrayClassLoader}, or by creation of a temporary
162: * {@link ByteArrayClassLoader} if not.
163: * </ul>
164: * If the <code>optionalClassLoader</code> is <code>null</code>, then the
165: * current thread's context class loader is used.
166: * <p>
167: * A number of constructors exist that provide useful default values for
168: * the various parameters, or parse their script from a {@link String}
169: * instead of a {@link Scanner}. (You hardly want to use a scanner other than
170: * the default scanner.)
171: * <p>
172: * If the type of the expression is not fixed, you can pass a <code>null</code>
173: * <code>optionalExpressionType<code> argument; in this case, references are
174: * returned as {@link Object}s, and primitive values are wrapped in their
175: * wrapper classes.
176: * <p>
177: * If <code>optionalExpressionType</code> is {@link Void#TYPE}, then the
178: * expression must be an invocation of a <code>void</code> method.
179: */
180: public class ExpressionEvaluator extends ScriptEvaluator {
181: public static final Class ANY_TYPE = null;
182:
183: private Class[] optionalExpressionTypes = null;
184:
185: /**
186: * Equivalent to<pre>
187: * ExpressionEvaluator ee = new ExpressionEvaluator();
188: * ee.setExpressionType(expressionType);
189: * ee.setParameters(parameterNames, parameterTypes);
190: * ee.cook(expression);</pre>
191: *
192: * @see #ExpressionEvaluator()
193: * @see ExpressionEvaluator#setExpressionType(Class)
194: * @see ScriptEvaluator#setParameters(String[], Class[])
195: * @see Cookable#cook(String)
196: */
197: public ExpressionEvaluator(String expression, Class expressionType,
198: String[] parameterNames, Class[] parameterTypes)
199: throws CompileException, Parser.ParseException,
200: Scanner.ScanException {
201: this .setExpressionType(expressionType);
202: this .setParameters(parameterNames, parameterTypes);
203: this .cook(expression);
204: }
205:
206: /**
207: * Equivalent to<pre>
208: * ExpressionEvaluator ee = new ExpressionEvaluator();
209: * ee.setExpressionType(expressionType);
210: * ee.setParameters(parameterNames, parameterTypes);
211: * ee.setThrownExceptions(thrownExceptions);
212: * ee.setParentClassLoader(optionalParentClassLoader);
213: * ee.cook(expression);</pre>
214: *
215: * @see #ExpressionEvaluator()
216: * @see ExpressionEvaluator#setExpressionType(Class)
217: * @see ScriptEvaluator#setParameters(String[], Class[])
218: * @see ScriptEvaluator#setThrownExceptions(Class[])
219: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
220: * @see Cookable#cook(String)
221: */
222: public ExpressionEvaluator(String expression, Class expressionType,
223: String[] parameterNames, Class[] parameterTypes,
224: Class[] thrownExceptions,
225: ClassLoader optionalParentClassLoader)
226: throws CompileException, Parser.ParseException,
227: Scanner.ScanException {
228: this .setExpressionType(expressionType);
229: this .setParameters(parameterNames, parameterTypes);
230: this .setThrownExceptions(thrownExceptions);
231: this .setParentClassLoader(optionalParentClassLoader);
232: this .cook(expression);
233: }
234:
235: /**
236: * Equivalent to<pre>
237: * ExpressionEvaluator ee = new ExpressionEvaluator();
238: * ee.setExpressionType(expressionType);
239: * ee.setParameters(parameterNames, parameterTypes);
240: * ee.setThrownExceptions(thrownExceptions);
241: * ee.setExtendedType(optionalExtendedType);
242: * ee.setImplementedTypes(implementedTypes);
243: * ee.setParentClassLoader(optionalParentClassLoader);
244: * ee.cook(expression);</pre>
245: *
246: * @see #ExpressionEvaluator()
247: * @see ExpressionEvaluator#setExpressionType(Class)
248: * @see ScriptEvaluator#setParameters(String[], Class[])
249: * @see ScriptEvaluator#setThrownExceptions(Class[])
250: * @see ClassBodyEvaluator#setExtendedType(Class)
251: * @see ClassBodyEvaluator#setImplementedTypes(Class[])
252: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
253: * @see Cookable#cook(String)
254: */
255: public ExpressionEvaluator(String expression, Class expressionType,
256: String[] parameterNames, Class[] parameterTypes,
257: Class[] thrownExceptions, Class optionalExtendedType,
258: Class[] implementedTypes,
259: ClassLoader optionalParentClassLoader)
260: throws CompileException, Parser.ParseException,
261: Scanner.ScanException {
262: this .setExpressionType(expressionType);
263: this .setParameters(parameterNames, parameterTypes);
264: this .setThrownExceptions(thrownExceptions);
265: this .setExtendedType(optionalExtendedType);
266: this .setImplementedTypes(implementedTypes);
267: this .setParentClassLoader(optionalParentClassLoader);
268: this .cook(expression);
269: }
270:
271: /**
272: * Equivalent to<pre>
273: * ExpressionEvaluator ee = new ExpressionEvaluator();
274: * ee.setClassName(className);
275: * ee.setExtendedType(optionalExtendedType);
276: * ee.setImplementedTypes(implementedTypes);
277: * ee.setStaticMethod(staticMethod);
278: * ee.setExpressionType(expressionType);
279: * ee.setMethodName(methodName);
280: * ee.setParameters(parameterNames, parameterTypes);
281: * ee.setThrownExceptions(thrownExceptions);
282: * ee.setParentClassLoader(optionalParentClassLoader);
283: * ee.cook(scanner);
284: *
285: * @see #ExpressionEvaluator()
286: * @see ClassBodyEvaluator#setClassName(String)
287: * @see ClassBodyEvaluator#setExtendedType(Class)
288: * @see ClassBodyEvaluator#setImplementedTypes(Class[])
289: * @see ScriptEvaluator#setStaticMethod(boolean)
290: * @see ExpressionEvaluator#setExpressionType(Class)
291: * @see ScriptEvaluator#setMethodName(String)
292: * @see ScriptEvaluator#setParameters(String[], Class[])
293: * @see ScriptEvaluator#setThrownExceptions(Class[])
294: * @see SimpleCompiler#setParentClassLoader(ClassLoader)
295: * @see Cookable#cook(Scanner)
296: */
297: public ExpressionEvaluator(Scanner scanner, String className,
298: Class optionalExtendedType, Class[] implementedTypes,
299: boolean staticMethod, Class expressionType,
300: String methodName, String[] parameterNames,
301: Class[] parameterTypes, Class[] thrownExceptions,
302: ClassLoader optionalParentClassLoader)
303: throws Scanner.ScanException, Parser.ParseException,
304: CompileException, IOException {
305: this .setClassName(className);
306: this .setExtendedType(optionalExtendedType);
307: this .setImplementedTypes(implementedTypes);
308: this .setStaticMethod(staticMethod);
309: this .setExpressionType(expressionType);
310: this .setMethodName(methodName);
311: this .setParameters(parameterNames, parameterTypes);
312: this .setThrownExceptions(thrownExceptions);
313: this .setParentClassLoader(optionalParentClassLoader);
314: this .cook(scanner);
315: }
316:
317: public ExpressionEvaluator() {
318: }
319:
320: /**
321: * Define the type of the expression. The special type {@link #ANY_TYPE} allows the expression
322: * to return any type (primitive or reference).
323: * <p>
324: * Defaults to {@link #ANY_TYPE}.
325: */
326: public void setExpressionType(Class expressionType) {
327: this .setExpressionTypes(new Class[] { expressionType });
328: }
329:
330: public void setExpressionTypes(Class[] expressionTypes) {
331: this .optionalExpressionTypes = expressionTypes;
332:
333: Class[] returnTypes = new Class[expressionTypes.length];
334: for (int i = 0; i < returnTypes.length; ++i) {
335: Class et = expressionTypes[i];
336: returnTypes[i] = et == ANY_TYPE ? Object.class : et;
337: }
338: super .setReturnTypes(returnTypes);
339: }
340:
341: protected Class getDefaultReturnType() {
342: return Object.class;
343: }
344:
345: protected Java.Block makeBlock(int idx, Scanner scanner)
346: throws ParseException, ScanException, IOException {
347: Java.Block block = new Java.Block(scanner.location());
348:
349: Parser parser = new Parser(scanner);
350:
351: // Parse the expression.
352: Rvalue value = parser.parseExpression().toRvalueOrPE();
353:
354: Class et = this .optionalExpressionTypes == null ? ANY_TYPE
355: : this .optionalExpressionTypes[idx];
356: if (et == void.class) {
357:
358: // ExpressionEvaluator with an expression type "void" is a simple expression statement.
359: block.addStatement(new Java.ExpressionStatement(value));
360: } else {
361:
362: // Special case: Expression type "ANY_TYPE" means return type "Object" and automatic
363: // wrapping of primitive types.
364: if (et == ANY_TYPE) {
365: value = new Java.MethodInvocation(scanner.location(), // location
366: new Java.ReferenceType( // optionalTarget
367: scanner.location(), // location
368: new String[] { "org", "codehaus",
369: "janino", "util",
370: "PrimitiveWrapper" } // identifiers
371: ), "wrap", // methodName
372: new Java.Rvalue[] { value } // arguments
373: );
374:
375: // Make sure "PrimitiveWrapper" is compiled.
376: PrimitiveWrapper.wrap(99);
377:
378: // Add "PrimitiveWrapper" as an auxiliary class.
379: this .classToType(null, PrimitiveWrapper.class);
380: }
381:
382: // Add a return statement.
383: block.addStatement(new Java.ReturnStatement(scanner
384: .location(), value));
385: }
386: if (!scanner.peek().isEOF())
387: throw new Parser.ParseException("Unexpected token \""
388: + scanner.peek() + "\"", scanner.location());
389:
390: return block;
391: }
392:
393: /**
394: * Creates a "fast expression evaluator" from the given {@link java.lang.String}
395: * <code>expression</code>, generating a class with the {@link #DEFAULT_CLASS_NAME} that
396: * extends {@link Object}.
397: * <p>
398: * See the class description for an explanation of the "fast expression evaluator" concept.
399: *
400: * @see #createFastExpressionEvaluator(Scanner, String[], String, Class, Class, String[], ClassLoader)
401: * @see ExpressionEvaluator
402: */
403: public static Object createFastExpressionEvaluator(
404: String expression, Class interfaceToImplement,
405: String[] parameterNames,
406: ClassLoader optionalParentClassLoader)
407: throws CompileException, Parser.ParseException,
408: Scanner.ScanException {
409: ExpressionEvaluator ee = new ExpressionEvaluator();
410: ee.setParentClassLoader(optionalParentClassLoader);
411: return ScriptEvaluator.createFastEvaluator(ee, expression,
412: parameterNames, interfaceToImplement);
413: }
414:
415: /**
416: * Creates a "fast expression evaluator" from the given {@link Scanner} with no default
417: * imports.
418: * <p>
419: * See the class description for an explanation of the "fast expression evaluator" concept.
420: *
421: * @see #createFastExpressionEvaluator(Scanner, String[], String, Class, Class, String[], ClassLoader)
422: * @see ExpressionEvaluator
423: */
424: public static Object createFastExpressionEvaluator(Scanner scanner,
425: String className, Class optionalExtendedType,
426: Class interfaceToImplement, String[] parameterNames,
427: ClassLoader optionalParentClassLoader)
428: throws CompileException, Parser.ParseException,
429: Scanner.ScanException, IOException {
430: ExpressionEvaluator ee = new ExpressionEvaluator();
431: ee.setClassName(className);
432: ee.setExtendedType(optionalExtendedType);
433: ee.setParentClassLoader(optionalParentClassLoader);
434: return ScriptEvaluator.createFastEvaluator(ee, scanner,
435: parameterNames, interfaceToImplement);
436: }
437:
438: /**
439: * Creates a "fast expression evaluator".
440: * <p>
441: * See the class description for an explanation of the "fast expression evaluator" concept.
442: * <p>
443: * Notice: The <code>interfaceToImplement</code> must either be declared <code>public</code>,
444: * or with package scope in the same package as <code>className</code>.
445: *
446: * @param scanner Source of expression tokens
447: * @param optionalDefaultImports Default imports, e.g. <code>{ "java.util.Map", "java.io.*" }</code>
448: * @param className Name of generated class
449: * @param optionalExtendedType Class to extend
450: * @param interfaceToImplement Must declare exactly the one method that defines the expression's signature
451: * @param parameterNames The expression references the parameters through these names
452: * @param optionalParentClassLoader Used to load referenced classes, defaults to the current thread's "context class loader"
453: * @return an object that implements the given interface and extends the <code>optionalExtendedType</code>
454: * @see ExpressionEvaluator
455: */
456: public static Object createFastExpressionEvaluator(Scanner scanner,
457: String[] optionalDefaultImports, String className,
458: Class optionalExtendedType, Class interfaceToImplement,
459: String[] parameterNames,
460: ClassLoader optionalParentClassLoader)
461: throws CompileException, Parser.ParseException,
462: Scanner.ScanException, IOException {
463: ExpressionEvaluator ee = new ExpressionEvaluator();
464: ee.setClassName(className);
465: ee.setExtendedType(optionalExtendedType);
466: ee.setDefaultImports(optionalDefaultImports);
467: ee.setParentClassLoader(optionalParentClassLoader);
468: return ScriptEvaluator.createFastEvaluator(ee, scanner,
469: parameterNames, interfaceToImplement);
470: }
471: }
|