Skip to content

Commit

Permalink
Big rewrite of the ExpressionEvaluator - ScriptEvaluator - ClassBodyE…
Browse files Browse the repository at this point in the history
…valuator - SimpleCompiler - Cookable hierarchy:

Previously, these classes EXTENDED from left to right, which was always a bit awkward: Actually an ExpressionEvaluator IS NOT a ScriptEvaluator (asf.).
To clean up that mess, the classes now DELEGATE instead of EXTENDING: ExpressionEvaluator now HAS a ScriptEvaluator (asf.). This works much better than the old model, and is so much cleaner.

Strictly speaking, this is an INCOMPATIBLE CHANGE. However I am very sure that nobody ever (intentionally) RELIES on the old class hierarchy, e.g. assigns an ExpressionEvaluator object to a ScriptEvaluator variable.

Also, the "trick" to NOT load the generated classes immediately, but process them differently (the "ExpressionCompiler") now works differently... need to update the documentation to accomodate for this.
  • Loading branch information
aunkrig committed Dec 16, 2019
1 parent 64fd1f5 commit 9e0c16c
Show file tree
Hide file tree
Showing 17 changed files with 1,680 additions and 596 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
public
class ClassBodyEvaluator extends SimpleCompiler implements IClassBodyEvaluator {

@Nullable private String[] optionalDefaultImports;
private String[] defaultImports = new String[0];
private String className = IClassBodyEvaluator.DEFAULT_CLASS_NAME;
@Nullable private Class<?> optionalExtendedType;
private Class<?>[] implementedTypes = new Class[0];
Expand All @@ -73,9 +73,10 @@ class ClassBodyEvaluator extends SimpleCompiler implements IClassBodyEvaluator {
setClassName(String className) { this.className = className; }

@Override public void
setDefaultImports(@Nullable String... optionalDefaultImports) {
this.optionalDefaultImports = optionalDefaultImports;
}
setDefaultImports(String... defaultImports) { this.defaultImports = defaultImports.clone(); }

@Override public String[]
getDefaultImports() { return this.defaultImports.clone(); }

@Override public void
setExtendedClass(@Nullable Class<?> optionalExtendedType) { this.optionalExtendedType = optionalExtendedType; }
Expand Down Expand Up @@ -137,8 +138,8 @@ class ClassBodyEvaluator extends SimpleCompiler implements IClassBodyEvaluator {
}

// Print default imports.
if (this.optionalDefaultImports != null) {
for (String defaultImport : this.optionalDefaultImports) {
if (this.defaultImports != null) {
for (String defaultImport : this.defaultImports) {
pw.print("import ");
pw.print(defaultImport);
pw.println(";");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,18 @@
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.Cookable;
import org.codehaus.commons.compiler.ErrorHandler;
import org.codehaus.commons.compiler.IExpressionEvaluator;
import org.codehaus.commons.compiler.IScriptEvaluator;
import org.codehaus.commons.compiler.InternalCompilerException;
import org.codehaus.commons.compiler.MultiCookable;
import org.codehaus.commons.compiler.WarningHandler;
import org.codehaus.commons.nullanalysis.Nullable;

/**
Expand Down Expand Up @@ -83,7 +91,13 @@
* </p>
*/
public
class ExpressionEvaluator extends ScriptEvaluator implements IExpressionEvaluator {
class ExpressionEvaluator extends MultiCookable implements IExpressionEvaluator {

private final ScriptEvaluator se = new ScriptEvaluator();
{
this.se.setClassName(IExpressionEvaluator.DEFAULT_CLASS_NAME);
this.se.setDefaultReturnType(IExpressionEvaluator.DEFAULT_EXPRESSION_TYPE);
}

/**
* Equivalent to
Expand Down Expand Up @@ -189,37 +203,94 @@ class ExpressionEvaluator extends ScriptEvaluator implements IExpressionEvaluato

public ExpressionEvaluator() {}

/**
* @deprecated Must not be used on an {@link IExpressionEvaluator}; use {@link #setExpressionType(Class)} instead
*/
@Deprecated @Override public void
setReturnType(Class<?> expressionType) { super.setReturnType(expressionType); }
// ============================= CONFIGURATION SETTS AND GETTERS =============================

/**
* @deprecated Must not be used on an {@link IExpressionEvaluator}; use {@link #setExpressionTypes(Class[])}
* instead
*/
@Deprecated @Override public void
setReturnTypes(Class<?>[] expressionTypes) { super.setReturnTypes(expressionTypes); }
@Override public void
setParentClassLoader(@Nullable ClassLoader parentClassLoader) {
this.se.setParentClassLoader(parentClassLoader);
}

@Override public void
setExpressionType(Class<?> expressionType) {
this.setExpressionTypes(new Class<?>[] { expressionType });
setDebuggingInformation(boolean debugSource, boolean debugLines, boolean debugVars) {
this.se.setDebuggingInformation(debugSource, debugLines, debugVars);
}

@Override public void
setExpressionTypes(Class<?>[] expressionTypes) {
super.setReturnTypes(expressionTypes);
setCompileErrorHandler(@Nullable ErrorHandler compileErrorHandler) {
this.se.setCompileErrorHandler(compileErrorHandler);
}

/**
* The default return type of an expression is {@code Object}{@code .class}.
*/
@Override protected Class<?>
getDefaultReturnType() { return Object.class; }
@Override public void
setWarningHandler(@Nullable WarningHandler warningHandler) {
this.se.setWarningHandler(warningHandler);
}

@Override public void
setDefaultImports(String... defaultImports) { this.se.setDefaultImports(defaultImports); }

@Override public String[]
getDefaultImports() { return this.se.getDefaultImports(); }

@Override public void
setDefaultExpressionType(Class<?> defaultExpressionType) { this.se.setDefaultReturnType(defaultExpressionType); }

@Override public Class<?>
getDefaultExpressionType() { return this.se.getDefaultReturnType(); }

@Override public void
setImplementedInterfaces(Class<?>[] implementedTypes) { this.se.setImplementedInterfaces(implementedTypes); }

@Override public void
setReturnType(Class<?> returnType) { this.se.setReturnType(returnType); }

@Override public void
setExpressionType(Class<?> expressionType) { this.se.setReturnType(expressionType); }

@Override public void
setExpressionTypes(Class<?>[] expressionTypes) { this.se.setReturnTypes(expressionTypes); }

@Override public void
setOverrideMethod(boolean overrideMethod) { this.se.setOverrideMethod(overrideMethod); }

@Override public void
setOverrideMethod(boolean[] overrideMethod) { this.se.setOverrideMethod(overrideMethod); }

@Override public void
setParameters(String[] parameterNames, Class<?>[] parameterTypes) {
this.se.setParameters(parameterNames, parameterTypes);
}

@Override public void
setParameters(String[][] parameterNames, Class<?>[][] parameterTypes) {
this.se.setParameters(parameterNames, parameterTypes);
}

@Override public void
setClassName(String className) { this.se.setClassName(className); }

@Override public void
setExtendedClass(@Nullable Class<?> extendedType) { this.se.setExtendedClass(extendedType); }

@Override public void
setStaticMethod(boolean staticMethod) { this.se.setStaticMethod(staticMethod); }

@Override public void
setStaticMethod(boolean[] staticMethod) { this.se.setStaticMethod(staticMethod); }

@Override public void
setMethodName(String methodName) { this.se.setMethodName(methodName); }

@Override public void
setMethodNames(String[] methodNames) { this.se.setMethodNames(methodNames); }

@Override public void
setThrownExceptions(Class<?>[] thrownExceptions) { this.se.setThrownExceptions(thrownExceptions); }

@Override public void
setThrownExceptions(Class<?>[][] thrownExceptions) { this.se.setThrownExceptions(thrownExceptions); }

@Override public void
cook(@Nullable String[] optionalFileNames, Reader[] readers) throws CompileException, IOException {
cook(String[] fileNames, Reader[] readers) throws CompileException, IOException {

readers = readers.clone(); // Don't modify the argument array.

Expand All @@ -238,7 +309,7 @@ public ExpressionEvaluator() {}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);

returnTypes[i] = this.getReturnType(i);
returnTypes[i] = this.se.getReturnType(i);
if (returnTypes[i] != void.class && returnTypes[i] != Void.class) {
pw.print("return ");
}
Expand All @@ -248,7 +319,95 @@ public ExpressionEvaluator() {}
pw.close();
readers[i] = new StringReader(sw.toString());
}
super.setReturnTypes(returnTypes);
this.cook(optionalFileNames, readers, imports);
// super.setReturnTypes(returnTypes);
this.se.cook(fileNames, readers, imports);
}

@Override @Nullable public Object
evaluate(@Nullable Object[] arguments) throws InvocationTargetException { return this.se.evaluate(arguments); }

@Override @Nullable public Object
evaluate(int idx, @Nullable Object[] arguments) throws InvocationTargetException {
return this.se.evaluate(idx, arguments);
}

@Override public Method
getMethod() { return this.se.getMethod(); }

@Override public Method
getMethod(int idx) { return this.se.getMethod(idx); }

@Override public Class<?>
getClazz() { return this.se.getClazz(); }

@Override public Method[]
getResult() { return this.se.getResult(); }

@Override public Map<String, byte[]>
getBytecodes() { return this.se.getBytecodes(); }

@Override public <T> T
createFastEvaluator(String script, Class<T> interfaceToImplement, String... parameterNames) throws CompileException {
try {
return this.createFastEvaluator(
new StringReader(script),
interfaceToImplement,
parameterNames
);
} catch (IOException ex) {
throw new InternalCompilerException("IOException despite StringReader", ex);
}
}

/**
* Notice: This method is not declared in {@link IScriptEvaluator}, and is hence only available in <em>this</em>
* implementation of {@code 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[])
*/
@Override public <T> T
createFastEvaluator(Reader reader, Class<T> interfaceToImplement, String... parameterNames)
throws CompileException, IOException {

if (!interfaceToImplement.isInterface()) {
throw new InternalCompilerException("\"" + interfaceToImplement + "\" is not an interface");
}

Method methodToImplement;
{
Method[] methods = interfaceToImplement.getDeclaredMethods();
if (methods.length != 1) {
throw new InternalCompilerException(
"Interface \""
+ interfaceToImplement
+ "\" must declare exactly one method"
);
}
methodToImplement = methods[0];
}

this.setImplementedInterfaces(new Class[] { interfaceToImplement });
this.setOverrideMethod(true);
this.setStaticMethod(false);
this.setExpressionType(methodToImplement.getReturnType());
this.setMethodName(methodToImplement.getName());
this.setParameters(parameterNames, methodToImplement.getParameterTypes());
this.setThrownExceptions(methodToImplement.getExceptionTypes());
this.cook(reader);

@SuppressWarnings("unchecked") Class<? extends T>
actualClass = (Class<? extends T>) this.getMethod().getDeclaringClass();

try {
return actualClass.newInstance();
} catch (InstantiationException e) {
// SNO - Declared class is always non-abstract.
throw new InternalCompilerException(e.toString(), e);
} catch (IllegalAccessException e) {
// SNO - interface methods are always PUBLIC.
throw new InternalCompilerException(e.toString(), e);
}
}
}

This file was deleted.

Loading

0 comments on commit 9e0c16c

Please sign in to comment.