/* * Janino - An embedded Java[TM] compiler * * Copyright (c) 2001-2010, Arno Unkrig * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the * following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.codehaus.janino; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.codehaus.commons.compiler.CompileException; import org.codehaus.commons.compiler.Cookable; import org.codehaus.commons.compiler.IExpressionEvaluator; import org.codehaus.commons.compiler.IScriptEvaluator; import org.codehaus.commons.compiler.Location; import org.codehaus.janino.Java.VariableDeclarator; import org.codehaus.janino.util.Traverser; /** A number of "convenience constructors" exist that execute the setup steps instantly. Their use is discouraged. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class ScriptEvaluator extends ClassBodyEvaluator implements IScriptEvaluator { /** Whether methods override a method declared by a supertype; {@code null} means "none". */ protected boolean[] optionalOverrideMethod; /** Whether methods are static; {@code null} means "all". */ protected boolean[] optionalStaticMethod; /** The methods' return types; {@code null} means "none". */ protected Class[] optionalReturnTypes; private String[] optionalMethodNames; private String[][] optionalParameterNames; private Class[][] optionalParameterTypes; private Class[][] optionalThrownExceptions; private Method[] result; // null=uncooked /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.cook(script);
* * @see #ScriptEvaluator() * @see Cookable#cook(String) */ public ScriptEvaluator(String script) throws CompileException { this.cook(script); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setReturnType(returnType);
     * se.cook(script);
* * @see #ScriptEvaluator() * @see #setReturnType(Class) * @see Cookable#cook(String) */ public ScriptEvaluator(String script, Class returnType) throws CompileException { this.setReturnType(returnType); this.cook(script); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setReturnType(returnType);
     * se.setParameters(parameterNames, parameterTypes);
     * se.cook(script);
* * @see #ScriptEvaluator() * @see #setReturnType(Class) * @see #setParameters(String[], Class[]) * @see Cookable#cook(String) */ public ScriptEvaluator(String script, Class returnType, String[] parameterNames, Class[] parameterTypes) throws CompileException { this.setReturnType(returnType); this.setParameters(parameterNames, parameterTypes); this.cook(script); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setReturnType(returnType);
     * se.setParameters(parameterNames, parameterTypes);
     * se.setThrownExceptions(thrownExceptions);
     * se.cook(script);
* * @see #ScriptEvaluator() * @see #setReturnType(Class) * @see #setParameters(String[], Class[]) * @see #setThrownExceptions(Class[]) * @see Cookable#cook(String) */ public ScriptEvaluator( String script, Class returnType, String[] parameterNames, Class[] parameterTypes, Class[] thrownExceptions ) throws CompileException { this.setReturnType(returnType); this.setParameters(parameterNames, parameterTypes); this.setThrownExceptions(thrownExceptions); this.cook(script); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setReturnType(returnType);
     * se.setParameters(parameterNames, parameterTypes);
     * se.setThrownExceptions(thrownExceptions);
     * se.setParentClassLoader(optionalParentClassLoader);
     * se.cook(optionalFileName, is);
* * @see #ScriptEvaluator() * @see #setReturnType(Class) * @see #setParameters(String[], Class[]) * @see #setThrownExceptions(Class[]) * @see SimpleCompiler#setParentClassLoader(ClassLoader) * @see Cookable#cook(String, InputStream) */ public ScriptEvaluator( String optionalFileName, InputStream is, Class returnType, String[] parameterNames, Class[] parameterTypes, Class[] thrownExceptions, ClassLoader optionalParentClassLoader // null = use current thread's context class loader ) throws CompileException, IOException { this.setReturnType(returnType); this.setParameters(parameterNames, parameterTypes); this.setThrownExceptions(thrownExceptions); this.setParentClassLoader(optionalParentClassLoader); this.cook(optionalFileName, is); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setReturnType(returnType);
     * se.setParameters(parameterNames, parameterTypes);
     * se.setThrownExceptions(thrownExceptions);
     * se.setParentClassLoader(optionalParentClassLoader);
     * se.cook(reader);
