Skip to content

Commit a0230a2

Browse files
committed
compile the class guard
1 parent 39f4096 commit a0230a2

4 files changed

Lines changed: 170 additions & 3 deletions

File tree

compiler/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@
5353
<version>12.0</version>
5454
</dependency>
5555

56+
<!-- ASM -->
57+
<dependency>
58+
<groupId>org.ow2.asm</groupId>
59+
<artifactId>asm-commons</artifactId>
60+
<version>4.0</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.ow2.asm</groupId>
64+
<artifactId>asm-util</artifactId>
65+
<version>4.0</version>
66+
</dependency>
67+
5668
<!-- Scala -->
5769
<dependency>
5870
<groupId>org.scala-lang</groupId>

compiler/src/main/java/com/github/mustachejava/reflect/ClassGuard.java

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.github.mustachejava.reflect;
22

3-
import com.google.common.base.Predicate;
3+
import org.objectweb.asm.ClassWriter;
4+
import org.objectweb.asm.Label;
5+
import org.objectweb.asm.Type;
6+
import org.objectweb.asm.commons.GeneratorAdapter;
7+
import org.objectweb.asm.commons.Method;
8+
9+
import static org.objectweb.asm.commons.GeneratorAdapter.*;
410

511
/**
612
* Ensure that the class of the current scope is that same as when this wrapper was generated.
@@ -9,7 +15,7 @@
915
* Time: 9:23 AM
1016
* To change this template use File | Settings | File Templates.
1117
*/
12-
public class ClassGuard implements Predicate<Object[]> {
18+
public class ClassGuard implements Guard {
1319
private final Class classGuard;
1420
private final int scopeIndex;
1521

@@ -33,6 +39,56 @@ public boolean equals(Object o) {
3339
public boolean apply(Object[] scopes) {
3440
if (scopes == null || scopes.length <= scopeIndex) return false;
3541
Object scope = scopes[scopeIndex];
36-
return (scope == null && classGuard == null) || (scope != null && classGuard == scope.getClass());
42+
if (scope != null && classGuard != scope.getClass()) return false;
43+
if (scope == null && classGuard != null) return false;
44+
return true;
3745
}
46+
47+
@Override
48+
public void addGuard(Label returnFalse, GeneratorAdapter gm, GeneratorAdapter sm, ClassWriter cw, int id, String className) {
49+
// Add the field for the class guard
50+
cw.visitField(ACC_PUBLIC | ACC_STATIC, "classGuard" + id, "Ljava/lang/Class;", null, null);
51+
52+
// Initialize the field
53+
sm.push(classGuard.getName());
54+
sm.invokeStatic(Type.getType(Class.class), Method.getMethod("Class forName(String)"));
55+
sm.putStatic(Type.getType(className), "classGuard" + id, Type.getType(Class.class));
56+
57+
Label next = new Label();
58+
Label scopeIsNull = new Label();
59+
60+
gm.loadArg(0); // scopes
61+
gm.ifNull(returnFalse); // if scopes == null return false
62+
63+
gm.loadArg(0); // scopes
64+
gm.arrayLength(); // scopes.length
65+
gm.push(scopeIndex);
66+
gm.ifICmp(LE, returnFalse); // scopes.length <= scopeIndex return false
67+
68+
gm.loadArg(0); // scopes
69+
gm.push(scopeIndex);
70+
gm.arrayLoad(Type.getType(Object.class)); // Object[]
71+
int scopeLocal = gm.newLocal(Type.getType(Object.class));
72+
gm.storeLocal(scopeLocal);
73+
int classGuardLocal = gm.newLocal(Type.getType(Class.class));
74+
gm.getStatic(Type.getType(className), "classGuard" + id, Type.getType(Class.class));
75+
gm.storeLocal(classGuardLocal);
76+
77+
gm.loadLocal(scopeLocal);
78+
gm.ifNull(scopeIsNull); // after here scope is not null
79+
80+
gm.loadLocal(scopeLocal);
81+
gm.invokeVirtual(Type.getType(Object.class), Method.getMethod("Class getClass()")); // scope.getClass()
82+
gm.loadLocal(classGuardLocal);
83+
gm.ifCmp(Type.getType(Class.class), NE, returnFalse); // if they are not equal return false
84+
85+
gm.goTo(next); // next guard
86+
87+
gm.visitLabel(scopeIsNull); // after here scope is null
88+
gm.loadLocal(classGuardLocal);
89+
gm.ifNonNull(returnFalse); // if there is a class guard, return false
90+
91+
gm.visitLabel(next); // end of method
92+
}
93+
3894
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.github.mustachejava.reflect;
2+
3+
import com.google.common.base.Predicate;
4+
import org.objectweb.asm.ClassWriter;
5+
import org.objectweb.asm.Label;
6+
import org.objectweb.asm.Opcodes;
7+
import org.objectweb.asm.commons.GeneratorAdapter;
8+
9+
/**
10+
* Optimize guards
11+
*/
12+
public interface Guard extends Predicate<Object[]>, Opcodes {
13+
void addGuard(Label returnFalse, GeneratorAdapter gm, GeneratorAdapter cm, ClassWriter cw, int id, String className);
14+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.github.mustachejava.reflect;
2+
3+
import org.junit.Test;
4+
import org.objectweb.asm.ClassWriter;
5+
import org.objectweb.asm.Label;
6+
import org.objectweb.asm.Opcodes;
7+
import org.objectweb.asm.Type;
8+
import org.objectweb.asm.commons.GeneratorAdapter;
9+
10+
import java.lang.reflect.InvocationTargetException;
11+
12+
import static junit.framework.Assert.assertFalse;
13+
import static junit.framework.Assert.assertTrue;
14+
import static org.objectweb.asm.commons.Method.getMethod;
15+
16+
/**
17+
* Test our guard compilations
18+
*/
19+
public class CompiledGuard implements Opcodes {
20+
21+
@Test
22+
public void testGuard() {
23+
ClassGuard stringClassGuard = new ClassGuard(0, "");
24+
assertTrue("string is ok", stringClassGuard.apply(new Object[]{"test"}));
25+
assertFalse("integer is not ok", stringClassGuard.apply(new Object[]{1}));
26+
}
27+
28+
public interface TestGuard {
29+
boolean test(Object[] scopes);
30+
}
31+
32+
@Test
33+
public void testCompiledGuard() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
34+
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
35+
String className = "Test";
36+
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", new String[]{TestGuard.class.getName().replace(".", "/")});
37+
cw.visitSource("Test.java", null);
38+
39+
GeneratorAdapter cm = new GeneratorAdapter(ACC_PUBLIC, getMethod("void <init> ()"), null, null, cw);
40+
cm.loadThis();
41+
cm.invokeConstructor(Type.getType(Object.class), getMethod("void <init> ()"));
42+
cm.returnValue();
43+
cm.endMethod();
44+
45+
46+
GeneratorAdapter sm = new GeneratorAdapter(ACC_PUBLIC | ACC_STATIC, getMethod("void <clinit> ()"), null, null, cw);
47+
48+
{
49+
GeneratorAdapter gm = new GeneratorAdapter(ACC_PUBLIC, getMethod("boolean test(Object[])"), null, null, cw);
50+
Label returnFalse = new Label();
51+
ClassGuard stringClassGuard = new ClassGuard(0, "");
52+
stringClassGuard.addGuard(returnFalse, gm, sm, cw, 0, className);
53+
// Makes it through the guard, success
54+
gm.push(true);
55+
gm.returnValue();
56+
// Jumps to returnFalse, failure
57+
gm.visitLabel(returnFalse);
58+
gm.push(false);
59+
gm.returnValue();
60+
gm.endMethod();
61+
}
62+
63+
sm.returnValue();
64+
sm.endMethod();
65+
66+
cw.visitEnd();
67+
Class<?> aClass = defineClass(className, cw.toByteArray());
68+
TestGuard testGuard = (TestGuard) aClass.getConstructor().newInstance();
69+
assertTrue("string is ok", testGuard.test(new Object[]{"test"}));
70+
assertFalse("integer is not ok", testGuard.test(new Object[]{1}));
71+
}
72+
73+
private static final DefiningClassLoader cl = new DefiningClassLoader();
74+
75+
private static class DefiningClassLoader extends ClassLoader {
76+
public Class<?> defineClass(final String name, final byte[] b) {
77+
return defineClass(name, b, 0, b.length);
78+
}
79+
}
80+
81+
public static Class<?> defineClass(String name, byte[] b) {
82+
return cl.defineClass(name, b);
83+
}
84+
85+
}

0 commit comments

Comments
 (0)