Permalink
Browse files

jitting arguments

  • Loading branch information...
1 parent 49abdf4 commit 379f282fd480c2b22b91f686d598fb29583320a7 @mariofusco committed Feb 11, 2012
View
@@ -1,3 +1,3 @@
set MAVEN_REPO=C:\Users\Mario\.m2\repository
-java -server -Xms512M -Xmx512M -cp %MAVEN_REPO%/cglib/cglib-nodep/2.2/cglib-nodep-2.2.jar;%MAVEN_REPO%/org/hamcrest/hamcrest-all/1.1/hamcrest-all-1.1.jar;%MAVEN_REPO%/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar;%MAVEN_REPO%/org/objenesis/objenesis/1.2/objenesis-1.2.jar;target/classes;target/test-classes ch.lambdaj.demo.LambdaDemoTestMain %1
+java -server -Xms512M -Xmx512M -cp %MAVEN_REPO%/cglib/cglib-nodep/2.2.2/cglib-nodep-2.2.2.jar;%MAVEN_REPO%/org/hamcrest/hamcrest-all/1.1/hamcrest-all-1.1.jar;%MAVEN_REPO%/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar;%MAVEN_REPO%/org/objenesis/objenesis/1.2/objenesis-1.2.jar;target/classes;target/test-classes ch.lambdaj.demo.LambdaDemoTestMain %1
@@ -35,7 +35,9 @@
public final class Lambda {
private Lambda() { }
-
+
+ public static int jitThreshold = 0;
+
/**
* Constructs a proxy object that mocks the given Class registering all the subsequent invocations on the object.
* @param clazz The class of the object to be mocked
@@ -4,16 +4,32 @@
package ch.lambdaj.function.argument;
+import ch.lambdaj.Lambda;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* An Argument represents a statically defined sequence of method invocations on a given Class.
* @author Mario Fusco
*/
public class Argument<T> {
private final InvocationSequence invocationSequence;
-
- Argument(InvocationSequence invocationSequence) {
+
+ private final boolean isJittable;
+
+ private Invoker jittedInvoker;
+
+ private AtomicInteger invocationCounter = new AtomicInteger(0);
+
+ private static final Map<String, Invoker> invokerCache = new HashMap<String, Invoker>();
+
+ Argument(InvocationSequence invocationSequence) {
this.invocationSequence = invocationSequence;
+ isJittable = Lambda.jitThreshold >= 0 && invocationSequence.isJittable();
+ if (isJittable) jittedInvoker = invokerCache.get(invocationSequence.toString());
}
/**
@@ -32,6 +48,15 @@ public String getInkvokedPropertyName() {
*/
@SuppressWarnings("unchecked")
public T evaluate(Object object) {
+ if (jittedInvoker != null) return (T)jittedInvoker.invokeOn(object);
+ if (!isJittable) return (T)invocationSequence.invokeOn(object);
+
+ if (invocationCounter.getAndIncrement() == Lambda.jitThreshold) {
+ jittedInvoker = new InvokerJitter(object, invocationSequence).jitInvoker();
+ invokerCache.put(invocationSequence.toString(), jittedInvoker);
+ return (T)jittedInvoker.invokeOn(object);
+ }
+
return (T)invocationSequence.invokeOn(object);
}
@@ -37,6 +37,10 @@
}
}
+ boolean hasArguments() {
+ return weakArgs != null;
+ }
+
private Object[] getConcreteArgs() {
if (weakArgs == null) return new Object[0];
Object[] args = new Object[weakArgs.length];
@@ -77,14 +81,13 @@ Object invokeOn(Object object) {
*/
@Override
public String toString() {
+ if (weakArgs == null) return invokedMethod.toString();
StringBuilder sb = new StringBuilder(invokedMethod.toString());
- if (weakArgs != null) {
- sb.append(" with args ");
- boolean first = true;
- for (ParameterReference arg : weakArgs) {
- sb.append(first ? "" : ", ").append(arg.get());
- first = false;
- }
+ sb.append(" with args ");
+ boolean first = true;
+ for (ParameterReference arg : weakArgs) {
+ sb.append(first ? "" : ", ").append(arg.get());
+ first = false;
}
return sb.toString();
}
@@ -10,12 +10,12 @@
* @author Mario Fusco
* @author Frode Carlsen
*/
-final class InvocationSequence {
+final class InvocationSequence implements Invoker {
private static final long serialVersionUID = 1L;
private final Class<?> rootInvokedClass;
private String inkvokedPropertyName;
- private Invocation lastInvocation;
+ Invocation lastInvocation;
private transient int hashCode;
InvocationSequence(Class<?> rootInvokedClass) {
@@ -80,9 +80,8 @@ public int hashCode() {
return hashCode;
}
- @SuppressWarnings("unchecked")
- public <T> T invokeOn(Object object) {
- return (T)invokeOn(lastInvocation, object);
+ public Object invokeOn(Object object) {
+ return invokeOn(lastInvocation, object);
}
private Object invokeOn(Invocation invocation, Object value) {
@@ -91,6 +90,14 @@ private Object invokeOn(Invocation invocation, Object value) {
return invocation.invokeOn(value);
}
+ boolean isJittable() {
+ return lastInvocation != null && isJittable(lastInvocation);
+ }
+
+ private boolean isJittable(Invocation invocation) {
+ return !invocation.hasArguments() && (invocation.previousInvocation == null || isJittable(invocation.previousInvocation));
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(100);
@@ -0,0 +1,5 @@
+package ch.lambdaj.function.argument;
+
+public interface Invoker {
+ Object invokeOn(Object object);
+}
@@ -0,0 +1,130 @@
+package ch.lambdaj.function.argument;
+
+import net.sf.cglib.asm.ClassWriter;
+import net.sf.cglib.asm.Label;
+import net.sf.cglib.asm.MethodVisitor;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static net.sf.cglib.asm.Opcodes.*;
+
+public class InvokerJitter {
+
+ private static final AtomicInteger jitCounter = new AtomicInteger(1);
+
+ private final InternalClassLoader classLoader;
+ private final InvocationSequence invocationSequence;
+
+ InvokerJitter(Object invokedObject, InvocationSequence invocationSequence) {
+ this(invokedObject.getClass().getClassLoader(), invocationSequence);
+ }
+
+ InvokerJitter(ClassLoader classLoader, InvocationSequence invocationSequence) {
+ this.classLoader = new InternalClassLoader(classLoader);;
+ this.invocationSequence = invocationSequence;
+ }
+
+ Invoker jitInvoker() {
+ try {
+ int id = jitCounter.getAndIncrement();
+ Class<?> clazz = classLoader.defineClass("ch.lambdaj.function.argument.Invoker_" + id, generateBytecode(id));
+ return (Invoker)clazz.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private byte[] generateBytecode(int id) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+ cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "ch/lambdaj/function/argument/Invoker_" + id, null, "java/lang/Object", new String[]{"ch/lambdaj/function/argument/Invoker"});
+ jitEmptyConstructor(cw);
+ jitInvocationMethod(cw);
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+
+ private void jitEmptyConstructor(ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ private void jitInvocationMethod(ClassWriter cw) {
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "invokeOn", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 1);
+ jitInvocationSequence(mv);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ private void jitInvocationSequence(MethodVisitor mv) {
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitTypeInsn(CHECKCAST, getClassName(invocationSequence.getRootInvokedClass()));
+ jitInvocation(mv, invocationSequence.lastInvocation);
+ }
+
+ private void jitInvocation(MethodVisitor mv, Invocation invocation) {
+ if (invocation.previousInvocation != null) jitInvocation(mv, invocation.previousInvocation);
+ mv.visitVarInsn(ASTORE, 2);
+ Label nonNull = new Label();
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitJumpInsn(IFNONNULL, nonNull);
+ mv.visitInsn(ACONST_NULL);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(nonNull);
+ mv.visitVarInsn(ALOAD, 2);
+ Method method = invocation.getInvokedMethod();
+ if (method.getDeclaringClass().isInterface()) {
+ mv.visitMethodInsn(INVOKEINTERFACE, getClassName(method.getDeclaringClass()), method.getName(), "()" + getInternalName(method.getReturnType()));
+ } else {
+ mv.visitMethodInsn(INVOKEVIRTUAL, getClassName(method.getDeclaringClass()), method.getName(), "()" + getInternalName(method.getReturnType()));
+ }
+ primitiveToObject(mv, method.getReturnType());
+ }
+
+ private String getClassName(Class<?> clazz) {
+ return clazz.getName().replace('.', '/');
+ }
+
+ private String getInternalName(Class<?> clazz) {
+ if (!clazz.isPrimitive()) return "L" + getClassName(clazz) + ";";
+ if (clazz == int.class) return "I";
+ if (clazz == long.class) return "J";
+ if (clazz == double.class) return "D";
+ if (clazz == float.class) return "F";
+ if (clazz == boolean.class) return "Z";
+ if (clazz == char.class) return "C";
+ if (clazz == short.class) return "S";
+ return "B";
+ }
+
+ private void primitiveToObject(MethodVisitor mv, Class<?> clazz) {
+ if (!clazz.isPrimitive()) return;
+ if (clazz == int.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
+ else if (clazz == long.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
+ else if (clazz == double.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
+ else if (clazz == float.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
+ else if (clazz == boolean.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
+ else if (clazz == char.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
+ else if (clazz == short.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
+ else if (clazz == byte.class) mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
+ }
+
+ private static class InternalClassLoader extends ClassLoader {
+
+ InternalClassLoader(ClassLoader classLoader) {
+ super(classLoader);
+ }
+
+ Class<?> defineClass(String name, byte[] b) {
+ return defineClass(name, b, 0, b.length);
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit 379f282

Please sign in to comment.