* * @see #ScriptEvaluator() * @see #setReturnType(Class) * @see #setParameters(String[], Class[]) * @see #setThrownExceptions(Class[]) * @see SimpleCompiler#setParentClassLoader(ClassLoader) * @see Cookable#cook(String, Reader) */ public ScriptEvaluator( String optionalFileName, Reader reader, Class returnType, String[] parameterNames, Class[] parameterTypes, Class[] thrownExceptions, ClassLoader optionalParentClassLoader // null = use current thread's context class loader ) throws CompileException, IOException { this.setReturnType(returnType); this.setParameters(parameterNames, parameterTypes); this.setThrownExceptions(thrownExceptions); this.setParentClassLoader(optionalParentClassLoader); this.cook(optionalFileName, reader); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setReturnType(returnType);
     * se.setParameters(parameterNames, parameterTypes);
     * se.setThrownExceptions(thrownExceptions);
     * se.setParentClassLoader(optionalParentClassLoader);
     * se.cook(scanner);
* * @see #ScriptEvaluator() * @see #setReturnType(Class) * @see #setParameters(String[], Class[]) * @see #setThrownExceptions(Class[]) * @see SimpleCompiler#setParentClassLoader(ClassLoader) * @see Cookable#cook(Reader) */ public ScriptEvaluator( Scanner scanner, Class returnType, String[] parameterNames, Class[] parameterTypes, Class[] thrownExceptions, ClassLoader optionalParentClassLoader // null = use current thread's context class loader ) throws CompileException, IOException { this.setReturnType(returnType); this.setParameters(parameterNames, parameterTypes); this.setThrownExceptions(thrownExceptions); this.setParentClassLoader(optionalParentClassLoader); this.cook(scanner); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setExtendedType(optionalExtendedType);
     * se.setImplementedTypes(implementedTypes);
     * se.setReturnType(returnType);
     * se.setParameters(parameterNames, parameterTypes);
     * se.setThrownExceptions(thrownExceptions);
     * se.setParentClassLoader(optionalParentClassLoader);
     * se.cook(scanner);
* * @see #ScriptEvaluator() * @see ClassBodyEvaluator#setExtendedClass(Class) * @see ClassBodyEvaluator#setImplementedInterfaces(Class[]) * @see #setReturnType(Class) * @see #setParameters(String[], Class[]) * @see #setThrownExceptions(Class[]) * @see SimpleCompiler#setParentClassLoader(ClassLoader) * @see Cookable#cook(Reader) */ public ScriptEvaluator( Scanner scanner, Class optionalExtendedType, Class[] implementedTypes, Class returnType, String[] parameterNames, Class[] parameterTypes, Class[] thrownExceptions, ClassLoader optionalParentClassLoader // null = use current thread's context class loader ) throws CompileException, IOException { this.setExtendedClass(optionalExtendedType); this.setImplementedInterfaces(implementedTypes); this.setReturnType(returnType); this.setParameters(parameterNames, parameterTypes); this.setThrownExceptions(thrownExceptions); this.setParentClassLoader(optionalParentClassLoader); this.cook(scanner); } /** * Equivalent to
     * ScriptEvaluator se = new ScriptEvaluator();
     * se.setClassName(className);
     * se.setExtendedType(optionalExtendedType);
     * se.setImplementedTypes(implementedTypes);
     * se.setStaticMethod(staticMethod);
     * se.setReturnType(returnType);
     * se.setMethodName(methodName);
     * se.setParameters(parameterNames, parameterTypes);
     * se.setThrownExceptions(thrownExceptions);
     * se.setParentClassLoader(optionalParentClassLoader);
     * se.cook(scanner);
