You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2013/09/13 05:11:18 UTC
svn commit: r1522767 [2/5] - in /lucene/dev/branches/lucene5207:
dev-tools/scripts/ lucene/ lucene/core/src/java/org/apache/lucene/util/
lucene/core/src/test/org/apache/lucene/util/ lucene/expressions/
lucene/expressions/src/ lucene/expressions/src/jav...
Added: lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java?rev=1522767&view=auto
==============================================================================
--- lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java (added)
+++ lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java Fri Sep 13 03:11:17 2013
@@ -0,0 +1,630 @@
+package org.apache.lucene.expressions.js;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.CharStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.tree.Tree;
+import org.apache.lucene.expressions.Expression;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import static org.objectweb.asm.Opcodes.AALOAD;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.BIPUSH;
+import static org.objectweb.asm.Opcodes.D2I;
+import static org.objectweb.asm.Opcodes.D2L;
+import static org.objectweb.asm.Opcodes.DADD;
+import static org.objectweb.asm.Opcodes.DCMPG;
+import static org.objectweb.asm.Opcodes.DCMPL;
+import static org.objectweb.asm.Opcodes.DCONST_0;
+import static org.objectweb.asm.Opcodes.DCONST_1;
+import static org.objectweb.asm.Opcodes.DDIV;
+import static org.objectweb.asm.Opcodes.DNEG;
+import static org.objectweb.asm.Opcodes.DREM;
+import static org.objectweb.asm.Opcodes.DRETURN;
+import static org.objectweb.asm.Opcodes.DSUB;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.I2D;
+import static org.objectweb.asm.Opcodes.I2L;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
+import static org.objectweb.asm.Opcodes.ICONST_2;
+import static org.objectweb.asm.Opcodes.ICONST_3;
+import static org.objectweb.asm.Opcodes.ICONST_4;
+import static org.objectweb.asm.Opcodes.ICONST_5;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.IFGE;
+import static org.objectweb.asm.Opcodes.IFGT;
+import static org.objectweb.asm.Opcodes.IFLE;
+import static org.objectweb.asm.Opcodes.IFLT;
+import static org.objectweb.asm.Opcodes.IFNE;
+import static org.objectweb.asm.Opcodes.ILOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.L2D;
+import static org.objectweb.asm.Opcodes.L2I;
+import static org.objectweb.asm.Opcodes.LAND;
+import static org.objectweb.asm.Opcodes.LCONST_0;
+import static org.objectweb.asm.Opcodes.LCONST_1;
+import static org.objectweb.asm.Opcodes.LOR;
+import static org.objectweb.asm.Opcodes.LSHL;
+import static org.objectweb.asm.Opcodes.LSHR;
+import static org.objectweb.asm.Opcodes.LUSHR;
+import static org.objectweb.asm.Opcodes.LXOR;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.SIPUSH;
+import static org.objectweb.asm.Opcodes.V1_7;
+
+/**
+ * An expression compiler for javascript expressions.
+ *
+ * @lucene.experimental
+ */
+public class JavascriptCompiler {
+ private static enum ComputedType {
+ INT, LONG, DOUBLE
+ }
+
+ class Loader extends ClassLoader {
+
+ Loader(ClassLoader parent) {
+ super(parent);
+ }
+
+ public Class<? extends Expression> define(String className, byte[] bytecode) {
+ return super.defineClass(className, bytecode, 0, bytecode.length).asSubclass(Expression.class);
+ }
+ }
+
+ private static final String EXPRESSION_CLASS_PREFIX = JavascriptCompiler.class.getPackage().getName() + ".Computed_";
+ private static final String EXPRESSION_INTERNAL_PREFIX = JavascriptCompiler.class.getPackage().getName().replace(".", "/") + "/Computed_";
+ private static final String COMPILED_EXPRESSION_INTERNAL = Type.getInternalName(Expression.class);
+ private static final String FUNCTION_VALUES_INTERNAL = Type.getInternalName(FunctionValues.class);
+
+ private Loader loader;
+ private AtomicLong counter = new AtomicLong();
+
+ private String className;
+ private ClassWriter classWriter;
+ private MethodVisitor methodVisitor;
+ private Map<String, Integer> externalsMap;
+ private List<String> externalsList;
+
+ /**
+ * Constructs a compiler for expressions.
+ */
+ public JavascriptCompiler() {
+ this(null);
+ }
+
+ /**
+ * Constructs a compiler for expressions that will be loaded using the given class loader as the parent.
+ * @param parent Class loader to load the dynamically compiled expression
+ */
+ public JavascriptCompiler(ClassLoader parent) {
+ if (parent == null) {
+ parent = getClass().getClassLoader();
+ }
+ loader = new Loader(parent);
+ }
+
+ /**
+ * Compiles the given expression.
+ *
+ * @param expression The expression to compile
+ * @return A new compiled expression
+ * @throws ParseException on failure to compile
+ */
+ public static Expression compile(String expression) throws ParseException {
+ return new JavascriptCompiler().compileExpression(expression);
+ }
+
+ /**
+ * Compiles the given expression.
+ *
+ * @param expression The expression to compile
+ * @return A new compiled expression
+ * @throws ParseException on failure to compile
+ */
+ public Expression compileExpression(String expression) throws ParseException {
+ try {
+ this.className = "Expr" + Long.toString(counter.incrementAndGet());
+ externalsMap = new HashMap<String, Integer>();
+ externalsList = new ArrayList<String>();
+
+ Tree antlrTree = getAntlrComputedExpressionTree(expression);
+
+ beginCompile();
+ recursiveCompile(antlrTree, ComputedType.DOUBLE);
+ endCompile();
+
+ Class<? extends Expression> evaluatorClass = loader.define(EXPRESSION_CLASS_PREFIX + className, classWriter.toByteArray());
+ Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
+ return constructor.newInstance(expression, externalsList.toArray(new String[externalsList.size()]));
+ } catch (InstantiationException exception) {
+ throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+ } catch (IllegalAccessException exception) {
+ throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+ } catch (NoSuchMethodException exception) {
+ throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+ } catch (InvocationTargetException exception) {
+ throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+ }
+ }
+
+ private void beginCompile() {
+ classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, EXPRESSION_INTERNAL_PREFIX + className,
+ null, COMPILED_EXPRESSION_INTERNAL, null);
+ MethodVisitor constructor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;[Ljava/lang/String;)V", null, null);
+ constructor.visitCode();
+ constructor.visitVarInsn(ALOAD, 0);
+ constructor.visitVarInsn(ALOAD, 1);
+ constructor.visitVarInsn(ALOAD, 2);
+ constructor.visitMethodInsn(INVOKESPECIAL, COMPILED_EXPRESSION_INTERNAL, "<init>", "(Ljava/lang/String;[Ljava/lang/String;)V");
+ constructor.visitInsn(RETURN);
+ constructor.visitMaxs(0, 0);
+ constructor.visitEnd();
+
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "evaluate", "(I[L" + FUNCTION_VALUES_INTERNAL + ";)D", null, null);
+ methodVisitor.visitCode();
+ }
+
+ private void recursiveCompile(Tree current, ComputedType expected) {
+ int type = current.getType();
+ String text = current.getText();
+
+ switch (type) {
+ case JavascriptParser.AT_CALL:
+ Tree identifier = current.getChild(0);
+ String call = identifier.getText();
+ int arguments = current.getChildCount() - 1;
+
+ JavascriptFunction method = JavascriptFunction.getMethod(call, arguments);
+
+ for (int argument = 1; argument <= arguments; ++argument) {
+ recursiveCompile(current.getChild(argument), ComputedType.DOUBLE);
+ }
+
+ methodVisitor.visitMethodInsn(INVOKESTATIC, method.klass, method.method, method.signature);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.ID:
+ int index;
+
+ if (externalsMap.containsKey(text)) {
+ index = externalsMap.get(text);
+ } else {
+ index = externalsList.size();
+ externalsList.add(text);
+ externalsMap.put(text, index);
+ }
+
+ methodVisitor.visitVarInsn(ALOAD, 2);
+
+ switch (index) {
+ case 0:
+ methodVisitor.visitInsn(ICONST_0);
+ break;
+ case 1:
+ methodVisitor.visitInsn(ICONST_1);
+ break;
+ case 2:
+ methodVisitor.visitInsn(ICONST_2);
+ break;
+ case 3:
+ methodVisitor.visitInsn(ICONST_3);
+ break;
+ case 4:
+ methodVisitor.visitInsn(ICONST_4);
+ break;
+ case 5:
+ methodVisitor.visitInsn(ICONST_5);
+ break;
+ default:
+ if (index < 128) {
+ methodVisitor.visitIntInsn(BIPUSH, index);
+ } else if (index < 16384) {
+ methodVisitor.visitIntInsn(SIPUSH, index);
+ } else {
+ methodVisitor.visitLdcInsn(index);
+ }
+
+ break;
+ }
+
+ methodVisitor.visitInsn(AALOAD);
+ methodVisitor.visitVarInsn(ILOAD, 1);
+ methodVisitor.visitMethodInsn(INVOKEVIRTUAL, FUNCTION_VALUES_INTERNAL, "doubleVal", "(I)D");
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.HEX:
+ long hex = Long.parseLong(text.substring(2), 16);
+
+ if (expected == ComputedType.INT) {
+ methodVisitor.visitLdcInsn((int)hex);
+ } else if (expected == ComputedType.LONG) {
+ methodVisitor.visitLdcInsn(hex);
+ } else {
+ methodVisitor.visitLdcInsn((double)hex);
+ }
+
+ break;
+ case JavascriptParser.OCTAL:
+ long octal = Long.parseLong(text.substring(1), 8);
+
+ if (expected == ComputedType.INT) {
+ methodVisitor.visitLdcInsn((int)octal);
+ } else if (expected == ComputedType.LONG) {
+ methodVisitor.visitLdcInsn(octal);
+ } else {
+ methodVisitor.visitLdcInsn((double)octal);
+ }
+
+ break;
+ case JavascriptParser.DECIMAL:
+ double decimal = Double.parseDouble(text);
+ methodVisitor.visitLdcInsn(decimal);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_NEGATE:
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DNEG);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_ADD:
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DADD);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_SUBTRACT:
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DSUB);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_MULTIPLY:
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(Opcodes.DMUL);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_DIVIDE:
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DDIV);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_MODULO:
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DREM);
+
+ typeCompile(expected, ComputedType.DOUBLE);
+ break;
+ case JavascriptParser.AT_BIT_SHL:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ recursiveCompile(current.getChild(1), ComputedType.INT);
+ methodVisitor.visitInsn(LSHL);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_BIT_SHR:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ recursiveCompile(current.getChild(1), ComputedType.INT);
+ methodVisitor.visitInsn(LSHR);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_BIT_SHU:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ recursiveCompile(current.getChild(1), ComputedType.INT);
+ methodVisitor.visitInsn(LUSHR);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_BIT_AND:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ recursiveCompile(current.getChild(1), ComputedType.LONG);
+ methodVisitor.visitInsn(LAND);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_BIT_OR:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ recursiveCompile(current.getChild(1), ComputedType.LONG);
+ methodVisitor.visitInsn(LOR);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_BIT_XOR:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ recursiveCompile(current.getChild(1), ComputedType.LONG);
+ methodVisitor.visitInsn(LXOR);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_BIT_NOT:
+ recursiveCompile(current.getChild(0), ComputedType.LONG);
+ methodVisitor.visitLdcInsn(new Long(-1));
+ methodVisitor.visitInsn(LXOR);
+
+ typeCompile(expected, ComputedType.LONG);
+ break;
+ case JavascriptParser.AT_COMP_EQ:
+ Label labelEqTrue = new Label();
+ Label labelEqReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DCMPL);
+
+ methodVisitor.visitJumpInsn(IFEQ, labelEqTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelEqReturn);
+ methodVisitor.visitLabel(labelEqTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelEqReturn);
+
+ break;
+ case JavascriptParser.AT_COMP_NEQ:
+ Label labelNeqTrue = new Label();
+ Label labelNeqReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DCMPL);
+
+ methodVisitor.visitJumpInsn(IFNE, labelNeqTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelNeqReturn);
+ methodVisitor.visitLabel(labelNeqTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelNeqReturn);
+
+ break;
+ case JavascriptParser.AT_COMP_LT:
+ Label labelLtTrue = new Label();
+ Label labelLtReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DCMPG);
+
+ methodVisitor.visitJumpInsn(IFLT, labelLtTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelLtReturn);
+ methodVisitor.visitLabel(labelLtTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelLtReturn);
+
+ break;
+ case JavascriptParser.AT_COMP_GT:
+ Label labelGtTrue = new Label();
+ Label labelGtReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DCMPL);
+
+ methodVisitor.visitJumpInsn(IFGT, labelGtTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelGtReturn);
+ methodVisitor.visitLabel(labelGtTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelGtReturn);
+
+ break;
+ case JavascriptParser.AT_COMP_LTE:
+ Label labelLteTrue = new Label();
+ Label labelLteReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DCMPG);
+
+ methodVisitor.visitJumpInsn(IFLE, labelLteTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelLteReturn);
+ methodVisitor.visitLabel(labelLteTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelLteReturn);
+
+ break;
+ case JavascriptParser.AT_COMP_GTE:
+ Label labelGteTrue = new Label();
+ Label labelGteReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+ recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+ methodVisitor.visitInsn(DCMPL);
+
+ methodVisitor.visitJumpInsn(IFGE, labelGteTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelGteReturn);
+ methodVisitor.visitLabel(labelGteTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelGteReturn);
+
+ break;
+ case JavascriptParser.AT_BOOL_NOT:
+ Label labelNotTrue = new Label();
+ Label labelNotReturn = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.INT);
+ methodVisitor.visitJumpInsn(IFEQ, labelNotTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, labelNotReturn);
+ methodVisitor.visitLabel(labelNotTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(labelNotReturn);
+
+ break;
+ case JavascriptParser.AT_BOOL_AND:
+ Label andFalse = new Label();
+ Label andEnd = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.INT);
+ methodVisitor.visitJumpInsn(IFEQ, andFalse);
+ recursiveCompile(current.getChild(1), ComputedType.INT);
+ methodVisitor.visitJumpInsn(IFEQ, andFalse);
+ truthCompile(expected, true);
+ methodVisitor.visitJumpInsn(GOTO, andEnd);
+ methodVisitor.visitLabel(andFalse);
+ truthCompile(expected, false);
+ methodVisitor.visitLabel(andEnd);
+
+ break;
+ case JavascriptParser.AT_BOOL_OR:
+ Label orTrue = new Label();
+ Label orEnd = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.INT);
+ methodVisitor.visitJumpInsn(IFNE, orTrue);
+ recursiveCompile(current.getChild(1), ComputedType.INT);
+ methodVisitor.visitJumpInsn(IFNE, orTrue);
+ truthCompile(expected, false);
+ methodVisitor.visitJumpInsn(GOTO, orEnd);
+ methodVisitor.visitLabel(orTrue);
+ truthCompile(expected, true);
+ methodVisitor.visitLabel(orEnd);
+
+ break;
+ case JavascriptParser.AT_COND_QUE:
+ Label condFalse = new Label();
+ Label condEnd = new Label();
+
+ recursiveCompile(current.getChild(0), ComputedType.INT);
+ methodVisitor.visitJumpInsn(IFEQ, condFalse);
+ recursiveCompile(current.getChild(1), expected);
+ methodVisitor.visitJumpInsn(GOTO, condEnd);
+ methodVisitor.visitLabel(condFalse);
+ recursiveCompile(current.getChild(2), expected);
+ methodVisitor.visitLabel(condEnd);
+
+ break;
+ default:
+ throw new IllegalStateException("Unknown operation specified: (" + current.getText() + ").");
+ }
+ }
+
+ private void typeCompile(ComputedType expected, ComputedType actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ switch (expected) {
+ case INT:
+ if (actual == ComputedType.LONG) {
+ methodVisitor.visitInsn(L2I);
+ } else {
+ methodVisitor.visitInsn(D2I);
+ }
+
+ break;
+ case LONG:
+ if (actual == ComputedType.INT) {
+ methodVisitor.visitInsn(I2L);
+ } else {
+ methodVisitor.visitInsn(D2L);
+ }
+
+ break;
+ default:
+ if (actual == ComputedType.INT) {
+ methodVisitor.visitInsn(I2D);
+ } else {
+ methodVisitor.visitInsn(L2D);
+ }
+
+ break;
+ }
+ }
+
+ private void truthCompile(ComputedType expected, boolean truth) {
+ switch (expected) {
+ case INT:
+ methodVisitor.visitInsn(truth ? ICONST_1 : ICONST_0);
+
+ break;
+ case LONG:
+ methodVisitor.visitInsn(truth ? LCONST_1 : LCONST_0);
+
+ break;
+ default:
+ methodVisitor.visitInsn(truth ? DCONST_1 : DCONST_0);
+
+ break;
+ }
+ }
+
+ private void endCompile() {
+ methodVisitor.visitInsn(DRETURN);
+ methodVisitor.visitMaxs(0, 0);
+ methodVisitor.visitEnd();
+
+ classWriter.visitEnd();
+ }
+
+ private static Tree getAntlrComputedExpressionTree(String expression) throws ParseException {
+ CharStream input = new ANTLRStringStream(expression);
+ JavascriptLexer lexer = new JavascriptLexer(input);
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
+ JavascriptParser parser = new JavascriptParser(tokens);
+
+ try {
+ return parser.expression().tree;
+
+ } catch (RecognitionException exception) {
+ throw new IllegalArgumentException(exception);
+ } catch (RuntimeException exception) {
+ if (exception.getCause() instanceof ParseException) {
+ throw (ParseException)exception.getCause();
+ }
+ throw exception;
+ }
+ }
+}
Added: lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java?rev=1522767&view=auto
==============================================================================
--- lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java (added)
+++ lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java Fri Sep 13 03:11:17 2013
@@ -0,0 +1,116 @@
+package org.apache.lucene.expressions.js;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.lucene.util.MathUtil;
+import org.objectweb.asm.Type;
+
+/**
+ * A wrapper to delegate function calls from a javascript expression.
+ */
+class JavascriptFunction {
+ private static Map<String, JavascriptFunction> methods = null;
+
+ static {
+ String mathClass = Type.getInternalName(Math.class);
+ String mathUtilClass = Type.getInternalName(MathUtil.class);
+
+ JavascriptFunction abs = new JavascriptFunction("abs", 1, mathClass, "abs", "(D)D" );
+ JavascriptFunction acos = new JavascriptFunction("acos", 1, mathClass, "acos", "(D)D" );
+ JavascriptFunction acosh = new JavascriptFunction("acosh", 1, mathUtilClass, "acosh", "(D)D" );
+ JavascriptFunction asin = new JavascriptFunction("asin", 1, mathClass, "asin", "(D)D" );
+ JavascriptFunction asinh = new JavascriptFunction("asinh", 1, mathUtilClass, "asinh", "(D)D" );
+ JavascriptFunction atan = new JavascriptFunction("atan", 1, mathClass, "atan", "(D)D" );
+ JavascriptFunction atan2 = new JavascriptFunction("atan2", 1, mathClass, "atan2", "(DD)D");
+ JavascriptFunction atanh = new JavascriptFunction("atanh", 1, mathUtilClass, "atanh", "(D)D" );
+ JavascriptFunction ceil = new JavascriptFunction("ceil", 1, mathClass, "ceil", "(D)D" );
+ JavascriptFunction cos = new JavascriptFunction("cos", 1, mathClass, "cos", "(D)D" );
+ JavascriptFunction cosh = new JavascriptFunction("cosh", 1, mathClass, "cosh", "(D)D" );
+ JavascriptFunction exp = new JavascriptFunction("exp", 1, mathClass, "exp", "(D)D" );
+ JavascriptFunction floor = new JavascriptFunction("floor", 1, mathClass, "floor", "(D)D" );
+ JavascriptFunction ln = new JavascriptFunction("ln", 1, mathClass, "log", "(D)D" );
+ JavascriptFunction log10 = new JavascriptFunction("log10", 1, mathClass, "log10", "(D)D" );
+ JavascriptFunction logn = new JavascriptFunction("logn", 2, mathUtilClass, "log", "(DD)D");
+ JavascriptFunction pow = new JavascriptFunction("pow", 2, mathClass, "pow", "(DD)D");
+ JavascriptFunction sin = new JavascriptFunction("sin", 1, mathClass, "sin", "(D)D" );
+ JavascriptFunction sinh = new JavascriptFunction("sinh", 1, mathClass, "sinh", "(D)D" );
+ JavascriptFunction sqrt = new JavascriptFunction("sqrt", 1, mathClass, "sqrt", "(D)D" );
+ JavascriptFunction tan = new JavascriptFunction("tan", 1, mathClass, "tan", "(D)D" );
+ JavascriptFunction tanh = new JavascriptFunction("tanh", 1, mathClass, "tanh", "(D)D" );
+
+ JavascriptFunction min = new JavascriptFunction("min", 2, mathClass, "min", "(DD)D");
+ JavascriptFunction max = new JavascriptFunction("max", 2, mathClass, "max", "(DD)D");
+
+ methods = new HashMap<String, JavascriptFunction>();
+ methods.put( "abs", abs );
+ methods.put( "acos", acos );
+ methods.put( "acosh", acosh );
+ methods.put( "asin", asin );
+ methods.put( "asinh", asinh );
+ methods.put( "atan", atan );
+ methods.put( "atan2", atan2 );
+ methods.put( "atanh", atanh );
+ methods.put( "ceil", ceil );
+ methods.put( "cos", cos );
+ methods.put( "cosh", cosh );
+ methods.put( "exp", exp );
+ methods.put( "floor", floor );
+ methods.put( "ln", ln );
+ methods.put( "log10", log10 );
+ methods.put( "logn", logn );
+ methods.put( "max", max );
+ methods.put( "min", min );
+ methods.put( "pow", pow );
+ methods.put( "sin", sin );
+ methods.put( "sinh", sinh );
+ methods.put( "sqrt", sqrt );
+ methods.put( "tan", tan );
+ methods.put( "tanh", tanh );
+ }
+
+ public static JavascriptFunction getMethod(String call, int arguments) {
+ JavascriptFunction method = methods.get(call);
+
+ if (method == null) {
+ throw new IllegalArgumentException("Unrecognized method call (" + call + ").");
+ }
+
+ if (arguments != method.arguments && method.arguments != -1) {
+ throw new IllegalArgumentException("Expected (" + method.arguments + ") arguments for method call (" +
+ call + "), but found (" + arguments + ").");
+ }
+
+ return method;
+ }
+
+ public final String call;
+ public final int arguments;
+ public final String klass;
+ public final String method;
+ public final String signature;
+
+ private JavascriptFunction(String call, int arguments, String klass, String method, String signature) {
+ this.call = call;
+ this.arguments = arguments;
+ this.klass = klass;
+ this.method = method;
+ this.signature = signature;
+ }
+}