Skip to content

Commit 023e84d

Browse files
committed
refactor the compilation stuff into a separate package
1 parent ea69100 commit 023e84d

File tree

11 files changed

+222
-186
lines changed

11 files changed

+222
-186
lines changed

compiler/src/main/java/com/github/mustachejava/codes/DefaultMustache.java

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,17 @@
44
import com.github.mustachejava.Mustache;
55
import com.github.mustachejava.MustacheFactory;
66
import com.github.mustachejava.TemplateContext;
7-
import com.github.mustachejava.reflect.CompilableGuard;
8-
import org.objectweb.asm.ClassWriter;
7+
import com.github.mustachejava.compile.CodeCompiler;
8+
import com.github.mustachejava.compile.CompiledCodes;
99
import org.objectweb.asm.Opcodes;
10-
import org.objectweb.asm.Type;
11-
import org.objectweb.asm.commons.GeneratorAdapter;
12-
import org.objectweb.asm.commons.Method;
1310

1411
import java.io.Writer;
15-
import java.lang.reflect.Modifier;
16-
import java.util.concurrent.atomic.AtomicInteger;
17-
18-
import static org.objectweb.asm.commons.Method.getMethod;
1912

2013
/**
2114
* Default Mustache
2215
*/
2316
public class DefaultMustache extends DefaultCode implements Mustache, Opcodes {
2417
private static boolean compile = Boolean.getBoolean("mustache.compile");
25-
private static AtomicInteger id = new AtomicInteger(0);
26-
private static final Method EXECUTE_METHOD = Method.getMethod("java.io.Writer execute(java.io.Writer, Object[])");
2718

2819
private Code[] codes;
2920
private boolean inited = false;
@@ -59,79 +50,7 @@ public final void setCodes(Code[] newcodes) {
5950
if (!compile || codes == null) {
6051
compiledCodes = null;
6152
} else {
62-
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
63-
int classId = id.incrementAndGet();
64-
String className = "com.github.mustachejava.codes.RunCodes" + classId;
65-
String internalClassName = className.replace(".", "/");
66-
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, internalClassName, null, "java/lang/Object", new String[]{CompiledCodes.class.getName().replace(".", "/")});
67-
cw.visitSource("runCodes", null);
68-
69-
GeneratorAdapter cm = new GeneratorAdapter(ACC_PUBLIC, getMethod("void <init> (com.github.mustachejava.Code[])"), null, null, cw);
70-
cm.loadThis();
71-
cm.invokeConstructor(Type.getType(Object.class), getMethod("void <init> ()"));
72-
{
73-
GeneratorAdapter gm = new GeneratorAdapter(ACC_PUBLIC, getMethod("java.io.Writer runCodes(java.io.Writer, Object[])"), null, null, cw);
74-
int writerLocal = gm.newLocal(Type.getType(Writer.class));
75-
// Put the writer in our local
76-
gm.loadArg(0);
77-
gm.storeLocal(writerLocal);
78-
int fieldNum = 0;
79-
for (Code newcode : newcodes) {
80-
Class<? extends Code> codeClass = newcode.getClass();
81-
Class fieldClass = codeClass;
82-
while(fieldClass.isAnonymousClass() || fieldClass.isLocalClass() || (fieldClass.getModifiers() & Modifier.PUBLIC) == 0) {
83-
if (codeClass.getSuperclass() != Object.class && codeClass.getSuperclass().isAssignableFrom(Code.class)) {
84-
fieldClass = codeClass.getSuperclass();
85-
} else {
86-
fieldClass = Code.class;
87-
}
88-
}
89-
Type fieldType = Type.getType(fieldClass);
90-
91-
// add a field for each one to the class
92-
String fieldName = "code" + fieldNum;
93-
cw.visitField(ACC_PRIVATE | ACC_FINAL, fieldName, fieldType.getDescriptor(), null, null);
94-
95-
// set the fields to the passed in values in the constructor
96-
cm.loadThis();
97-
cm.loadArg(0);
98-
cm.push(fieldNum);
99-
cm.arrayLoad(Type.getType(Code.class));
100-
cm.checkCast(fieldType);
101-
cm.putField(Type.getType(internalClassName), fieldName, fieldType);
102-
103-
// writer, scopes)
104-
gm.loadThis();
105-
gm.getField(Type.getType(internalClassName), fieldName, fieldType);
106-
gm.loadLocal(writerLocal);
107-
gm.loadArg(1);
108-
// code.execute(
109-
if (fieldClass.isInterface()) {
110-
gm.invokeInterface(fieldType, EXECUTE_METHOD);
111-
} else {
112-
gm.invokeVirtual(fieldType, EXECUTE_METHOD);
113-
}
114-
// writer =
115-
gm.storeLocal(writerLocal);
116-
117-
fieldNum++;
118-
}
119-
cm.returnValue();
120-
cm.endMethod();
121-
122-
// Load writer and return it
123-
gm.loadLocal(writerLocal);
124-
gm.returnValue();
125-
gm.endMethod();
126-
}
127-
128-
cw.visitEnd();
129-
Class<?> aClass = CompilableGuard.Compiler.defineClass(className, cw.toByteArray());
130-
try {
131-
compiledCodes = (CompiledCodes) aClass.getConstructor(Code[].class).newInstance(new Object[] { codes });
132-
} catch (Exception e) {
133-
e.printStackTrace();
134-
}
53+
compiledCodes = CodeCompiler.compile(codes, newcodes);
13554
}
13655
}
13756

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.github.mustachejava.compile;
2+
3+
import com.github.mustachejava.Code;
4+
import org.objectweb.asm.ClassWriter;
5+
import org.objectweb.asm.Opcodes;
6+
import org.objectweb.asm.Type;
7+
import org.objectweb.asm.commons.GeneratorAdapter;
8+
import org.objectweb.asm.commons.Method;
9+
10+
import java.io.Writer;
11+
import java.lang.reflect.Modifier;
12+
import java.util.concurrent.atomic.AtomicInteger;
13+
14+
import static org.objectweb.asm.commons.Method.getMethod;
15+
16+
/**
17+
* Compile a list of codes to execute down to a single method.
18+
*/
19+
public class CodeCompiler {
20+
private static AtomicInteger id = new AtomicInteger(0);
21+
private static final Method EXECUTE_METHOD = Method.getMethod("java.io.Writer execute(java.io.Writer, Object[])");
22+
23+
public static CompiledCodes compile(Code[] codes, Code[] newcodes) {
24+
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
25+
int classId = id.incrementAndGet();
26+
String className = "com.github.mustachejava.codes.RunCodes" + classId;
27+
String internalClassName = className.replace(".", "/");
28+
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, internalClassName, null, "java/lang/Object", new String[]{CompiledCodes.class.getName().replace(".", "/")});
29+
cw.visitSource("runCodes", null);
30+
31+
GeneratorAdapter cm = new GeneratorAdapter(Opcodes.ACC_PUBLIC, getMethod("void <init> (com.github.mustachejava.Code[])"), null, null, cw);
32+
cm.loadThis();
33+
cm.invokeConstructor(Type.getType(Object.class), getMethod("void <init> ()"));
34+
{
35+
GeneratorAdapter gm = new GeneratorAdapter(Opcodes.ACC_PUBLIC, getMethod("java.io.Writer runCodes(java.io.Writer, Object[])"), null, null, cw);
36+
int writerLocal = gm.newLocal(Type.getType(Writer.class));
37+
// Put the writer in our local
38+
gm.loadArg(0);
39+
gm.storeLocal(writerLocal);
40+
int fieldNum = 0;
41+
for (Code newcode : newcodes) {
42+
Class<? extends Code> codeClass = newcode.getClass();
43+
Class fieldClass = codeClass;
44+
while(fieldClass.isAnonymousClass() || fieldClass.isLocalClass() || (fieldClass.getModifiers() & Modifier.PUBLIC) == 0) {
45+
if (codeClass.getSuperclass() != Object.class && codeClass.getSuperclass().isAssignableFrom(Code.class)) {
46+
fieldClass = codeClass.getSuperclass();
47+
} else {
48+
fieldClass = Code.class;
49+
}
50+
}
51+
Type fieldType = Type.getType(fieldClass);
52+
53+
// add a field for each one to the class
54+
String fieldName = "code" + fieldNum;
55+
cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, fieldName, fieldType.getDescriptor(), null, null);
56+
57+
// set the fields to the passed in values in the constructor
58+
cm.loadThis();
59+
cm.loadArg(0);
60+
cm.push(fieldNum);
61+
cm.arrayLoad(Type.getType(Code.class));
62+
cm.checkCast(fieldType);
63+
cm.putField(Type.getType(internalClassName), fieldName, fieldType);
64+
65+
// writer, scopes)
66+
gm.loadThis();
67+
gm.getField(Type.getType(internalClassName), fieldName, fieldType);
68+
gm.loadLocal(writerLocal);
69+
gm.loadArg(1);
70+
// code.execute(
71+
if (fieldClass.isInterface()) {
72+
gm.invokeInterface(fieldType, EXECUTE_METHOD);
73+
} else {
74+
gm.invokeVirtual(fieldType, EXECUTE_METHOD);
75+
}
76+
// writer =
77+
gm.storeLocal(writerLocal);
78+
79+
fieldNum++;
80+
}
81+
cm.returnValue();
82+
cm.endMethod();
83+
84+
// Load writer and return it
85+
gm.loadLocal(writerLocal);
86+
gm.returnValue();
87+
gm.endMethod();
88+
}
89+
90+
cw.visitEnd();
91+
Class<?> aClass = GuardCompiler.defineClass(className, cw.toByteArray());
92+
try {
93+
return (CompiledCodes) aClass.getConstructor(Code[].class).newInstance(new Object[] {codes});
94+
} catch (Exception e) {
95+
e.printStackTrace();
96+
return null;
97+
}
98+
}
99+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.github.mustachejava.compile;
2+
3+
import com.github.mustachejava.reflect.Guard;
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+
import java.util.concurrent.atomic.AtomicInteger;
10+
11+
/**
12+
* Optimizable guards
13+
*/
14+
public interface CompilableGuard extends Guard, Opcodes {
15+
void addGuard(Label returnFalse, GeneratorAdapter gm, GeneratorAdapter sm, ClassWriter cw, AtomicInteger id, String className);
16+
}