* * @see #ScriptEvaluator() * @see ClassBodyEvaluator#setClassName(String) * @see ClassBodyEvaluator#setExtendedClass(Class) * @see ClassBodyEvaluator#setImplementedInterfaces(Class[]) * @see #setStaticMethod(boolean) * @see #setReturnType(Class) * @see #setMethodName(String) * @see #setParameters(String[], Class[]) * @see #setThrownExceptions(Class[]) * @see SimpleCompiler#setParentClassLoader(ClassLoader) * @see Cookable#cook(Reader) */ public ScriptEvaluator( Scanner scanner, String className, Class optionalExtendedType, Class[] implementedTypes, boolean staticMethod, Class returnType, String methodName, String[] parameterNames, Class[] parameterTypes, Class[] thrownExceptions, ClassLoader optionalParentClassLoader // null = use current thread's context class loader ) throws CompileException, IOException { this.setClassName(className); this.setExtendedClass(optionalExtendedType); this.setImplementedInterfaces(implementedTypes); this.setStaticMethod(staticMethod); this.setReturnType(returnType); this.setMethodName(methodName); this.setParameters(parameterNames, parameterTypes); this.setThrownExceptions(thrownExceptions); this.setParentClassLoader(optionalParentClassLoader); this.cook(scanner); } /** * Constructs a script evaluator with all the default settings (return type {@code void} */ public ScriptEvaluator() {} @Override public void setOverrideMethod(boolean overrideMethod) { this.setOverrideMethod(new boolean[] { overrideMethod }); } @Override public void setStaticMethod(boolean staticMethod) { this.setStaticMethod(new boolean[] { staticMethod }); } /** * Defines the return types of the generated methods. * * @param returnType The method's return type; {@code null} means the "default return type", which is the type * returned by {@link #getDefaultReturnType()} ({@code void.class} for {@link ScriptEvaluator} * and {@code Object.class} for {@link ExpressionEvaluator}) * @see ScriptEvaluator#getDefaultReturnType() * @see ExpressionEvaluator#getDefaultReturnType() */ @Override public void setReturnType(Class returnType) { this.setReturnTypes(new Class[] { returnType }); } @Override public void setMethodName(String methodName) { this.setMethodNames(new String[] { methodName }); } @Override public void setParameters(String[] parameterNames, Class[] parameterTypes) { this.setParameters(new String[][] { parameterNames }, new Class[][] { parameterTypes }); } @Override public void setThrownExceptions(Class[] thrownExceptions) { this.setThrownExceptions(new Class[][] { thrownExceptions }); } @Override public final void cook(Scanner scanner) throws CompileException, IOException { this.cook(new Scanner[] { scanner }); } @Override public Object evaluate(Object[] arguments) throws InvocationTargetException { return this.evaluate(0, arguments); } @Override public Method getMethod() { return this.getMethod(0); } @Override public void setOverrideMethod(boolean[] overrideMethod) { this.assertNotCooked(); this.optionalOverrideMethod = overrideMethod.clone(); } @Override public void setStaticMethod(boolean[] staticMethod) { this.assertNotCooked(); this.optionalStaticMethod = staticMethod.clone(); } /** * Defines the return types of the generated methods. * * @param returnTypes The methods' return types; {@code null} elements mean the "default return type", which is the * type returned by {@link #getDefaultReturnType()} ({@code void.class} for {@link * ScriptEvaluator} and {@code Object.class} for {@link ExpressionEvaluator}) * @see ScriptEvaluator#getDefaultReturnType() * @see ExpressionEvaluator#getDefaultReturnType() */ @Override public void setReturnTypes(Class[] returnTypes) { this.assertNotCooked(); this.optionalReturnTypes = returnTypes.clone(); } @Override public void setMethodNames(String[] methodNames) { this.assertNotCooked(); this.optionalMethodNames = methodNames.clone(); } @Override public void setParameters(String[][] parameterNames, Class[][] parameterTypes) { this.assertNotCooked(); this.optionalParameterNames = parameterNames.clone(); this.optionalParameterTypes = parameterTypes.clone(); } @Override public void setThrownExceptions(Class[][] thrownExceptions) { this.assertNotCooked(); this.optionalThrownExceptions = thrownExceptions.clone(); } /** * Like {@link #cook(Scanner)}, but cooks a set of scripts into one class. Notice that * if any of the scripts causes trouble, the entire compilation will fail. If you * need to report which of the scripts causes the exception, you may want to use the * optionalFileName argument of {@link Scanner#Scanner(String, Reader)} to * distinguish between the individual token sources. *

* On a 2 GHz Intel Pentium Core Duo under Windows XP with an IBM 1.4.2 JDK, compiling * 10000 expressions "a + b" (integer) takes about 4 seconds and 56 MB of main memory. * The generated class file is 639203 bytes large. *

