Skip to content

Commit 958ad9e

Browse files
committed
passing all the tests
1 parent ea03d61 commit 958ad9e

8 files changed

Lines changed: 155 additions & 15 deletions

File tree

compiler/src/main/java/com/github/mustachejava/Mustache.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,12 @@ public interface Mustache extends Code {
5858
* @param codes
5959
*/
6060
void setCodes(Code[] codes);
61+
62+
/**
63+
* Only executes the codes. Does not append the text.
64+
* @param writer
65+
* @param scopes
66+
* @return
67+
*/
68+
Writer runCodes(Writer writer, Object[] scopes);
6169
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.github.mustachejava.codes;
2+
3+
import java.io.Writer;
4+
5+
/**
6+
* Compiled code.
7+
*/
8+
public interface CompiledCodes {
9+
Writer runCodes(Writer writer, Object[] scopes);
10+
}

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,7 @@ protected Writer appendText(Writer writer) {
137137
}
138138

139139
protected Writer runCodes(Writer writer, Object[] scopes) {
140-
Code[] codes = getCodes();
141-
if (codes != null) {
142-
for (Code code : codes) {
143-
writer = code.execute(writer, scopes);
144-
}
145-
}
146-
return writer;
140+
return mustache == null ? writer : mustache.runCodes(writer, scopes);
147141
}
148142

149143
@Override

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

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,125 @@
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;
9+
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;
713

814
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;
919

1020
/**
1121
* Default Mustache
1222
*/
13-
public class DefaultMustache extends DefaultCode implements Mustache {
23+
public class DefaultMustache extends DefaultCode implements Mustache, Opcodes {
24+
private static AtomicInteger id = new AtomicInteger(0);
25+
private static final Method EXECUTE_METHOD = Method.getMethod("java.io.Writer execute(java.io.Writer, Object[])");
26+
1427
private Code[] codes;
1528
private boolean inited = false;
1629

1730
public DefaultMustache(TemplateContext tc, MustacheFactory cf, Code[] codes, String name) {
1831
super(tc, cf.getObjectHandler(), null, name, null);
19-
this.codes = codes;
32+
setCodes(codes);
2033
}
2134

2235
@Override
2336
public Code[] getCodes() {
2437
return codes;
2538
}
2639

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

32128
@Override
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.github.mustachejava.reflect;
2+
3+
/**
4+
* TODO: Edit this
5+
* <p/>
6+
* User: sam
7+
* Date: 9/2/12
8+
* Time: 4:36 PM
9+
*/
10+
public class Test {
11+
Object o1;
12+
Object o2;
13+
public Test(Object[] objects) {
14+
o1 = objects[0];
15+
o2 = objects[1];
16+
}
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.mustachejava.codes;
2+
3+
import com.github.mustachejava.DefaultMustacheFactory;
4+
import com.github.mustachejava.Mustache;
5+
import org.junit.Test;
6+
7+
public class CodeCompileTest {
8+
@Test
9+
public void testCompile() {
10+
DefaultMustacheFactory dmf = new DefaultMustacheFactory();
11+
Mustache mustache = dmf.compile("compiletest.mustache");
12+
}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{value}}

indy/src/test/java/IndyDemo.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ public static void main(String[] args) throws Throwable {
1212
IndyDemo indyDemo = new IndyDemo();
1313
for (int i = 0; i < 10; i++) {
1414
timeReflectionOH(indyDemo);
15-
timeIndyOH(indyDemo);
16-
timeIndyOHNoGuard(indyDemo);
17-
timeReflection(indyDemo);
18-
timeReflectionCached(indyDemo);
19-
timeDirect(indyDemo);
15+
// timeReflectionOH(indyDemo);
16+
// timeIndyOH(indyDemo);
17+
// timeIndyOHNoGuard(indyDemo);
18+
// timeReflection(indyDemo);
19+
// timeReflectionCached(indyDemo);
20+
// timeDirect(indyDemo);
2021
}
2122
}
2223

0 commit comments

Comments
 (0)