LUCENE-6417: Make JavascriptCompiler completely stateless (thanks to visitor pattern)

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1694876 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2015-08-09 12:10:30 +00:00
parent 1e82854532
commit 4b3268aaf4
1 changed files with 14 additions and 26 deletions

View File

@ -19,7 +19,6 @@ package org.apache.lucene.expressions.js;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
@ -37,6 +36,7 @@ import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.expressions.js.JavascriptParser.ExpressionContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.util.IOUtils;
import org.objectweb.asm.ClassWriter;
@ -45,8 +45,6 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import static org.apache.lucene.expressions.js.JavascriptParser.ExpressionContext;
/**
* An expression compiler for javascript expressions.
* <p>
@ -74,7 +72,7 @@ import static org.apache.lucene.expressions.js.JavascriptParser.ExpressionContex
*
* @lucene.experimental
*/
public class JavascriptCompiler {
public final class JavascriptCompiler {
static final class Loader extends ClassLoader {
Loader(ClassLoader parent) {
super(parent);
@ -111,10 +109,6 @@ public class JavascriptCompiler {
private static final int MAX_SOURCE_LENGTH = 16384;
final String sourceText;
final Map<String, Integer> externalsMap = new LinkedHashMap<>();
final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
GeneratorAdapter gen;
final Map<String,Method> functions;
/**
@ -189,19 +183,18 @@ public class JavascriptCompiler {
* @throws ParseException on failure to compile
*/
private Expression compileExpression(ClassLoader parent) throws ParseException {
final Map<String, Integer> externalsMap = new LinkedHashMap<>();
final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
generateClass(getAntlrParseTree(), classWriter, externalsMap);
try {
ParseTree parseTree = getAntlrParseTree();
beginCompile();
internalCompile(parseTree);
endCompile();
final Class<? extends Expression> evaluatorClass = new Loader(parent)
.define(COMPILED_EXPRESSION_CLASS, classWriter.toByteArray());
final Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
return constructor.newInstance(sourceText, externalsMap.keySet().toArray(new String[externalsMap.size()]));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) {
} catch (ReflectiveOperationException exception) {
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + sourceText + ").", exception);
}
}
@ -229,16 +222,16 @@ public class JavascriptCompiler {
}
}
private void beginCompile() {
private void generateClass(ParseTree parseTree, ClassWriter classWriter, Map<String, Integer> externalsMap) {
classWriter.visit(CLASSFILE_VERSION,
Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
COMPILED_EXPRESSION_INTERNAL,
null, EXPRESSION_TYPE.getInternalName(), null);
String clippedSourceText = (sourceText.length() <= MAX_SOURCE_LENGTH) ?
final String clippedSourceText = (sourceText.length() <= MAX_SOURCE_LENGTH) ?
sourceText : (sourceText.substring(0, MAX_SOURCE_LENGTH - 3) + "...");
classWriter.visitSource(clippedSourceText, null);
GeneratorAdapter constructor = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
final GeneratorAdapter constructor = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
EXPRESSION_CTOR, null, null, classWriter);
constructor.loadThis();
constructor.loadArgs();
@ -246,13 +239,10 @@ public class JavascriptCompiler {
constructor.returnValue();
constructor.endMethod();
gen = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
final GeneratorAdapter gen = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
EVALUATE_METHOD, null, null, classWriter);
}
// internalCompile is used to create an anonymous inner class around the ANTLR listener
// to completely hide the implementation details of expression compilation
private void internalCompile(ParseTree parseTree) {
// to completely hide the ANTLR visitor we use an anonymous impl:
new JavascriptBaseVisitor<Void>() {
private final Deque<Type> typeStack = new ArrayDeque<>();
@ -674,9 +664,7 @@ public class JavascriptCompiler {
}
}
}.visit(parseTree);
}
private void endCompile() {
gen.returnValue();
gen.endMethod();