LUCENE-5207: Revert the dynamic class name. Its much better to use the "source file attribute". The class name is now constant (as every class gets own class loader) and looks like an internal class of the compiler. The stack trace is then looking like:

Throwable #1: java.lang.IllegalArgumentException: foobar
       at __randomizedtesting.SeedInfo.seed([3968E8DD2901F71C:4292B9595A397818]:0)
       at org.apache.lucene.util.MathUtil.log(MathUtil.java:51)
       at org.apache.lucene.expressions.js.JavascriptCompiler$CompiledExpression.evaluate(logn(2, 0))
       at org.apache.lucene.expressions.js.TestJavascriptFunction.assertEvaluatesTo(TestJavascriptFunction.java:27)
       at org.apache.lucene.expressions.js.TestJavascriptFunction.testLognMethod(TestJavascriptFunction.java:178)
       at java.lang.Thread.run(Thread.java:724)

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene5207@1522907 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2013-09-13 12:54:29 +00:00
parent 026526dbd4
commit 115d5dd0e1
1 changed files with 17 additions and 36 deletions

View File

@ -116,10 +116,12 @@ public class JavascriptCompiler {
}
}
private static final int MAX_CLASS_NAME_LENGTH = 1024;
// We use the same class name for all generated classes as they all have their own class loader.
// The source code is displayed as "source file name" in stack trace.
private static final String COMPILED_EXPRESSION_CLASS = JavascriptCompiler.class.getName() + "$CompiledExpression";
private static final String COMPILED_EXPRESSION_INTERNAL = COMPILED_EXPRESSION_CLASS.replace('.', '/');
private static final String EXPRESSION_CLASS_PREFIX = JavascriptCompiler.class.getPackage().getName() + ".Expr_";
private static final String COMPILED_EXPRESSION_INTERNAL = Type.getInternalName(Expression.class);
private static final String EXPRESSION_INTERNAL = Type.getInternalName(Expression.class);
private static final Type FUNCTION_VALUES_TYPE = Type.getType(FunctionValues.class);
private static final Type FUNCTION_VALUES_ARRAY_TYPE = Type.getType(FunctionValues[].class);
@ -130,6 +132,8 @@ public class JavascriptCompiler {
private static final String EVALUATE_METHOD_DESC = Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.INT_TYPE, FUNCTION_VALUES_ARRAY_TYPE);
private static final String DOUBLE_VAL_METHOD_DESC = Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.INT_TYPE);
private static final int MAX_SOURCE_LENGTH = 16384;
private final Loader loader = new Loader(getClass().getClassLoader());
private ClassWriter classWriter;
@ -165,60 +169,37 @@ public class JavascriptCompiler {
if (sourceText == null) {
throw new NullPointerException();
}
final String className = EXPRESSION_CLASS_PREFIX + createClassName(sourceText);
// System.out.println(sourceText + "|" + className);
try {
externalsMap = new HashMap<String, Integer>();
externalsList = new ArrayList<String>();
Tree antlrTree = getAntlrComputedExpressionTree(sourceText);
beginCompile(className);
beginCompile(sourceText);
recursiveCompile(antlrTree, ComputedType.DOUBLE);
endCompile();
Class<? extends Expression> evaluatorClass = loader.define(className, classWriter.toByteArray());
Class<? extends Expression> evaluatorClass = loader.define(COMPILED_EXPRESSION_CLASS, classWriter.toByteArray());
Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
return constructor.newInstance(sourceText, externalsList.toArray(new String[externalsList.size()]));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) {
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + sourceText + ").", exception);
}
}
private String createClassName(String sourceText) {
final StringBuilder sb = new StringBuilder(Math.min(sourceText.length() / 2, MAX_CLASS_NAME_LENGTH));
boolean wasIdentifierPart = true;
for (int i = 0, c = sourceText.length(); i < c && sb.length() < MAX_CLASS_NAME_LENGTH; i++) {
final char ch = sourceText.charAt(i);
if (Character.isJavaIdentifierPart(ch)) {
sb.append(ch);
wasIdentifierPart = true;
} else if (wasIdentifierPart) {
sb.append('_');
wasIdentifierPart = false;
}
}
// remove trailing underscores
for (int i = sb.length() - 1; i >= 0; i--) {
if (sb.charAt(i) == '_') {
sb.setLength(i);
} else {
break;
}
}
return sb.toString();
}
private void beginCompile(String className) {
private void beginCompile(String sourceText) {
classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, className.replace('.', '/'),
null, COMPILED_EXPRESSION_INTERNAL, null);
classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, COMPILED_EXPRESSION_INTERNAL,
null, EXPRESSION_INTERNAL, null);
String clippedSourceText = (sourceText.length() <= MAX_SOURCE_LENGTH) ? sourceText : (sourceText.substring(0, MAX_SOURCE_LENGTH - 3) + "...");
classWriter.visitSource(clippedSourceText, null);
MethodVisitor constructor = classWriter.visitMethod(ACC_PUBLIC, "<init>", CONSTRUCTOR_DESC, null, null);
constructor.visitCode();
constructor.visitVarInsn(ALOAD, 0);
constructor.visitVarInsn(ALOAD, 1);
constructor.visitVarInsn(ALOAD, 2);
constructor.visitMethodInsn(INVOKESPECIAL, COMPILED_EXPRESSION_INTERNAL, "<init>", CONSTRUCTOR_DESC);
constructor.visitMethodInsn(INVOKESPECIAL, EXPRESSION_INTERNAL, "<init>", CONSTRUCTOR_DESC);
constructor.visitInsn(RETURN);
constructor.visitMaxs(0, 0);
constructor.visitEnd();