All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.codehaus.commons.compiler.IScriptEvaluator Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version

/*
 * 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. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 com.hazelcast.org.codehaus.commons.compiler;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.hazelcast.org.codehaus.commons.compiler.lang.ClassLoaders;
import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;

/**
 * An engine that executes a script in JVM bytecode.
 * 

* The syntax of the script to compile is a sequence of import declarations (not allowed if you compile many scripts * at a time, see below) followed by a sequence of statements, as defined in the Java Language Specification, Java SE 7 Edition, sections 7.5 and * 14. *

*

* An implementation may or may not implement the concept of "local methods", i.e. method declarations being freely * intermixed with statements. *

*

* Example: *

*
 *   import java.text.*;
 *
 *   System.out.println("HELLO");
 *   System.out.println(new DecimalFormat("####,###.##").format(a));
 * 
*

* (Notice that this expression refers to a parameter "a", as explained below.) *

*

* The script may complete abnormally, e.g. through a RETURN statement: *

*
 *   if (a == null) {
 *       System.out.println("Oops!");
 *       return;
 *   }
 * 
*

* Optionally, the script may be declared with a non-void return type. In this case, the last statement of the script * must be a RETURN statement (or a THROW statement), and all RETURN statements in the script must return a value * with the given type. *

*

* The script evaluator is implemented by creating and compiling a temporary compilation unit defining one class with * one method the body of which consists of the statements of the script. *

*

* To set up a {@link IScriptEvaluator} object, proceed as follows: *

*
    *
  1. Create an {@link IScriptEvaluator}-implementing class.
  2. *
  3. * Configure the {@link IScriptEvaluator} by calling any of the following methods: *
      *
    • {@link #setReturnType(Class)} *
    • {@link #setParameters(String[], Class[])} *
    • {@link #setThrownExceptions(Class[])} *
    • {@link ISimpleCompiler#setParentClassLoader(ClassLoader)} *
    • {@link IClassBodyEvaluator#setDefaultImports(String[])} *
    *
  4. *
  5. * Call any of the {@link com.hazelcast.org.codehaus.commons.compiler.Cookable#cook(Reader)} methods to scan, parse, compile and * load the script into the JVM. *
  6. *
*

* After the {@link IScriptEvaluator} object is created, the script can be executed as often with different parameter * values (see {@link #evaluate(Object[])}). This execution is very fast, compared to the compilation. *

*

* Less common methods exist that allow for the specification of the name of the generated class, the class it * extends, the interfaces it implements, the name of the method that executes the script, the exceptions that this * method (i.e. the script) is allowed to throw, and the {@link ClassLoader} that is used to define the generated * class and to load classes referenced by the script. *

*

* If you want to compile many scripts at the same time, you have the option to cook an array of scripts in * one {@link IScriptEvaluator} by using the following methods: *

*
    *
  • {@link #setMethodNames(String[])} *
  • {@link #setParameters(String[][], Class[][])} *
  • {@link #setReturnTypes(Class[])} *
  • {@link #setStaticMethod(boolean[])} *
  • {@link #setThrownExceptions(Class[][])} *
  • {@link #cook(Reader)} *
  • {@link #evaluate(int, Object[])} *
*

* Notice that these methods have array parameters in contrast to their one-script brethren. *

*/ public interface IScriptEvaluator extends ICookable, IMultiCookable { /** * The name of the generated method(s), if no custom method name is configured with {@link * #setMethodNames(String[])}. *

* The {@code '*'} in this string is replaced with the method index, starting at 0. *

*/ String DEFAULT_METHOD_NAME = "eval*"; /** * The return type for any script for which no return type is explicitly configured. */ Class DEFAULT_RETURN_TYPE = void.class; /** * The "parent class loader" is used to load referenced classes. Useful values are: * * * * * * * * * *
{@code System.getSystemClassLoader()}The running JVM's class path
{@code Thread.currentThread().getContextClassLoader()} or {@code null}The class loader effective for the invoking thread
{@link ClassLoaders#BOOTCLASSPATH_CLASS_LOADER}The running JVM's boot class path
*

* The parent class loader defaults to the current thread's context class loader. *

*/ void setParentClassLoader(@Nullable ClassLoader parentClassLoader); /** * Determines what kind of debugging information is included in the generates classes. The default is typically * "{@code -g:none}". */ void setDebuggingInformation(boolean debugSource, boolean debugLines, boolean debugVars); /** * By default, {@link CompileException}s are thrown on compile errors, but an application my install its own * {@link ErrorHandler}. *

* Be aware that a single problem during compilation often causes a bunch of compile errors, so a good {@link * ErrorHandler} counts errors and throws a {@link CompileException} when a limit is reached. *

*

* If the given {@link ErrorHandler} throws {@link CompileException}s, then the compilation is terminated and * the exception is propagated. *

*

* If the given {@link ErrorHandler} does not throw {@link CompileException}s, then the compiler may or may not * continue compilation, but must eventually throw a {@link CompileException}. *

*

* In other words: The {@link ErrorHandler} may throw a {@link CompileException} or not, but the compiler must * definitely throw a {@link CompileException} if one or more compile errors have occurred. *

* * @param compileErrorHandler {@code null} to restore the default behavior (throwing a {@link CompileException} */ void setCompileErrorHandler(@Nullable ErrorHandler compileErrorHandler); /** * By default, warnings are discarded, but an application my install a custom {@link WarningHandler}. * * @param warningHandler {@code null} to indicate that no warnings be issued */ void setWarningHandler(@Nullable WarningHandler warningHandler); /** @see IClassBodyEvaluator#setClassName(String) */ void setClassName(String className); /** @see IClassBodyEvaluator#setImplementedInterfaces(Class[]) */ void setImplementedInterfaces(Class[] implementedInterfaces); /** @see IClassBodyEvaluator#setExtendedClass(Class) */ void setExtendedClass(Class extendedClass); /** * When this {@link IScriptEvaluator} is coooked, then the defaultReturnType applies to all scripts for * which no explicit return type was configured. *

* The initial default return type (if you want, the "default-default" return type) is {@code void.class}. *

* * @see #setReturnType(Class) * @see #setReturnTypes(Class[]) */ void setDefaultReturnType(Class defaultReturnType); /** * @return The default return type that was previously configured with {@link #setDefaultReturnType(Class)}, or * {@link #DEFAULT_RETURN_TYPE} */ Class getDefaultReturnType(); /** * Defines whether the generated method overrides a methods declared in a supertype. */ void setOverrideMethod(boolean overrideMethod); /** * Defines whether the generated method should be STATIC or not. Defaults to {@code true}. */ void setStaticMethod(boolean staticMethod); /** * Defines the return type of the generated method. Value {@code null} means "use the default return type". * * @see #setDefaultReturnType(Class) */ void setReturnType(Class returnType); /** * Defines the name of the generated method. {@code null} means use a reasonable {@value #DEFAULT_METHOD_NAME}. */ void setMethodName(@Nullable String methodName); /** * Defines the names and types of the parameters of the generated method. *

* names{@code .length} and types{@code .length} must be equal. This invariant may be * checked immediately, or later when the script is cooked. *

*

* The parameters can be of primitive type, e.g. {@code double.class}. *

*

* The default is to have zero parameters. *

*/ void setParameters(String[] names, Class[] types); /** * Defines the exceptions that the generated method may throw. */ void setThrownExceptions(Class[] thrownExceptions); /** * Calls the script with concrete parameter values. *

* Each argument must have the same type as specified through the {@code parameterTypes} parameter of {@link * #setParameters(String[], Class[])}. *

*

* Arguments of primitive type must passed with their wrapper class objects. *

*

* The object returned has the class as specified through {@link #setReturnType(Class)}. *

*

* This method is thread-safe. *

* * @param arguments The actual parameter values * @throws IllegalStateException This IScriptEvaluator is not yet cooked */ @Nullable Object evaluate(@Nullable Object[] arguments) throws InvocationTargetException; /** * @return The generated and loaded {@link java.lang.reflect.Method} * @throws IllegalStateException This IScriptEvaluator is not yet cooked */ Method getMethod(); /** * Same as {@link #setOverrideMethod(boolean)}, but for multiple scripts. */ void setOverrideMethod(boolean[] overrideMethod); /** * Same as {@link #setStaticMethod(boolean)}, but for multiple scripts. */ void setStaticMethod(boolean[] staticMethod); /** * Configures the return types of the generated methods. If an element of the array is {@code null}, then use * the "default return type" for that script. * * @param returnTypes The methods' return types; {@code null} elements mean "use the default return * type" * @see #setDefaultReturnType(Class) * @see #getDefaultReturnType() */ void setReturnTypes(Class[] returnTypes); /** * Same as {@link #setMethodName(String)}, but for multiple scripts. *

* Define the names of the generated methods. By default the methods have distinct and implementation-specific * names. *

*

* If two scripts have the same name, then they must have different parameter types (see {@link * #setParameters(String[][], Class[][])}). *

*/ void setMethodNames(String[] methodNames); /** * Same as {@link #setParameters(String[], Class[])}, but for multiple scripts. */ void setParameters(String[][] names, Class[][] types); /** * Same as {@link #setThrownExceptions(Class[])}, but for multiple scripts. */ void setThrownExceptions(Class[][] thrownExceptions); /** * Same as {@link #cook(Reader)}, but for multiple scripts. */ @Override void cook(Reader... readers) throws CompileException, IOException; /** * Same as {@link #cook(String, Reader)}, 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 * {@code fileNames} parameter to distinguish between the individual token sources. *

* Iff the number of scanners is one, then that single script may contain leading IMPORT directives. *

s * * @throws IllegalStateException if any of the preceding {@code set...()} had an array * size different from that of {@code scanners} */ @Override void cook(String[] fileNames, Reader[] readers) throws CompileException, IOException; /** * Same as {@link #cook(String)}, but for multiple scripts. */ @Override void cook(String[] strings) throws CompileException; /** * Same as {@link #cook(String, String)}, but for multiple scripts. */ @Override void cook(String[] fileNames, String[] strings) throws CompileException; /** * Same as {@link #evaluate(Object[])}, but for multiple scripts. */ @Nullable Object evaluate(int idx, @Nullable Object[] arguments) throws InvocationTargetException; /** * Same as {@link #getMethod()}, but for multiple scripts. */ Method getMethod(int idx); /** * @param script Contains the sequence of script tokens * @see #createFastEvaluator(Reader, Class, String[]) */ T createFastEvaluator( String script, Class interfaceToImplement, String[] parameterNames ) throws CompileException; /** * If the parameter and return types of the expression are known at compile time, then a "fast" script evaluator * can be instantiated through this method. *

* Script evaluation is faster than through {@link #evaluate(Object[])}, because it is not done through * reflection but through direct method invocation. *

*

* Example: *

*
     * public interface Foo {
     *     int bar(int a, int b);
     * }
     * ...
     * IScriptEvaluator se = {@link CompilerFactoryFactory}.{@link
     * CompilerFactoryFactory#getDefaultCompilerFactory getDefaultCompilerFactory}().{@link
     * ICompilerFactory#newScriptEvaluator newScriptEvaluator}();
     *
     * // Optionally configure the SE her:
     * se.{@link #setClassName(String) setClassName}("Bar");
     * se.{@link #setDefaultImports(String[]) setDefaultImports}(new String[] { "java.util.*" });
     * se.{@link #setExtendedClass(Class) setExtendedClass}(SomeOtherClass.class);
     * se.{@link #setParentClassLoader(ClassLoader) setParentClassLoader}(someClassLoader);
     *
     * Foo f = (Foo) se.{@link #createFastEvaluator(String, Class, String[]) createFastScriptEvaluator}(
     *     "return a - b;",
     *     Foo.class,
     *     new String[] { "a", "b" }
     * );
     * System.out.println("1 - 2 = " + f.bar(1, 2));
     * 
*

* All other configuration (implemented type, static method, return type, method name, parameter names and types, * thrown exceptions) are predetermined by the {@code interfaceToImplement}. *

*

* Notice: The {@code interfaceToImplement} must either be declared {@code public}, or with package scope in the * same package as the generated class (see {@link #setClassName(String)}). *

s * * @param reader Produces the stream of script tokens * @param interfaceToImplement Must declare exactly one method * @param parameterNames The names of the parameters of that method * @return An object that implements the given interface */ T createFastEvaluator( Reader reader, Class interfaceToImplement, String[] parameterNames ) throws CompileException, IOException; /** @see IClassBodyEvaluator#setDefaultImports(String...) */ void setDefaultImports(String... defaultImports); /** @see IClassBodyEvaluator#getDefaultImports() */ String[] getDefaultImports(); /** @see IClassBodyEvaluator#getClazz() */ Class getClazz(); /** * @return The generated and loaded methods that implement the cooked scripts * @throws IllegalStateException This {@link IScriptEvaluator} is not yet cooked */ Method[] getResult(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy