|
4 | 4 | import com.github.mustachejava.Mustache; |
5 | 5 | import com.github.mustachejava.MustacheFactory; |
6 | 6 | 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; |
7 | 13 |
|
8 | 14 | 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; |
9 | 19 |
|
10 | 20 | /** |
11 | 21 | * Default Mustache |
12 | 22 | */ |
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 | + |
14 | 27 | private Code[] codes; |
15 | 28 | private boolean inited = false; |
16 | 29 |
|
17 | 30 | public DefaultMustache(TemplateContext tc, MustacheFactory cf, Code[] codes, String name) { |
18 | 31 | super(tc, cf.getObjectHandler(), null, name, null); |
19 | | - this.codes = codes; |
| 32 | + setCodes(codes); |
20 | 33 | } |
21 | 34 |
|
22 | 35 | @Override |
23 | 36 | public Code[] getCodes() { |
24 | 37 | return codes; |
25 | 38 | } |
26 | 39 |
|
| 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 | + |
27 | 46 | @Override |
28 | | - public void setCodes(Code[] newcodes) { |
| 47 | + public final void setCodes(Code[] newcodes) { |
29 | 48 | 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 | + } |
30 | 126 | } |
31 | 127 |
|
32 | 128 | @Override |
|
0 commit comments