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

View File

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