compiler/src/main/java/com/github/mustachejava/codes/CompiledCodes.java renamed to compiler/src/main/java/com/github/mustachejava/compile/CompiledCodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.github.mustachejava.codes;
1+
package com.github.mustachejava.compile;
22

33
import java.io.Writer;
44

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.github.mustachejava.compile;
2+
3+
import com.github.mustachejava.reflect.Guard;
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+
import java.util.ArrayList;
12+
import java.util.List;
13+
import java.util.concurrent.atomic.AtomicInteger;
14+
15+
import static org.objectweb.asm.commons.Method.getMethod;
16+
17+
/**
18+
* Compiles Compilable Guards.
19+
*/
20+
public class GuardCompiler {
21+
private static AtomicInteger id = new AtomicInteger(0);
22+
23+
public static Guard[] compile(Guard[] guards) {
24+
List<Guard> finalGuards = new ArrayList<Guard>();
25+
List<CompilableGuard> compilableGuards = new ArrayList<CompilableGuard>();
26+
for (Guard guard : guards) {
27+
if (guard instanceof CompilableGuard) {
28+
compilableGuards.add((CompilableGuard) guard);
29+
} else {
30+
finalGuards.add(guard);
31+
}
32+
}
33+
try {
34+
Guard compiledGuard = compile("compiledguards", compilableGuards);
35+
finalGuards.add(0, compiledGuard);
36+
return finalGuards.toArray(new Guard[finalGuards.size()]);
37+
} catch (Exception e) {
38+
e.printStackTrace();
39+
return guards;
40+
}
41+
}
42+
43+
public static Guard compile(String source, Iterable<CompilableGuard> guards) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
44+
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
45+
int classId = id.incrementAndGet();
46+
String className = "com.github.mustachejava.reflect.CompiledGuards" + classId;
47+
String internalClassName = className.replace(".", "/");
48+
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, internalClassName, null, "java/lang/Object", new String[]{Guard.class.getName().replace(".", "/")});
49+
cw.visitSource(source, null);
50+
51+
GeneratorAdapter cm = new GeneratorAdapter(Opcodes.ACC_PUBLIC, getMethod("void <init> ()"), null, null, cw);
52+
cm.loadThis();
53+
cm.invokeConstructor(Type.getType(Object.class), getMethod("void <init> ()"));
54+
cm.returnValue();
55+
cm.endMethod();
56+
57+
GeneratorAdapter sm = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, getMethod("void <clinit> ()"), null, null, cw);
58+
59+
{
60+
GeneratorAdapter gm = new GeneratorAdapter(Opcodes.ACC_PUBLIC, getMethod("boolean apply(Object[])"), null, null, cw);
61+
Label returnFalse = new Label();
62+
for (CompilableGuard guard : guards) {
63+
guard.addGuard(returnFalse, gm, sm, cw, id, internalClassName);
64+
}
65+
66+
// Makes it through the guard, success
67+
gm.push(true);
68+
gm.returnValue();
69+
// Jumps to returnFalse, failure
70+
gm.visitLabel(returnFalse);
71+
gm.push(false);
72+
gm.returnValue();
73+
gm.endMethod();
74+
}
75+
76+
sm.returnValue();
77+
sm.endMethod();
78+
79+
cw.visitEnd();
80+
Class<?> aClass = defineClass(className, cw.toByteArray());
81+
return (Guard) aClass.getConstructor().newInstance();
82+
}
83+
84+
private static final DefiningClassLoader cl = new DefiningClassLoader();
85+
86+
private static class DefiningClassLoader extends ClassLoader {
87+
public Class<?> defineClass(final String name, final byte[] b) {
88+
return defineClass(name, b, 0, b.length);
89+
}
90+
}
91+
92+
public static Class<?> defineClass(String name, byte[] b) {
93+
return cl.defineClass(name, b);
94+
}
95+
96+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.mustachejava.reflect;
22

3+
import com.github.mustachejava.compile.CompilableGuard;
34
import org.objectweb.asm.ClassWriter;
45
import org.objectweb.asm.Label;
56
import org.objectweb.asm.Type;

0 commit comments

Comments
 (0)