Skip to content

Commit

Permalink
jitting arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
mariofusco committed Feb 11, 2012
1 parent 49abdf4 commit 379f282
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 61 deletions.
2 changes: 1 addition & 1 deletion LambdajDemoTest.bat
@@ -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
4 changes: 3 additions & 1 deletion src/main/java/ch/lambdaj/Lambda.java
Expand Up @@ -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
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/ch/lambdaj/function/argument/Argument.java
Expand Up @@ -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());
}

/**
Expand All @@ -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);
}

Expand Down
17 changes: 10 additions & 7 deletions src/main/java/ch/lambdaj/function/argument/Invocation.java
Expand Up @@ -37,6 +37,10 @@ final class Invocation {
}
}

boolean hasArguments() {
return weakArgs != null;
}

private Object[] getConcreteArgs() {
if (weakArgs == null) return new Object[0];
Object[] args = new Object[weakArgs.length];
Expand Down Expand Up @@ -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();
}
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/ch/lambdaj/function/argument/InvocationSequence.java
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/ch/lambdaj/function/argument/Invoker.java
@@ -0,0 +1,5 @@
package ch.lambdaj.function.argument;

public interface Invoker {
Object invokeOn(Object object);
}
130 changes: 130 additions & 0 deletions src/main/java/ch/lambdaj/function/argument/InvokerJitter.java
@@ -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);
}
}
}

0 comments on commit 379f282

Please sign in to comment.