* The number and the complexity of the scripts is restricted by the * Limitations * of the Java Virtual Machine, where the most limiting factor is the 64K entries limit * of the constant pool. Since every method with a distinct name requires one entry there, * you can define at best 32K (very simple) scripts. * * If and only if the number of scanners is one, then that single script may contain leading * IMPORT directives. * * @throws IllegalStateException Any of the preceeding set...() had an array size different from that * of scanners */ public final void cook(Scanner[] scanners) throws CompileException, IOException { if (scanners == null) throw new NullPointerException(); Parser[] parsers = new Parser[scanners.length]; for (int i = 0; i < scanners.length; ++i) { parsers[i] = new Parser(scanners[i]); } this.cook(parsers); } /** @see #cook(Scanner[]) */ public final void cook(Parser[] parsers) throws CompileException, IOException { // The "dimension" of this ScriptEvaluator, i.e. how many scripts are cooked at the same // time. int count = parsers.length; // Check array sizes. if (this.optionalMethodNames != null && this.optionalMethodNames.length != count) { throw new IllegalStateException("methodName count"); } if (this.optionalParameterNames != null && this.optionalParameterNames.length != count) { throw new IllegalStateException("parameterNames count"); } if (this.optionalParameterTypes != null && this.optionalParameterTypes.length != count) { throw new IllegalStateException("parameterTypes count"); } if (this.optionalOverrideMethod != null && this.optionalOverrideMethod.length != count) { throw new IllegalStateException("overrideMethod count"); } if (this.optionalReturnTypes != null && this.optionalReturnTypes.length != count) { throw new IllegalStateException("returnTypes count"); } if (this.optionalStaticMethod != null && this.optionalStaticMethod.length != count) { throw new IllegalStateException("staticMethod count"); } if (this.optionalThrownExceptions != null && this.optionalThrownExceptions.length != count) { throw new IllegalStateException("thrownExceptions count"); } // Create compilation unit. Java.CompilationUnit compilationUnit = this.makeCompilationUnit(count == 1 ? parsers[0] : null); // Create class declaration. Java.ClassDeclaration cd = this.addPackageMemberClassDeclaration(parsers[0].location(), compilationUnit); // Determine method names. String[] methodNames; if (this.optionalMethodNames == null) { methodNames = new String[count]; for (int i = 0; i < count; ++i) methodNames[i] = "eval" + i; } else { methodNames = this.optionalMethodNames; } // Create methods with one block each. for (int i = 0; i < count; ++i) { Parser parser = parsers[i]; List statements = this.makeStatements(i, parser); // Determine the following script properties AFTER the call to "makeBlock()", // because "makeBlock()" may modify these script properties on-the-fly. boolean staticMethod = this.optionalStaticMethod == null || this.optionalStaticMethod[i]; boolean overrideMethod = this.optionalOverrideMethod != null && this.optionalOverrideMethod[i]; Class returnType = ( this.optionalReturnTypes == null ? this.getDefaultReturnType() : this.optionalReturnTypes[i] ); String[] parameterNames = ( this.optionalParameterNames == null ? new String[0] : this.optionalParameterNames[i] ); Class[] parameterTypes = ( this.optionalParameterTypes == null ? new Class[0] : this.optionalParameterTypes[i] ); Class[] thrownExceptions = ( this.optionalThrownExceptions == null ? new Class[0] : this.optionalThrownExceptions[i] ); // If the method is non-static, assume that it overrides a method in a supertype. Location loc = parser.location(); cd.addDeclaredMethod(this.makeMethodDeclaration( loc, // location ( // annotations overrideMethod ? new Java.Annotation[] { new Java.MarkerAnnotation(this.classToType(loc, Override.class)) } : new Java.Annotation[0] ), staticMethod, // staticMethod returnType, // returnType methodNames[i], // methodName parameterTypes, // parameterTypes parameterNames, // parameterNames thrownExceptions, // thrownExceptions statements // statements )); } // Compile and load the compilation unit. Class c = this.compileToClass(compilationUnit); // Find the script methods by name. this.result = new Method[count]; if (count <= 10) { for (int i = 0; i < count; ++i) { try { this.result[i] = c.getDeclaredMethod( methodNames[i], this.optionalParameterTypes == null ? new Class[0] : this.optionalParameterTypes[i] ); } catch (NoSuchMethodException ex) { throw new JaninoRuntimeException(( "SNO: Loaded class does not declare method \"" + methodNames[i] + "\"" ), ex); } } } else { class MethodWrapper { private final String name; private final Class[] parameterTypes; MethodWrapper(String name, Class[] parameterTypes) { this.name = name; this.parameterTypes = parameterTypes; } @Override public boolean equals(Object o) { if (!(o instanceof MethodWrapper)) return false; MethodWrapper that = (MethodWrapper) o; if (!this.name.equals(that.name)) return false; int cnt = this.parameterTypes.length; if (cnt != that.parameterTypes.length) return false; for (int i = 0; i < cnt; ++i) { if (!this.parameterTypes[i].equals(that.parameterTypes[i])) return false; } return true; } @Override public int hashCode() { int hc = this.name.hashCode(); for (Class parameterType : this.parameterTypes) hc ^= parameterType.hashCode(); return hc; } } Method[] ma = c.getDeclaredMethods(); Map dms = new HashMap(2 * count); for (Method m : ma) dms.put(new MethodWrapper(m.getName(), m.getParameterTypes()), m); for (int i = 0; i < count; ++i) { Method m = (Method) dms.get(new MethodWrapper( methodNames[i], this.optionalParameterTypes == null ? new Class[0] : this.optionalParameterTypes[i] )); if (m == null) { throw new JaninoRuntimeException( "SNO: Loaded class does not declare method \"" + methodNames[i] + "\"" ); } this.result[i] = m; } } } @Override public final void cook(Reader[] readers) throws CompileException, IOException { this.cook(new String[readers.length], readers); } /** * On a 2 GHz Intel Pentium Core Duo under Windows XP with an IBM 1.4.2 JDK, compiling * 10000 expressions "a + b" (integer) takes about 4 seconds and 56 MB of main memory. * The generated class file is 639203 bytes large. *

* The number and the complexity of the scripts is restricted by the * Limitations * of the Java Virtual Machine, where the most limiting factor is the 64K entries limit * of the constant pool. Since every method with a distinct name requires one entry there, * you can define at best 32K (very simple) scripts. */ @Override public final void cook(String[] optionalFileNames, Reader[] readers) throws CompileException, IOException { Scanner[] scanners = new Scanner[readers.length]; for (int i = 0; i < readers.length; ++i) { scanners[i] = new Scanner(optionalFileNames == null ? null : optionalFileNames[i], readers[i]); } this.cook(scanners); } @Override public final void cook(String[] strings) throws CompileException { this.cook(null, strings); } @Override public final void cook(String[] optionalFileNames, String[] strings) throws CompileException { Reader[] readers = new Reader[strings.length]; for (int i = 0; i < strings.length; ++i) readers[i] = new StringReader(strings[i]); try { this.cook(optionalFileNames, readers); } catch (IOException ex) { throw new JaninoRuntimeException("SNO: IOException despite StringReader", ex); } } /** * @return {@code void.class} * @see #setReturnTypes(Class[]) */ protected Class getDefaultReturnType() { return void.class; } /** Fills the given block by parsing statements until EOF and adding them to the block. */ protected List makeStatements(int idx, Parser parser) throws CompileException, IOException { List statements = new ArrayList(); while (!parser.peekEof()) { statements.add(parser.parseBlockStatement()); } return statements; } /** * To the given {@link Java.ClassDeclaration}, add *

* * @param returnType Return type of the declared method */ protected Java.MethodDeclarator makeMethodDeclaration( Location location, Java.Annotation[] annotations, boolean staticMethod, Class returnType, String methodName, Class[] parameterTypes, String[] parameterNames, Class[] thrownExceptions, List statements ) { if (parameterNames.length != parameterTypes.length) { throw new JaninoRuntimeException( "Lengths of \"parameterNames\" (" + parameterNames.length + ") and \"parameterTypes\" (" + parameterTypes.length + ") do not match" ); } Java.FunctionDeclarator.FormalParameters fps = new Java.FunctionDeclarator.FormalParameters( location, new Java.FunctionDeclarator.FormalParameter[parameterNames.length], false ); for (int i = 0; i < fps.parameters.length; ++i) { fps.parameters[i] = new Java.FunctionDeclarator.FormalParameter( location, // location true, // finaL this.classToType(location, parameterTypes[i]), // type parameterNames[i] // name ); } return new Java.MethodDeclarator( location, // location null, // optionalDocComment new Java.Modifiers( // modifiers staticMethod ? (short) (Mod.PUBLIC | Mod.STATIC) : (short) Mod.PUBLIC, annotations ), this.classToType(location, returnType), // type methodName, // name fps, // formalParameters this.classesToTypes(location, thrownExceptions), // thrownExceptions statements // optionalStatements ); } /** * @deprecated Use {@link #createFastScriptEvaluator(Scanner, String[], String, Class, Class, String[], * ClassLoader)} instead */ @Deprecated public static Object createFastScriptEvaluator(String script, Class interfaceToImplement, String[] parameterNames) throws CompileException { ScriptEvaluator se = new ScriptEvaluator(); return se.createFastEvaluator(script, interfaceToImplement, parameterNames); } /** * @deprecated Use {@link #createFastScriptEvaluator(Scanner, String[], String, Class, Class, String[], * ClassLoader)} instead */ @Deprecated public static Object createFastScriptEvaluator( Scanner scanner, Class interfaceToImplement, String[] parameterNames, ClassLoader optionalParentClassLoader ) throws CompileException, IOException { ScriptEvaluator se = new ScriptEvaluator(); se.setParentClassLoader(optionalParentClassLoader); return se.createFastEvaluator(scanner, interfaceToImplement, parameterNames); } /** * @deprecated Use {@link #createFastScriptEvaluator(Scanner, String[], String, Class, Class, String[], * ClassLoader)} instead */ @Deprecated public static Object createFastScriptEvaluator( Scanner scanner, String className, Class optionalExtendedType, Class interfaceToImplement, String[] parameterNames, ClassLoader optionalParentClassLoader ) throws CompileException, IOException { ScriptEvaluator se = new ScriptEvaluator(); se.setClassName(className); se.setExtendedClass(optionalExtendedType); se.setParentClassLoader(optionalParentClassLoader); return se.createFastEvaluator(scanner, interfaceToImplement, parameterNames); } /** *
     * {@link ScriptEvaluator} se = new {@link ScriptEvaluator#ScriptEvaluator() ScriptEvaluator}();
     * se.{@link #setDefaultImports(String[]) setDefaultImports}.(optionalDefaultImports);
     * se.{@link #setClassName(String) setClassName}.(className);
     * se.{@link #setExtendedClass(Class) setExtendedClass}.(optionalExtendedClass);
     * se.{@link #setParentClassLoader(ClassLoader) setParentClassLoader}(optionalParentClassLoader);
     * return se.{@link #createFastEvaluator(Scanner, Class, String[]) createFastEvaluator}(scanner,
     * interfaceToImplement, parameterNames);
     * 
* * @deprecated Use {@link #createFastEvaluator(Scanner,Class,String[])} instead: */ @Deprecated public static Object createFastScriptEvaluator( Scanner scanner, String[] optionalDefaultImports, String className, Class optionalExtendedClass, Class interfaceToImplement, String[] parameterNames, ClassLoader optionalParentClassLoader ) throws CompileException, IOException { ScriptEvaluator se = new ScriptEvaluator(); se.setDefaultImports(optionalDefaultImports); se.setClassName(className); se.setExtendedClass(optionalExtendedClass); se.setParentClassLoader(optionalParentClassLoader); return se.createFastEvaluator(scanner, interfaceToImplement, parameterNames); } /** Don't use. */ @Override public final Object createInstance(Reader reader) { throw new UnsupportedOperationException("createInstance"); } @Override public Object createFastEvaluator(Reader reader, Class interfaceToImplement, String[] parameterNames) throws CompileException, IOException { return this.createFastEvaluator(new Scanner(null, reader), interfaceToImplement, parameterNames); } @Override public Object createFastEvaluator(String script, Class interfaceToImplement, String[] parameterNames) throws CompileException { try { return this.createFastEvaluator( new StringReader(script), interfaceToImplement, parameterNames ); } catch (IOException ex) { throw new JaninoRuntimeException("IOException despite StringReader", ex); } } /** * Notice: This method is not declared in {@link IScriptEvaluator}, and is hence only available in this * implementation of org.codehaus.commons.compiler. To be independent from this particular * implementation, try to switch to {@link #createFastEvaluator(Reader, Class, String[])}. * * @param scanner Source of tokens to read * @see #createFastEvaluator(Reader, Class, String[]) */ public Object createFastEvaluator(Scanner scanner, Class interfaceToImplement, String[] parameterNames) throws CompileException, IOException { if (!interfaceToImplement.isInterface()) { throw new JaninoRuntimeException("\"" + interfaceToImplement + "\" is not an interface"); } Method methodToImplement; { Method[] methods = interfaceToImplement.getDeclaredMethods(); if (methods.length != 1) { throw new JaninoRuntimeException( "Interface \"" + interfaceToImplement + "\" must declare exactly one method" ); } methodToImplement = methods[0]; } this.setImplementedInterfaces(new Class[] { interfaceToImplement }); this.setOverrideMethod(true); this.setStaticMethod(false); if (this instanceof IExpressionEvaluator) { // Must not call "IExpressionEvaluator.setReturnType()". ((IExpressionEvaluator) this).setExpressionType(methodToImplement.getReturnType()); } else { this.setReturnType(methodToImplement.getReturnType()); } this.setMethodName(methodToImplement.getName()); this.setParameters(parameterNames, methodToImplement.getParameterTypes()); this.setThrownExceptions(methodToImplement.getExceptionTypes()); this.cook(scanner); Class c = this.getMethod().getDeclaringClass(); try { return c.newInstance(); } catch (InstantiationException e) { // SNO - Declared class is always non-abstract. throw new JaninoRuntimeException(e.toString(), e); } catch (IllegalAccessException e) { // SNO - interface methods are always PUBLIC. throw new JaninoRuntimeException(e.toString(), e); } } /** * Guess the names of the parameters used in the given expression. The strategy is to look * at all "ambiguous names" in the expression (e.g. in "a.b.c.d()", the ambiguous name * is "a.b.c"), and then at the components of the ambiguous name. * * * @see Scanner#Scanner(String, Reader) */ public static String[] guessParameterNames(Scanner scanner) throws CompileException, IOException { Parser parser = new Parser(scanner); // Eat optional leading import declarations. while (parser.peek("import")) parser.parseImportDeclaration(); // Parse the script statements into a block. Java.Block block = new Java.Block(scanner.location()); while (!parser.peekEof()) block.addStatement(parser.parseBlockStatement()); // Traverse the block for ambiguous names and guess which of them are parameter names. final Set localVariableNames = new HashSet(); final Set parameterNames = new HashSet(); new Traverser() { @Override public void traverseLocalVariableDeclarationStatement(Java.LocalVariableDeclarationStatement lvds) { for (VariableDeclarator vd : lvds.variableDeclarators) localVariableNames.add(vd.name); super.traverseLocalVariableDeclarationStatement(lvds); } @Override public void traverseAmbiguousName(Java.AmbiguousName an) { // If any of the components starts with an upper-case letter, then the ambiguous // name is most probably a type name, e.g. "System.out" or "java.lang.System.out". for (int i = 0; i < an.identifiers.length; ++i) { if (Character.isUpperCase(an.identifiers[i].charAt(0))) return; } // Is it a local variable's name? if (localVariableNames.contains(an.identifiers[0])) return; // It's most probably a parameter name (although it could be a field name as well). parameterNames.add(an.identifiers[0]); } }.traverseBlock(block); return (String[]) parameterNames.toArray(new String[parameterNames.size()]); } @Override public Object evaluate(int idx, Object[] arguments) throws InvocationTargetException { if (this.result == null) throw new IllegalStateException("Must only be called after \"cook()\""); try { return this.result[idx].invoke(null, arguments); } catch (IllegalAccessException ex) { throw new JaninoRuntimeException(ex.toString(), ex); } } @Override public Method getMethod(int idx) { if (this.result == null) throw new IllegalStateException("Must only be called after \"cook()\""); return this.result[idx]; } }