com.hazelcast.org.codehaus.janino.ScriptEvaluator Maven / Gradle / Ivy
/*
* 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.janino;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.hazelcast.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.org.codehaus.commons.compiler.Cookable;
import com.hazelcast.org.codehaus.commons.compiler.ErrorHandler;
import com.hazelcast.org.codehaus.commons.compiler.IScriptEvaluator;
import com.hazelcast.org.codehaus.commons.compiler.InternalCompilerException;
import com.hazelcast.org.codehaus.commons.compiler.Location;
import com.hazelcast.org.codehaus.commons.compiler.MultiCookable;
import com.hazelcast.org.codehaus.commons.compiler.WarningHandler;
import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;
import com.hazelcast.org.codehaus.janino.Java.AbstractClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.AbstractCompilationUnit.ImportDeclaration;
import com.hazelcast.org.codehaus.janino.Java.Atom;
import com.hazelcast.org.codehaus.janino.Java.BlockStatement;
import com.hazelcast.org.codehaus.janino.Java.CompilationUnit;
import com.hazelcast.org.codehaus.janino.Java.ExpressionStatement;
import com.hazelcast.org.codehaus.janino.Java.LocalClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.LocalClassDeclarationStatement;
import com.hazelcast.org.codehaus.janino.Java.LocalVariableDeclarationStatement;
import com.hazelcast.org.codehaus.janino.Java.MethodDeclarator;
import com.hazelcast.org.codehaus.janino.Java.Modifier;
import com.hazelcast.org.codehaus.janino.Java.Primitive;
import com.hazelcast.org.codehaus.janino.Java.PrimitiveType;
import com.hazelcast.org.codehaus.janino.Java.Type;
import com.hazelcast.org.codehaus.janino.Java.VariableDeclarator;
import com.hazelcast.org.codehaus.janino.Parser.ClassDeclarationContext;
import com.hazelcast.org.codehaus.janino.Parser.MethodDeclarationContext;
import com.hazelcast.org.codehaus.janino.util.AbstractTraverser;
/**
* An implementation of {@link IScriptEvaluator} that utilizes the JANINO Java compiler.
*
* This implementation implements the concept of "Local methods", i.e. statements may be freely intermixed with
* method declarations. These methods are typically called by the "main code" of the script evaluator. One limitation
* exists: When cooking multiple scripts in one {@link ScriptEvaluator}, then local method signatures
* (names and parameter types) must not collide between scripts.
*
*
* A plethora of "convenience constructors" exist that execute the setup steps instantly. Their use is discouraged,
* in favor of using the default constructor, plus calling a number of setters, and then one of the {@code cook()}
* methods.
*
*/
public
class ScriptEvaluator extends MultiCookable implements IScriptEvaluator {
private int sourceVersion = -1;
@Nullable private WarningHandler warningHandler;
private final ClassBodyEvaluator cbe = new ClassBodyEvaluator();
/**
* Represents one script that this {@link ScriptEvaluator} declares. Typically there exactly one such
* script, but there can be two or more - see {@link ScriptEvaluator#ScriptEvaluator()}.
*/
class Script {
/**
* Whether the generated method overrides a method declared by a supertype; defaults to {@code false}.
*/
protected boolean overrideMethod;
/**
* Whether the method is generated {@code static}; defaults to {@code true}.
*/
protected boolean staticMethod = true;
/**
* The generated method's return type. {@code null} means "use the default return type".
*
* @see ScriptEvaluator#setDefaultReturnType(Class)
*/
@Nullable protected Class> returnType;
/**
* The name of the generated method.
*/
private String methodName;
private String[] parameterNames = new String[0];
private Class>[] parameterTypes = new Class>[0];
private Class>[] thrownExceptions = new Class>[0];
Script(String methodName) { this.methodName = methodName; }
}
/**
* The scripts to compile. Is initialized on the first call to {@link #setStaticMethod(boolean[])} or one of its
* friends.
*/
@Nullable private Script[] scripts;
private Class> defaultReturnType = IScriptEvaluator.DEFAULT_RETURN_TYPE;
@Override public void
setParentClassLoader(@Nullable ClassLoader parentClassLoader) { this.cbe.setParentClassLoader(parentClassLoader); }
@Override public void
setDebuggingInformation(boolean debugSource, boolean debugLines, boolean debugVars) {
this.cbe.setDebuggingInformation(debugSource, debugLines, debugVars);
}
@Override public void
setSourceVersion(int version) {
this.cbe.setSourceVersion(version);
this.sourceVersion = version;
}
@Override public void
setTargetVersion(int version) { this.cbe.setTargetVersion(version); }
@Override public void
setCompileErrorHandler(@Nullable ErrorHandler compileErrorHandler) {
this.cbe.setCompileErrorHandler(compileErrorHandler);
}
@Override public void
setWarningHandler(@Nullable WarningHandler warningHandler) {
this.cbe.setWarningHandler(warningHandler);
this.warningHandler = warningHandler;
}
/**
* @return A reference to the currently effective compilation options; changes to it take
* effect immediately
*/
public EnumSet
options() { return this.cbe.options(); }
/**
* Sets the options for all future compilations.
*/
public ScriptEvaluator
options(EnumSet options) {
this.cbe.options(options);
return this;
}
/**
* @throws IllegalArgumentException count is different from previous invocations of
* this method
*/
public void
setScriptCount(int count) {
Script[] ss = this.scripts;
if (ss == null) {
this.scripts = (ss = new Script[count]);
for (int i = 0; i < count; i++) {
ss[i] = new Script(IScriptEvaluator.DEFAULT_METHOD_NAME.replace("*", Integer.toString(i)));
}
} else {
if (count != ss.length) {
throw new IllegalArgumentException(
"Inconsistent script count; previously " + ss.length + ", now " + count
);
}
}
}
private Script
getScript(int index) {
if (this.scripts != null) return this.scripts[index];
throw new IllegalStateException("\"getScript()\" invoked before \"setScriptCount()\"");
}
/**
* 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(parentClassLoader);
* se.cook(fileName, is);
*
*
* @see #ScriptEvaluator()
* @see #setReturnType(Class)
* @see #setParameters(String[], Class[])
* @see #setThrownExceptions(Class[])
* @see SimpleCompiler#setParentClassLoader(ClassLoader)
* @see Cookable#cook(String, InputStream)
*/
public
ScriptEvaluator(
@Nullable String fileName,
InputStream is,
Class> returnType,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
@Nullable ClassLoader parentClassLoader // null = use current thread's context class loader
) throws CompileException, IOException {
this.setReturnType(returnType);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(fileName, is);
}
/**
* Equivalent to
*
* ScriptEvaluator se = new ScriptEvaluator();
* se.setReturnType(returnType);
* se.setParameters(parameterNames, parameterTypes);
* se.setThrownExceptions(thrownExceptions);
* se.setParentClassLoader(parentClassLoader);
* 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(
@Nullable String fileName,
Reader reader,
Class> returnType,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
@Nullable ClassLoader parentClassLoader // null = use current thread's context class loader
) throws CompileException, IOException {
this.setReturnType(returnType);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(fileName, reader);
}
/**
* Equivalent to
*
* ScriptEvaluator se = new ScriptEvaluator();
* se.setReturnType(returnType);
* se.setParameters(parameterNames, parameterTypes);
* se.setThrownExceptions(thrownExceptions);
* se.setParentClassLoader(parentClassLoader);
* 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,
@Nullable ClassLoader parentClassLoader // null = use current thread's context class loader
) throws CompileException, IOException {
this.setReturnType(returnType);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(scanner);
}
/**
* Equivalent to
*
* ScriptEvaluator se = new ScriptEvaluator();
* se.setExtendedType(extendedType);
* se.setImplementedTypes(implementedTypes);
* se.setReturnType(returnType);
* se.setParameters(parameterNames, parameterTypes);
* se.setThrownExceptions(thrownExceptions);
* se.setParentClassLoader(parentClassLoader);
* 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,
@Nullable Class> extendedType,
Class>[] implementedTypes,
Class> returnType,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
@Nullable ClassLoader parentClassLoader // null = use current thread's context class loader
) throws CompileException, IOException {
this.setExtendedClass(extendedType);
this.setImplementedInterfaces(implementedTypes);
this.setReturnType(returnType);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(scanner);
}
/**
* Equivalent to
*
* ScriptEvaluator se = new ScriptEvaluator();
* se.setClassName(className);
* se.setExtendedType(extendedType);
* se.setImplementedTypes(implementedTypes);
* se.setStaticMethod(staticMethod);
* se.setReturnType(returnType);
* se.setMethodName(methodName);
* se.setParameters(parameterNames, parameterTypes);
* se.setThrownExceptions(thrownExceptions);
* se.setParentClassLoader(parentClassLoader);
* 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,
@Nullable Class> extendedType,
Class>[] implementedTypes,
boolean staticMethod,
Class> returnType,
String methodName,
String[] parameterNames,
Class>[] parameterTypes,
Class>[] thrownExceptions,
@Nullable ClassLoader parentClassLoader // null = use current thread's context class loader
) throws CompileException, IOException {
this.setClassName(className);
this.setExtendedClass(extendedType);
this.setImplementedInterfaces(implementedTypes);
this.setStaticMethod(staticMethod);
this.setReturnType(returnType);
this.setMethodName(methodName);
this.setParameters(parameterNames, parameterTypes);
this.setThrownExceptions(thrownExceptions);
this.setParentClassLoader(parentClassLoader);
this.cook(scanner);
}
/**
* Constructs a script evaluator with all the default settings.
*/
public
ScriptEvaluator() {}
/**
* Constructs a script evaluator with the given number of scripts.
*
* The argument of all following invocations of
* {@link #setMethodNames(String[])},
* {@link #setOverrideMethod(boolean[])},
* {@link #setParameters(String[][], Class[][])},
* {@link #setReturnTypes(Class[])},
* {@link #setStaticMethod(boolean[])},
* {@link #setThrownExceptions(Class[][])},
* {@link #cook(Parser[])},
* {@link #cook(Reader[])},
* {@link #cook(Scanner[])},
* {@link #cook(String[])},
* {@link #cook(String[], Reader[])} and
* {@link #cook(String[], String[])}
* must be arrays with exactly that length.
*
*
* If a different constructor is used, then the first invocation of one of the above method implicitly sets the
* script count.
*
*/
public ScriptEvaluator(int count) { this.setScriptCount(count); }
@Override public void
setClassName(String className) { this.cbe.setClassName(className); }
@Override public void
setImplementedInterfaces(Class>[] implementedTypes) { this.cbe.setImplementedInterfaces(implementedTypes); }
@Override public void
setExtendedClass(@Nullable Class> extendedType) { this.cbe.setExtendedClass(extendedType); }
@Override public void
setDefaultReturnType(Class> defaultReturnType) { this.defaultReturnType = defaultReturnType; }
@Override public Class>
getDefaultReturnType() { return this.defaultReturnType; }
// ================= SINGLE SCRIPT CONFIGURATION SETTERS =================
@Override public void
setOverrideMethod(boolean overrideMethod) { this.setOverrideMethod(new boolean[] { overrideMethod }); }
@Override public void
setStaticMethod(boolean staticMethod) { this.setStaticMethod(new boolean[] { staticMethod }); }
@Override public void
setReturnType(@Nullable Class> returnType) { this.setReturnTypes(new Class[] { returnType }); }
@Override public void
setMethodName(@Nullable 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 });
}
// ================= MULTIPLE SCRIPT CONFIGURATION SETTERS =================
@Override public void
setOverrideMethod(boolean[] overrideMethod) {
this.setScriptCount(overrideMethod.length);
for (int i = 0; i < overrideMethod.length; i++) this.getScript(i).overrideMethod = overrideMethod[i];
}
@Override public void
setStaticMethod(boolean[] staticMethod) {
this.setScriptCount(staticMethod.length);
for (int i = 0; i < staticMethod.length; i++) this.getScript(i).staticMethod = staticMethod[i];
}
@Override public void
setReturnTypes(Class>[] returnTypes) {
this.setScriptCount(returnTypes.length);
for (int i = 0; i < returnTypes.length; i++) {
this.getScript(i).returnType = returnTypes[i];
}
}
@Override public void
setMethodNames(String[] methodNames) {
this.setScriptCount(methodNames.length);
for (int i = 0; i < methodNames.length; i++) this.getScript(i).methodName = methodNames[i];
}
@Override public void
setParameters(String[][] parameterNames, Class>[][] parameterTypes) {
this.setScriptCount(parameterNames.length);
this.setScriptCount(parameterTypes.length);
for (int i = 0; i < parameterNames.length; i++) {
final Script script = this.getScript(i);
script.parameterNames = (String[]) parameterNames[i].clone();
script.parameterTypes = (Class[]) parameterTypes[i].clone();
}
}
@Override public void
setThrownExceptions(Class>[][] thrownExceptions) {
this.setScriptCount(thrownExceptions.length);
for (int i = 0; i < thrownExceptions.length; i++) this.getScript(i).thrownExceptions = thrownExceptions[i];
}
// ---------------------------------------------------------------
@Override public void
cook(@Nullable String fileName, Reader reader) throws CompileException, IOException {
this.cook(new Scanner(fileName, reader));
}
/**
* 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[] fileNames, Reader[] readers) throws CompileException, IOException {
this.setScriptCount(fileNames.length);
this.setScriptCount(readers.length);
Scanner[] scanners = new Scanner[readers.length];
for (int i = 0; i < readers.length; ++i) scanners[i] = new Scanner(fileNames[i], readers[i]);
this.cook(scanners);
}
/**
* 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
* fileName 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 preceding {@code set...()} had an array size different from that of
* {@code scanners}
*/
public final void
cook(Scanner... scanners) throws CompileException, IOException {
this.setScriptCount(scanners.length);
Parser[] parsers = new Parser[scanners.length];
for (int i = 0; i < scanners.length; ++i) {
parsers[i] = new Parser(scanners[i]);
parsers[i].setSourceVersion(this.sourceVersion);
parsers[i].setWarningHandler(this.warningHandler);
}
this.cook(parsers);
}
/**
* @see #cook(Scanner[])
*/
public final void
cook(Parser[] parsers) throws CompileException, IOException {
int count = parsers.length;
this.setScriptCount(count);
final Parser parser = count == 1 ? parsers[0] : null;
// Create compilation unit.
Java.AbstractCompilationUnit.ImportDeclaration[]
importDeclarations = this.parseImports(parser);
Java.BlockStatement[][] statementss = new Java.BlockStatement[count][];
Java.MethodDeclarator[][] localMethodss = new Java.MethodDeclarator[count][];
// Create methods with one block each.
for (int i = 0; i < count; ++i) {
// Create the statements of the method.
List statements = new ArrayList();
List localMethods = new ArrayList();
this.makeStatements(i, parsers[i], statements, localMethods);
statementss[i] = (BlockStatement[]) statements.toArray(new Java.BlockStatement[statements.size()]);
localMethodss[i] = (MethodDeclarator[]) localMethods.toArray(new Java.MethodDeclarator[localMethods.size()]); // SUPPRESS CHECKSTYLE LineLength
}
this.cook(
parsers.length >= 1 ? parsers[0].getScanner().getFileName() : null, // fileName
importDeclarations,
statementss,
localMethodss
);
}
void
cook(
@Nullable String fileName,
ImportDeclaration[] importDeclarations,
Java.BlockStatement[][] statementss,
Java.MethodDeclarator[][] localMethodss
) throws CompileException {
int count = statementss.length;
Collection methodDeclarators = new ArrayList();
for (int i = 0; i < count; i++) {
Script es = this.getScript(i);
Java.BlockStatement[] statements = statementss[i];
Java.MethodDeclarator[] localMethods = localMethodss[i];
final Location loc = statements.length == 0 ? Location.NOWHERE : statements[0].getLocation();
Class> rt = es.returnType;
if (rt == null) rt = this.getDefaultReturnType();
methodDeclarators.add(this.makeMethodDeclaration(
loc, // location
( // annotations
es.overrideMethod
? new Java.Annotation[] { new Java.MarkerAnnotation(this.classToType(loc, Override.class)) }
: new Java.Annotation[0]
),
es.staticMethod, // staticMethod
rt, // returnType
es.methodName, // methodName
es.parameterTypes, // parameterTypes
es.parameterNames, // parameterNames
es.thrownExceptions, // thrownExceptions
statements // statements
));
// Also add the "local methods" that a script my declare.
for (MethodDeclarator lm : localMethods) methodDeclarators.add(lm);
}
this.cook(new Java.CompilationUnit(fileName, importDeclarations), methodDeclarators);
}
public final void
cook(CompilationUnit compilationUnit, Collection methodDeclarators)
throws CompileException {
// Create class declaration.
final AbstractClassDeclaration
cd = this.cbe.addPackageMemberClassDeclaration(
((MethodDeclarator) methodDeclarators.iterator().next()).getLocation(),
compilationUnit
);
for (MethodDeclarator md : methodDeclarators) cd.addDeclaredMethod(md);
this.cook(compilationUnit);
}
Java.AbstractCompilationUnit.ImportDeclaration[]
parseImports(@Nullable Parser parser) throws CompileException, IOException {
return this.cbe.makeImportDeclarations(parser);
}
@Override public Method[]
getResult() { return this.getMethods(); }
/**
* @return The generated methods
* @throws IllegalStateException The {@link ScriptEvaluator} has not yet be cooked
*/
Method[]
getMethods() {
Method[] result = this.getMethodsCache;
if (result != null) return result;
final Class> c = this.getClazz();
// Find the script methods by name and parameter types.
assert this.scripts != null;
int count = this.scripts.length;
// Clear the generated methods.
result = new Method[count];
// "Class.getDeclaredMethod(name, parameterTypes)" is slow when the class declares MANY methods (say, in
// the thousands). So let's use "Class.getDeclaredMethods()" instead.
// Create a (temporary) mapping of method key to method index.
Map dms = new HashMap