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:
Uwe Schindler 2013-09-15 10:39:10 +00:00
parent 90f2793896
commit 96db861f04
2 changed files with 62 additions and 46 deletions

View File

@ -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);

View File

@ -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"));