mirror of https://github.com/apache/lucene.git
LUCENE-5207: Better classloade test: It now uses a completely synthetic class, so its 100% unreachable from main classloader. Also remove the static Opcodes imports
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene5207@1523426 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
90f2793896
commit
96db861f04
|
@ -16,14 +16,6 @@ package org.apache.lucene.expressions.js;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.ACC_SYNTHETIC;
|
||||
import static org.objectweb.asm.Opcodes.IFEQ;
|
||||
import static org.objectweb.asm.Opcodes.IFNE;
|
||||
import static org.objectweb.asm.Opcodes.V1_7;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
@ -91,7 +83,7 @@ public class JavascriptCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
private static final int CLASSFILE_VERSION = V1_7;
|
||||
private static final int CLASSFILE_VERSION = Opcodes.V1_7;
|
||||
|
||||
// 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.
|
||||
|
@ -211,19 +203,24 @@ public class JavascriptCompiler {
|
|||
}
|
||||
|
||||
private void beginCompile() {
|
||||
classWriter.visit(CLASSFILE_VERSION, ACC_PUBLIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC, COMPILED_EXPRESSION_INTERNAL,
|
||||
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) ? sourceText : (sourceText.substring(0, MAX_SOURCE_LENGTH - 3) + "...");
|
||||
String clippedSourceText = (sourceText.length() <= MAX_SOURCE_LENGTH) ?
|
||||
sourceText : (sourceText.substring(0, MAX_SOURCE_LENGTH - 3) + "...");
|
||||
classWriter.visitSource(clippedSourceText, null);
|
||||
|
||||
GeneratorAdapter constructor = new GeneratorAdapter(ACC_PUBLIC | ACC_SYNTHETIC, EXPRESSION_CTOR, null, null, classWriter);
|
||||
GeneratorAdapter constructor = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
|
||||
EXPRESSION_CTOR, null, null, classWriter);
|
||||
constructor.loadThis();
|
||||
constructor.loadArgs();
|
||||
constructor.invokeConstructor(EXPRESSION_TYPE, EXPRESSION_CTOR);
|
||||
constructor.returnValue();
|
||||
constructor.endMethod();
|
||||
|
||||
gen = new GeneratorAdapter(ACC_PUBLIC | ACC_SYNTHETIC, EVALUATE_METHOD, null, null, classWriter);
|
||||
gen = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
|
||||
EVALUATE_METHOD, null, null, classWriter);
|
||||
}
|
||||
|
||||
private void recursiveCompile(Tree current, Type expected) {
|
||||
|
@ -350,7 +347,7 @@ public class JavascriptCompiler {
|
|||
Label labelNotReturn = new Label();
|
||||
|
||||
recursiveCompile(current.getChild(0), Type.INT_TYPE);
|
||||
gen.visitJumpInsn(IFEQ, labelNotTrue);
|
||||
gen.visitJumpInsn(Opcodes.IFEQ, labelNotTrue);
|
||||
pushBoolean(expected, false);
|
||||
gen.goTo(labelNotReturn);
|
||||
gen.visitLabel(labelNotTrue);
|
||||
|
@ -362,9 +359,9 @@ public class JavascriptCompiler {
|
|||
Label andEnd = new Label();
|
||||
|
||||
recursiveCompile(current.getChild(0), Type.INT_TYPE);
|
||||
gen.visitJumpInsn(IFEQ, andFalse);
|
||||
gen.visitJumpInsn(Opcodes.IFEQ, andFalse);
|
||||
recursiveCompile(current.getChild(1), Type.INT_TYPE);
|
||||
gen.visitJumpInsn(IFEQ, andFalse);
|
||||
gen.visitJumpInsn(Opcodes.IFEQ, andFalse);
|
||||
pushBoolean(expected, true);
|
||||
gen.goTo(andEnd);
|
||||
gen.visitLabel(andFalse);
|
||||
|
@ -376,9 +373,9 @@ public class JavascriptCompiler {
|
|||
Label orEnd = new Label();
|
||||
|
||||
recursiveCompile(current.getChild(0), Type.INT_TYPE);
|
||||
gen.visitJumpInsn(IFNE, orTrue);
|
||||
gen.visitJumpInsn(Opcodes.IFNE, orTrue);
|
||||
recursiveCompile(current.getChild(1), Type.INT_TYPE);
|
||||
gen.visitJumpInsn(IFNE, orTrue);
|
||||
gen.visitJumpInsn(Opcodes.IFNE, orTrue);
|
||||
pushBoolean(expected, false);
|
||||
gen.goTo(orEnd);
|
||||
gen.visitLabel(orTrue);
|
||||
|
@ -390,7 +387,7 @@ public class JavascriptCompiler {
|
|||
Label condEnd = new Label();
|
||||
|
||||
recursiveCompile(current.getChild(0), Type.INT_TYPE);
|
||||
gen.visitJumpInsn(IFEQ, condFalse);
|
||||
gen.visitJumpInsn(Opcodes.IFEQ, condFalse);
|
||||
recursiveCompile(current.getChild(1), expected);
|
||||
gen.goTo(condEnd);
|
||||
gen.visitLabel(condFalse);
|
||||
|
|
|
@ -17,8 +17,6 @@ package org.apache.lucene.expressions.js;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -26,6 +24,10 @@ import java.util.Map;
|
|||
|
||||
import org.apache.lucene.expressions.Expression;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.GeneratorAdapter;
|
||||
|
||||
/** Tests customing the function map */
|
||||
public class TestCustomFunctions extends LuceneTestCase {
|
||||
|
@ -160,23 +162,33 @@ public class TestCustomFunctions extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
/** hack to load this test a second time in a different classLoader */
|
||||
static class Loader extends ClassLoader {
|
||||
/** Classloader that can be used to create a fake static class that has one method returning a static var */
|
||||
static class Loader extends ClassLoader implements Opcodes {
|
||||
Loader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
public Class<?> loadFromParentResource(String className) throws Exception {
|
||||
final ByteArrayOutputStream byteCode = new ByteArrayOutputStream();
|
||||
try (InputStream in = getParent().getResourceAsStream(className.replace('.', '/') + ".class")) {
|
||||
final byte[] buf = new byte[1024];
|
||||
int read;
|
||||
do {
|
||||
read = in.read(buf);
|
||||
if (read > 0) byteCode.write(buf, 0, read);
|
||||
} while (read > 0);
|
||||
}
|
||||
final byte[] bc = byteCode.toByteArray();
|
||||
public Class<?> createFakeClass() {
|
||||
String className = TestCustomFunctions.class.getName() + "$Foo";
|
||||
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
classWriter.visit(Opcodes.V1_5, ACC_PUBLIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC,
|
||||
className.replace('.', '/'), null, Type.getInternalName(Object.class), null);
|
||||
|
||||
org.objectweb.asm.commons.Method m = org.objectweb.asm.commons.Method.getMethod("void <init>()");
|
||||
GeneratorAdapter constructor = new GeneratorAdapter(ACC_PRIVATE | ACC_SYNTHETIC, m, null, null, classWriter);
|
||||
constructor.loadThis();
|
||||
constructor.loadArgs();
|
||||
constructor.invokeConstructor(Type.getType(Object.class), m);
|
||||
constructor.returnValue();
|
||||
constructor.endMethod();
|
||||
|
||||
GeneratorAdapter gen = new GeneratorAdapter(ACC_STATIC | ACC_PUBLIC | ACC_SYNTHETIC,
|
||||
org.objectweb.asm.commons.Method.getMethod("double bar()"), null, null, classWriter);
|
||||
gen.push(2.0);
|
||||
gen.returnValue();
|
||||
gen.endMethod();
|
||||
|
||||
byte[] bc = classWriter.toByteArray();
|
||||
return defineClass(className, bc, 0, bc.length);
|
||||
}
|
||||
}
|
||||
|
@ -184,31 +196,38 @@ public class TestCustomFunctions extends LuceneTestCase {
|
|||
/** uses this test with a different classloader and tries to
|
||||
* register it using the default classloader, which should fail */
|
||||
public void testClassLoader() throws Exception {
|
||||
Loader child = new Loader(this.getClass().getClassLoader());
|
||||
Class<?> thisInDifferentLoader = child.loadFromParentResource(getClass().getName());
|
||||
Map<String,Method> functions = Collections.singletonMap("zeroArgMethod", thisInDifferentLoader.getMethod("zeroArgMethod"));
|
||||
ClassLoader thisLoader = getClass().getClassLoader();
|
||||
Loader childLoader = new Loader(thisLoader);
|
||||
Class<?> fooClass = childLoader.createFakeClass();
|
||||
|
||||
Method barMethod = fooClass.getMethod("bar");
|
||||
Map<String,Method> functions = Collections.singletonMap("bar", barMethod);
|
||||
assertNotSame(thisLoader, fooClass.getClassLoader());
|
||||
assertNotSame(thisLoader, barMethod.getDeclaringClass().getClassLoader());
|
||||
|
||||
// this should pass:
|
||||
Expression expr = JavascriptCompiler.compile("bar()", functions, childLoader);
|
||||
assertEquals(2.0, expr.evaluate(0, null), DELTA);
|
||||
|
||||
// use our classloader, not the foreign one, which should fail!
|
||||
try {
|
||||
JavascriptCompiler.compile("zeroArgMethod()", functions, getClass().getClassLoader());
|
||||
JavascriptCompiler.compile("bar()", functions, thisLoader);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("is not declared by a class which is accessible by the given parent ClassLoader"));
|
||||
}
|
||||
|
||||
// this should pass:
|
||||
Expression expr = JavascriptCompiler.compile("zeroArgMethod()", functions, child);
|
||||
assertEquals(5, expr.evaluate(0, null), DELTA);
|
||||
|
||||
// mix foreign and default functions
|
||||
Map<String,Method> mixedFunctions = new HashMap<>(JavascriptCompiler.DEFAULT_FUNCTIONS);
|
||||
mixedFunctions.putAll(functions);
|
||||
expr = JavascriptCompiler.compile("zeroArgMethod()", mixedFunctions, child);
|
||||
assertEquals(5, expr.evaluate(0, null), DELTA);
|
||||
expr = JavascriptCompiler.compile("sqrt(20)", mixedFunctions, child);
|
||||
expr = JavascriptCompiler.compile("bar()", mixedFunctions, childLoader);
|
||||
assertEquals(2.0, expr.evaluate(0, null), DELTA);
|
||||
expr = JavascriptCompiler.compile("sqrt(20)", mixedFunctions, childLoader);
|
||||
assertEquals(Math.sqrt(20), expr.evaluate(0, null), DELTA);
|
||||
|
||||
// use our classloader, not the foreign one, which should fail!
|
||||
try {
|
||||
JavascriptCompiler.compile("zeroArgMethod()", functions, getClass().getClassLoader());
|
||||
JavascriptCompiler.compile("bar()", functions, thisLoader);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("is not declared by a class which is accessible by the given parent ClassLoader"));
|
||||
|
|
Loading…
Reference in New Issue