package nebula.simpletemplate;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import nebula.lang.Operator;
import nebula.simpletemplate.CompilerContext.Arg;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
public class Compiler implements Opcodes {
static void _Print_(MethodVisitor mv, String msg) {
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn(msg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
}
static void _IConst_(MethodVisitor mv, int i) {
if (i <= 5) {
mv.visitInsn(ICONST_0 + i);
} else {
mv.visitIntInsn(BIPUSH, i);
}
}
static Class<?> _box_(MethodVisitor mv, Class<?> clz) {
Class<?> pclz = null;
if (clz == Boolean.TYPE) {
pclz = Boolean.class;
} else if (clz == Byte.TYPE) {
pclz = Byte.class;
} else if (clz == Character.TYPE) {
pclz = Character.class;
} else if (clz == Double.TYPE) {
pclz = Double.class;
} else if (clz == Float.TYPE) {
pclz = Float.class;
} else if (clz == Short.TYPE) {
pclz = Short.class;
} else if (clz == Integer.TYPE) {
pclz = Integer.class;
} else if (clz == Long.TYPE) {
pclz = Integer.class;
} else {
throw new RuntimeException("not primary type");
}
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(pclz), "valueOf", "(" + Type.getDescriptor(clz) + ")" + Type.getDescriptor(pclz));
return pclz;
}
static class ArgRefer extends Expression<Object> {
final Var arg;
final Var argv;
ArgRefer(final Var argv, final Var var) {
this.argv = argv;
this.arg = var;
}
@Override
public Class<?> compile(String clzName, ClassWriter cw, MethodVisitor mv, CompilerContext context) {
mv.visitVarInsn(ALOAD, argv.index);
_IConst_(mv, arg.index);
mv.visitInsn(AALOAD);
Arg a = null;
if (arg.index < context.arges.size()) {
a = context.getArg(arg.index);
if (a != null) {
if (Map.class.isAssignableFrom(a.clz)) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Map.class));
return a.clz;
} else if (List.class.isAssignableFrom(a.clz)) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(List.class));
return a.clz;
} else if (String.class.isAssignableFrom(a.clz)) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(String.class));
return String.class;
} else if (StringBuilder.class.isAssignableFrom(a.clz)) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(StringBuilder.class));
return StringBuilder.class;
} else {
// mv.visitTypeInsn(CHECKCAST,
// Type.getInternalName(Object.class));
return a.clz;
}
}
}
return Object.class;
}
@Override
public String toString() {
return arg.name;
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static class Block implements Opcodes, Statement {
final List<Statement> statements;
Block(List<Statement> statements) {
this.statements = statements;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
for (Statement st : statements) {
st.compile(clzName, cw, mv, context);
}
return Void.TYPE;
}
@Override
public void exec(Object root) {
for (Statement st : statements) {
st.exec(root);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(1024);
sb.append("{\n");
for (Statement st : statements) {
sb.append(st.toString());
}
sb.append("}");
return sb.toString();
}
@Override
public String toString(CompilerContext context) {
StringBuilder sb = new StringBuilder(1024);
sb.append("{\n");
for (Statement st : statements) {
sb.append(st.toString(context));
}
sb.append("}");
return sb.toString();
}
}
/**
* A logical "and" expression.
*/
static class Conditional extends Expression<Boolean> {
final Expr<Boolean> e1;
final Expr<Boolean> e2;
final Operator op;
public Conditional(Operator op, Expr<Boolean> e1, Expr<Boolean> e2) {
this.op = op;
this.e1 = e1;
this.e2 = e2;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
// compiles e1
e1.compile(clzName, cw, mv, context);
// tests if e1 is false
mv.visitInsn(DUP);
Label end = new Label();
if (op == Operator.AND) {
mv.visitJumpInsn(IFEQ, end);
} else if (op == Operator.OR) {
mv.visitJumpInsn(IFNE, end);
} else {
throw new UnsupportedOperationException();
}
// case where e1 is true : e1 && e2 is equal to e2
mv.visitInsn(POP);
e2.compile(clzName, cw, mv, context);
// if e1 is false, e1 && e2 is equal to e1:
// we jump directly to this label, without evaluating e2
mv.visitLabel(end);
return Boolean.TYPE;
}
@Override
public String toString() {
return "(" + e1.toString() + " " + op.getSign() + " " + e2.toString() + ")";
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static class DecimalCst extends Expression<BigDecimal> {
final String text;
DecimalCst(String text) {
this.text = text;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
mv.visitTypeInsn(NEW, "java/math/BigDecimal");
mv.visitInsn(DUP);
mv.visitLdcInsn(text);
mv.visitMethodInsn(INVOKESPECIAL, "java/math/BigDecimal", "<init>", "(Ljava/lang/String;)V");
return BigDecimal.class;
}
@Override
public BigDecimal eval(Object root) {
return new BigDecimal(this.text);
}
@Override
public String toString() {
return text;
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
abstract static class Expression<T> implements Opcodes, Expr<T> {
@Override
public T eval(Object root) {
throw new UnsupportedOperationException();
}
protected void fromObject(final MethodVisitor mv, Expr<Object> expr, CompilerContext context) {
// switch (expr.getExprType(context).getRawType()) {
// case Boolean:
// mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
// mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean",
// "booleanValue", "()Z");
// break;
// case Long:
// mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
// mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue",
// "()J");
// break;
// case String:
// mv.visitTypeInsn(CHECKCAST, "java/lang/String");
// break;
// default:
// break;
// }
}
protected void toObject(final MethodVisitor mv, Expr<Object> expr, CompilerContext context) {
// switch (expr.getExprType(context).getRawType()) {
// case Boolean:
// mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf",
// "(Z)Ljava/lang/Boolean;");
// break;
// case Long:
// mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf",
// "(J)Ljava/lang/Long;");
// break;
// default:
// break;
// }
}
}
static class FieldOf extends Expression<Object> {
final Expr<Object> e1;
final String name;
FieldOf(Expr<Object> e1, String name) {
this.e1 = e1;
this.name = name;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
Class<?> retClz = null;
Class<?> parentClz = e1.compile(clzName, cw, mv, context);
Method m = null;
Field f = null;
if (Map.class.isAssignableFrom(parentClz)) {
mv.visitInsn(DUP);
Label ifEnd = new Label();
mv.visitJumpInsn(IFNULL, ifEnd);
{
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
}
mv.visitLabel(ifEnd);
retClz = Object.class;
} else if ((m = CompilerContext.getProp(parentClz, name)) != null) {
retClz = m.getReturnType();
Class<?> defineClass = m.getDeclaringClass();
if (retClz.isPrimitive()) {
mv.visitInsn(DUP);
Label ifEnd = new Label();
Label ifFalse = new Label();
mv.visitJumpInsn(IFNULL, ifFalse);
{
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(defineClass));
if (parentClz.isInterface()) {
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(defineClass), m.getName(), "()" + Type.getDescriptor(retClz));
} else {
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(defineClass), m.getName(), "()" + Type.getDescriptor(retClz));
}
mv.visitVarInsn(ALOAD, 0); // push not null
mv.visitJumpInsn(GOTO, ifEnd);
}
mv.visitLabel(ifFalse);
{
mv.visitInsn(POP);
mv.visitLdcInsn(0);
mv.visitInsn(ACONST_NULL);
}
mv.visitLabel(ifEnd);
} else {
mv.visitInsn(DUP);
Label ifEnd = new Label();
mv.visitJumpInsn(IFNULL, ifEnd);
{
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(defineClass));
if (parentClz.isInterface()) {
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(defineClass), m.getName(), "()" + Type.getDescriptor(retClz));
} else {
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(defineClass), m.getName(), "()" + Type.getDescriptor(retClz));
}
}
mv.visitLabel(ifEnd);
}
} else if ((f = CompilerContext.getField(parentClz, name)) != null) {
retClz = f.getType();
Class<?> defineClass = f.getDeclaringClass();
if (retClz.isPrimitive()) {
mv.visitInsn(DUP);
Label ifEnd = new Label();
Label ifFalse = new Label();
mv.visitJumpInsn(IFNULL, ifFalse);
{
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(defineClass));
mv.visitFieldInsn(GETFIELD, Type.getInternalName(defineClass), name, Type.getDescriptor(retClz));
mv.visitVarInsn(ALOAD, 0); // push not null
mv.visitJumpInsn(GOTO, ifEnd);
}
mv.visitLabel(ifFalse);
{
mv.visitInsn(POP);
mv.visitLdcInsn(0);
mv.visitInsn(ACONST_NULL);
}
mv.visitLabel(ifEnd);
} else {
mv.visitInsn(DUP);
Label ifEnd = new Label();
mv.visitJumpInsn(IFNULL, ifEnd);
{
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(defineClass));
mv.visitFieldInsn(GETFIELD, Type.getInternalName(defineClass), name, Type.getDescriptor(retClz));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(retClz));
}
mv.visitLabel(ifEnd);
}
} else {
throw new RuntimeException("Cannot find " + e1.toString(context) + "." + name);
}
return retClz;
}
public Class<?> compile(Label ifAllFalse, String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
Class<?> retClz = null;
Class<?> parentClz = null;
if (e1 instanceof FieldOf) {
parentClz = ((FieldOf) e1).compile(ifAllFalse, clzName, cw, mv, context);
} else {
parentClz = e1.compile(clzName, cw, mv, context);
}
mv.visitInsn(DUP);
mv.visitJumpInsn(IFNULL, ifAllFalse);
Method m = null;
Field f = null;
if (Map.class.isAssignableFrom(parentClz)) {
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
retClz = Object.class;
} else if ((m = CompilerContext.getProp(parentClz, name)) != null) {
retClz = m.getReturnType();
Class<?> defineClass = m.getDeclaringClass();
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(defineClass));
if (parentClz.isInterface()) {
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(defineClass), m.getName(), "()" + Type.getDescriptor(retClz));
} else {
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(defineClass), m.getName(), "()" + Type.getDescriptor(retClz));
}
// if (!retClz.isPrimitive()) {
// mv.visitTypeInsn(CHECKCAST, Type.getInternalName(retClz));
// }
} else if ((f = CompilerContext.getField(parentClz, name)) != null) {
retClz = f.getType();
Class<?> defineClass = f.getDeclaringClass();
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(defineClass));
mv.visitFieldInsn(GETFIELD, Type.getInternalName(defineClass), name, Type.getDescriptor(retClz));
// mv.visitTypeInsn(CHECKCAST, Type.getInternalName(retClz));
} else {
throw new RuntimeException("Cannot find " + e1.toString(context) + "." + name);
}
return retClz;
}
@Override
public Object eval(Object root) {
// return ((Entity) e1.eval()).get(name);
return null;
}
@Override
public String toString() {
return e1.toString() + "." + name.toString();
}
@Override
public String toString(CompilerContext context) {
return e1.toString() + ".\"" + name.toString() + "\"";
}
}
static class Include extends Expression<Object> {
final List<Argument> args;
final Expr<Object> group;
final Expr<String> name;
static final int THIS = 0;
static final int GROUP = 1;
static final int TEMPLATE = 2;
static final int STRINGBILDER = 3;
static final int ARGV = 4;
Include(Expr<Object> group, Expr<String> name, List<Argument> args) {
this.group = group;
this.name = name;
this.args = args;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
return compileInlineToString(clzName, cw, mv, context);
}
private Class<?> evalValue(String clzName, Expr<Object> value, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
if (value instanceof IncludeSubTemplate) {
IncludeSubTemplate sub = (IncludeSubTemplate) value;
sub.compileInlineToString(clzName, cw, mv, context);
return String.class;
} else {
Class<?> retClass = null;
if (value instanceof FieldOf) {
Label ifNull = new Label();
retClass = ((FieldOf) value).compile(ifNull, clzName, cw, mv, context);
{
if (retClass.isPrimitive()) {
retClass = _box_(mv, retClass);
}
}
mv.visitLabel(ifNull);
} else {
retClass = value.compile(clzName, cw, mv, context);
}
return retClass;
}
}
public Class<?> compileInlineToString(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
int localStringBuilder = context.locals++; // 0 + 1 = 1
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
mv.visitVarInsn(ASTORE, localStringBuilder);
_IConst_(mv, args.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
List<Class<?>> clzes = Lists.newArrayList();
Class<?> firstType = null;
if (args.size() > 0) {
mv.visitInsn(DUP);
_IConst_(mv, 0);
firstType = evalValue(clzName, args.get(0).value, cw, mv, context);
clzes.add(firstType);
mv.visitInsn(AASTORE);
for (int i = 1; i < args.size(); i++) {
mv.visitInsn(DUP);
_IConst_(mv, i);
Class<?> clz = evalValue(clzName, args.get(i).value, cw, mv, context);
mv.visitInsn(AASTORE);
clzes.add(clz);
}
}
int localSubArgv = context.locals++; // 1 + 1 = 2
mv.visitVarInsn(ASTORE, localSubArgv); // store sub argv to argv
if (args.size() == 0) {
if (this.name instanceof Name) {
String templateName = ((Name) this.name).value;
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent argv
// to
// argv
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitVarInsn(ASTORE, STRINGBILDER);
CompiledST subImpl = context.impl.nativeGroup.lookupTemplate(templateName);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
group.compile(clzName, cw, mv, context);
name.compile(clzName, cw, mv, context);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(STGroup.class), "lookupTemplate",
"(Ljava/lang/String;)" + Type.getDescriptor(CompiledST.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv
// to
}
mv.visitVarInsn(ASTORE, STRINGBILDER);
mv.visitVarInsn(ASTORE, ARGV);
} else {
// String templateName = ((StringCst)this.name).value;
//
// mv.visitVarInsn(ALOAD, ARGV); // store parent argv to
// argv
// mv.visitVarInsn(ALOAD, localSubArgv);
// mv.visitVarInsn(ASTORE, ARGV);
//
// mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent
// argv
// // to
// // argv
// mv.visitVarInsn(ALOAD, localStringBuilder);
// mv.visitVarInsn(ASTORE, STRINGBILDER);
//
// CompilerContext subContext = new
// CompilerContext(context.impl, clzes);
// Code code =
// subContext.impl.nativeGroup.lookupTemplate(templateName).code;
// code.compile(clzName, cw, mv, subContext);
//
// group.compile(clzName, cw, mv, context);
// name.compile(clzName, cw, mv, context);
// mv.visitMethodInsn(INVOKEVIRTUAL,
// Type.getInternalName(STGroup.class), "lookupTemplate",
// "(Ljava/lang/String;)" +
// Type.getDescriptor(TemplateImpl.class));
//
// mv.visitVarInsn(ASTORE, STRINGBILDER);
// mv.visitVarInsn(ASTORE, ARGV);
}
} else if (List.class.isAssignableFrom(firstType)) {
int listLocal = context.locals++; // 2+1=3
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, listLocal);
Label forCompare = new Label();
mv.visitJumpInsn(GOTO, forCompare);
Label forBegin = new Label();
mv.visitLabel(forBegin);
{
// subArgv[0] = list.next()
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitInsn(AASTORE);
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
int localFirstArg = context.locals++; // 3+1 =4
{
mv.visitVarInsn(ASTORE, localFirstArg);
doCall(clzName, cw, mv, context, localStringBuilder, localSubArgv, localFirstArg);
}
context.locals--;// localFirstArg 4-1 = 3
}
mv.visitLabel(forCompare);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, forBegin);
context.locals--;// listLocal 3-1 = 2
} else {
if (this.name instanceof Name) {
String templateName = ((Name) this.name).value;
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent argv
// to
// argv
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitVarInsn(ASTORE, STRINGBILDER);
CompiledST subImpl = context.impl.nativeGroup.lookupTemplate(templateName);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
group.compile(clzName, cw, mv, context);
name.compile(clzName, cw, mv, context);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(STGroup.class), "lookupTemplate",
"(Ljava/lang/String;)" + Type.getDescriptor(CompiledST.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv
// to
}
mv.visitVarInsn(ASTORE, STRINGBILDER);
mv.visitVarInsn(ASTORE, ARGV);
} else {
// String templateName = ((StringCst)this.name).value;
//
// mv.visitVarInsn(ALOAD, ARGV); // store parent argv to
// argv
// mv.visitVarInsn(ALOAD, localSubArgv);
// mv.visitVarInsn(ASTORE, ARGV);
//
// mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent
// argv
// // to
// // argv
// mv.visitVarInsn(ALOAD, localStringBuilder);
// mv.visitVarInsn(ASTORE, STRINGBILDER);
//
// CompilerContext subContext = new
// CompilerContext(context.impl, clzes);
// Code code =
// subContext.impl.nativeGroup.lookupTemplate(templateName).code;
// code.compile(clzName, cw, mv, subContext);
//
// group.compile(clzName, cw, mv, context);
// name.compile(clzName, cw, mv, context);
// mv.visitMethodInsn(INVOKEVIRTUAL,
// Type.getInternalName(STGroup.class), "lookupTemplate",
// "(Ljava/lang/String;)" +
// Type.getDescriptor(TemplateImpl.class));
//
// mv.visitVarInsn(ASTORE, STRINGBILDER);
// mv.visitVarInsn(ASTORE, ARGV);
}
}
context.locals--;// localSubArgv // 2-1 = 1
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(StringBuilder.class), "toString", "()Ljava/lang/String;");
context.locals--; // localStringBuilder 1-1=0
return String.class;
}
static int cntInclude = 0;
private void doCall(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context, int localStringBuilder, int localSubArgv,
int localFirstArg) {
{
FieldVisitor fv;
String templateActionFieldName, templateTemplateFieldName, templateClzFieldName;
if (this.name instanceof Name) {
templateActionFieldName = "action_" + ((Name) this.name).value + "_" + cntInclude;
templateTemplateFieldName = "temp_" + ((Name) this.name).value + "_" + cntInclude;
templateClzFieldName = "clz_" + ((Name) this.name).value + "_" + cntInclude++;
} else {
templateActionFieldName = "temp_" + cntInclude;
templateTemplateFieldName = "temp_" + cntInclude;
templateClzFieldName = "clz_" + this.name + cntInclude++;
}
{// Define field
fv = cw.visitField(ACC_PRIVATE, templateActionFieldName, Type.getDescriptor(Action.class), null, null);
fv.visitEnd();
fv = cw.visitField(ACC_PRIVATE, templateTemplateFieldName, Type.getDescriptor(CompiledST.class), null, null);
fv.visitEnd();
fv = cw.visitField(ACC_PRIVATE, templateClzFieldName, "Ljava/lang/Class;", "Ljava/lang/Class<*>;", null);
fv.visitEnd();
}
// prepare first arg
mv.visitVarInsn(ALOAD, localFirstArg);
Label lblAllEnd = new Label();
{
Label lblNotNull = new Label();
mv.visitJumpInsn(IFNULL, lblNotNull); // if(o != null)
{
// this.clz1
mv.visitVarInsn(ALOAD, THIS);
mv.visitFieldInsn(GETFIELD, clzName, templateClzFieldName, "Ljava/lang/Class;");
// arg1.getClass()
mv.visitVarInsn(ALOAD, localFirstArg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
// if this.clz1 = arg1.getClass()
Label lblIfNotEq = new Label();
mv.visitJumpInsn(IF_ACMPNE, lblIfNotEq);
{
// template1.exec(group, template, out, argv);
doInvokeActionExec(mv, clzName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
mv.visitJumpInsn(GOTO, lblAllEnd);
}
// else
mv.visitLabel(lblIfNotEq);
{
{// this.clz1 = arg1.getClass()
mv.visitVarInsn(ALOAD, THIS);
mv.visitVarInsn(ALOAD, localFirstArg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
mv.visitFieldInsn(PUTFIELD, clzName, templateClzFieldName, "Ljava/lang/Class;");
}
{// template1Action =
// template.get(o.getClass().getName(),
// tempalte1LeadingClass);
mv.visitVarInsn(ALOAD, THIS);
mv.visitInsn(DUP);
group.compile(clzName, cw, mv, context);
name.compile(clzName, cw, mv, context);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(STGroup.class), "lookupTemplate",
"(Ljava/lang/String;)" + Type.getDescriptor(CompiledST.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
// cost this
mv.visitFieldInsn(PUTFIELD, clzName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));
mv.visitInsn(DUP); // dup this
mv.visitFieldInsn(GETFIELD, clzName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));// cost
{// arg1.getClass().getName
mv.visitVarInsn(ALOAD, localFirstArg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;");
}
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(CompiledST.class), "get", "(Ljava/lang/String;[Ljava/lang/Object;)"
+ Type.getDescriptor(Action.class));
mv.visitFieldInsn(PUTFIELD, clzName, templateActionFieldName, Type.getDescriptor(Action.class));
}
doInvokeActionExec(mv, clzName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
}
mv.visitJumpInsn(GOTO, lblAllEnd);
}
mv.visitLabel(lblNotNull);
{// if (this.clz1 == Void.class) {
mv.visitVarInsn(ALOAD, THIS);
mv.visitFieldInsn(GETFIELD, clzName, templateClzFieldName, "Ljava/lang/Class;");
mv.visitLdcInsn(Type.getType("Ljava/lang/Void;"));
Label _notEq = new Label();
mv.visitJumpInsn(IF_ACMPNE, _notEq);
{
doInvokeActionExec(mv, clzName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
mv.visitJumpInsn(GOTO, lblAllEnd);
}
mv.visitLabel(_notEq);
{
{ // this.clz1 = Void.class;
mv.visitVarInsn(ALOAD, THIS);
mv.visitLdcInsn(Type.getType("Ljava/lang/Void;"));
mv.visitFieldInsn(PUTFIELD, clzName, templateClzFieldName, "Ljava/lang/Class;");
}
{
// template1 =
// template.get(Void.class.getName(), );
mv.visitVarInsn(ALOAD, THIS);
mv.visitInsn(DUP); // dup this
// group.lookupTemplate(name)
group.compile(clzName, cw, mv, context);
name.compile(clzName, cw, mv, context);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(STGroup.class), "lookupTemplate",
"(Ljava/lang/String;)" + Type.getDescriptor(CompiledST.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
// cost this
mv.visitFieldInsn(PUTFIELD, clzName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));
mv.visitInsn(DUP); // dup this
mv.visitFieldInsn(GETFIELD, clzName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));// cost
// Void.class.getName
mv.visitLdcInsn(Type.getType("Ljava/lang/Void;"));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;");
mv.visitVarInsn(ALOAD, localSubArgv);
// template.get
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(CompiledST.class), "get", "(Ljava/lang/String;[Ljava/lang/Object;)"
+ Type.getDescriptor(Action.class));
mv.visitFieldInsn(PUTFIELD, clzName, templateActionFieldName, Type.getDescriptor(Action.class));
}
doInvokeActionExec(mv, clzName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
}
}
}
mv.visitLabel(lblAllEnd);
}
}
private void doInvokeActionExec(MethodVisitor mv, String thisClassName, String templateFieldName, String templateTemplateFieldName,
int localStringBuilder, int localSubArgv) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, thisClassName, templateFieldName, Type.getDescriptor(Action.class));
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, thisClassName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Action.class), "exec",
"(" + Type.getDescriptor(STGroup.class) + Type.getDescriptor(CompiledST.class) + Type.getDescriptor(StringBuilder.class)
+ "[Ljava/lang/Object;)V");
}
public Class<?> compileInlineAppend(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
_IConst_(mv, args.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
List<Class<?>> clzes = Lists.newArrayList();
Class<?> firstType = null;
if (args.size() > 0) {
mv.visitInsn(DUP);
_IConst_(mv, 0);
firstType = evalValue(clzName, args.get(0).value, cw, mv, context);
clzes.add(firstType);
mv.visitInsn(AASTORE);
for (int i = 1; i < args.size(); i++) {
mv.visitInsn(DUP);
_IConst_(mv, i);
Class<?> clz = evalValue(clzName, args.get(i).value, cw, mv, context);
mv.visitInsn(AASTORE);
clzes.add(clz);
}
}
int localSubArgv = context.locals++; // 1 + 1 = 2
mv.visitVarInsn(ASTORE, localSubArgv); // store sub argv to argv
if (args.size() == 0) {
if (this.name instanceof StringCst) {
String templateName = ((StringCst) this.name).value;
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
CompiledST subImpl = context.impl.nativeGroup.lookupTemplate(templateName);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
group.compile(clzName, cw, mv, context);
name.compile(clzName, cw, mv, context);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(STGroup.class), "lookupTemplate",
"(Ljava/lang/String;)" + Type.getDescriptor(CompiledST.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE);
}
mv.visitVarInsn(ASTORE, ARGV);
} else {
// String templateName = ((StringCst)this.name).value;
//
// mv.visitVarInsn(ALOAD, ARGV); // store parent argv to
// argv
// mv.visitVarInsn(ALOAD, localSubArgv);
// mv.visitVarInsn(ASTORE, ARGV);
//
// mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent
// argv
// // to
// // argv
// mv.visitVarInsn(ALOAD, localStringBuilder);
// mv.visitVarInsn(ASTORE, STRINGBILDER);
//
// CompilerContext subContext = new
// CompilerContext(context.impl, clzes);
// Code code =
// subContext.impl.nativeGroup.lookupTemplate(templateName).code;
// code.compile(clzName, cw, mv, subContext);
//
// group.compile(clzName, cw, mv, context);
// name.compile(clzName, cw, mv, context);
// mv.visitMethodInsn(INVOKEVIRTUAL,
// Type.getInternalName(STGroup.class), "lookupTemplate",
// "(Ljava/lang/String;)" +
// Type.getDescriptor(TemplateImpl.class));
//
// mv.visitVarInsn(ASTORE, STRINGBILDER);
// mv.visitVarInsn(ASTORE, ARGV);
}
} else if (List.class.isAssignableFrom(firstType)) {
int localStringBuilder = 3; // 0 + 1 = 1
int listLocal = context.locals++; // 2+1=3
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, listLocal);
Label forCompare = new Label();
mv.visitJumpInsn(GOTO, forCompare);
Label forBegin = new Label();
mv.visitLabel(forBegin);
{
// subArgv[0] = list.next()
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitInsn(AASTORE);
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
int localFirstArg = context.locals++; // 3+1 =4
{
mv.visitVarInsn(ASTORE, localFirstArg);
doCall(clzName, cw, mv, context, localStringBuilder, localSubArgv, localFirstArg);
}
context.locals--;// localFirstArg 4-1 = 3
}
mv.visitLabel(forCompare);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, forBegin);
context.locals--;// listLocal 3-1 = 2
} else {
if (this.name instanceof StringCst) {
String templateName = ((StringCst) this.name).value;
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
CompiledST subImpl = context.impl.nativeGroup.lookupTemplate(templateName);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
group.compile(clzName, cw, mv, context);
name.compile(clzName, cw, mv, context);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(STGroup.class), "lookupTemplate",
"(Ljava/lang/String;)" + Type.getDescriptor(CompiledST.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv
// to
}
mv.visitVarInsn(ASTORE, ARGV);
} else {
// String templateName = ((StringCst)this.name).value;
//
// mv.visitVarInsn(ALOAD, ARGV); // store parent argv to
// argv
// mv.visitVarInsn(ALOAD, localSubArgv);
// mv.visitVarInsn(ASTORE, ARGV);
//
// mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent
// argv
// // to
// // argv
// mv.visitVarInsn(ALOAD, localStringBuilder);
// mv.visitVarInsn(ASTORE, STRINGBILDER);
//
// CompilerContext subContext = new
// CompilerContext(context.impl, clzes);
// Code code =
// subContext.impl.nativeGroup.lookupTemplate(templateName).code;
// code.compile(clzName, cw, mv, subContext);
//
// group.compile(clzName, cw, mv, context);
// name.compile(clzName, cw, mv, context);
// mv.visitMethodInsn(INVOKEVIRTUAL,
// Type.getInternalName(STGroup.class), "lookupTemplate",
// "(Ljava/lang/String;)" +
// Type.getDescriptor(TemplateImpl.class));
//
// mv.visitVarInsn(ASTORE, STRINGBILDER);
// mv.visitVarInsn(ASTORE, ARGV);
}
}
context.locals--;// localSubArgv // 2-1 = 1
return Void.class;
}
@Override
public Object eval(Object root) {
// return ((Entity) e1.eval()).get(name);
return null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name.toString());
if (args != null && args.size() > 0) {
sb.append("(");
for (Argument arg : args) {
sb.append(arg.value.toString());
sb.append(',');
}
sb.setCharAt(sb.length() - 1, ')');
} else {
sb.append("()");
}
return sb.toString();
}
@Override
public String toString(CompilerContext context) {
// Method m = null;
// if (context.isMap) {
// return e1.toString() + ".get(\"" + name.toString() + "\")";
// } else if ((m = context.getProp(name)) != null) {
// return e1.toString() + "." + m.getName() + "()";
// } else {
// throw new RuntimeException("dd");
// }
return null;
}
}
static class IncludeSubTemplate extends Expression<Object> {
// final Var argv;
// final Var sb;s
final Expr<Object> template;
final int subTemplateIndex;
final List<Argument> args;
IncludeSubTemplate(Var argv, Var sb, Expr<Object> template, int subTemplateIndex, List<Argument> args) {
// this.sb = sb;
// this.argv = argv;
this.template = template;
this.subTemplateIndex = subTemplateIndex;
this.args = args;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
return this.compileInlineToString(clzName, cw, mv, context);
}
private Class<?> evalValue(String clzName, Expr<Object> value, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
if (value instanceof IncludeSubTemplate) {
IncludeSubTemplate sub = (IncludeSubTemplate) value;
sub.compileInlineToString(clzName, cw, mv, context);
return String.class;
} else {
Class<?> retClass = null;
if (value instanceof FieldOf) {
Label ifNull = new Label();
retClass = ((FieldOf) value).compile(ifNull, clzName, cw, mv, context);
{
if (retClass.isPrimitive()) {
retClass = _box_(mv, retClass);
}
}
mv.visitLabel(ifNull);
} else {
retClass = value.compile(clzName, cw, mv, context);
}
return retClass;
}
}
static final int THIS = 0;
static final int GROUP = 1;
static final int TEMPLATE = 2;
static final int STRINGBILDER = 3;
static final int ARGV = 4;
public Class<?> compileInlineToString(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
int localStringBuilder = context.locals++; // 0 + 1 = 1
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
mv.visitVarInsn(ASTORE, localStringBuilder);
_IConst_(mv, args.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
List<Class<?>> clzes = Lists.newArrayList();
Class<?> firstType = null;
if (args.size() > 0) {
mv.visitInsn(DUP);
_IConst_(mv, 0);
firstType = evalValue(clzName, args.get(0).value, cw, mv, context);
clzes.add(firstType);
mv.visitInsn(AASTORE);
for (int i = 1; i < args.size(); i++) {
mv.visitInsn(DUP);
_IConst_(mv, i);
Class<?> clz = evalValue(clzName, args.get(i).value, cw, mv, context);
mv.visitInsn(AASTORE);
clzes.add(clz);
}
}
int localSubArgv = context.locals++; // 1 + 1 = 2
mv.visitVarInsn(ASTORE, localSubArgv); // store sub argv to argv
if (args.size() == 0) {
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent argv to
// argv
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitVarInsn(ASTORE, STRINGBILDER);
CompiledST subImpl = context.impl.implicitlyDefinedTemplates.get(subTemplateIndex);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
mv.visitInsn(DUP);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(CompiledST.class), "implicitlyDefinedTemplates", Type.getDescriptor(List.class));
_IConst_(mv, subTemplateIndex);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(List.class), "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv to
}
mv.visitVarInsn(ASTORE, STRINGBILDER); // store parent argv to
// argv
mv.visitVarInsn(ASTORE, ARGV); // store parent argv to
// argv
} else if (List.class.isAssignableFrom(firstType)) {
int listLocal = context.locals++; // 2+1=3
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, listLocal);
Label forCompare = new Label();
mv.visitJumpInsn(GOTO, forCompare);
Label forBegin = new Label();
mv.visitLabel(forBegin);
{
// subArgv[0] = list.next()
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object.class));
mv.visitInsn(AASTORE);
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
int localFirstArg = context.locals++; // 3+1 =4
{
mv.visitVarInsn(ASTORE, localFirstArg);
doCall(clzName, cw, mv, context, localStringBuilder, localSubArgv, localFirstArg);
}
context.locals--;// localFirstArg 4-1 = 3
}
mv.visitLabel(forCompare);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, forBegin);
context.locals--;// listLocal 3-1 = 2
} else {
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
mv.visitVarInsn(ALOAD, STRINGBILDER); // store parent argv to
// argv
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitVarInsn(ASTORE, STRINGBILDER);
CompiledST subImpl = context.impl.implicitlyDefinedTemplates.get(subTemplateIndex);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
mv.visitInsn(DUP);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(CompiledST.class), "implicitlyDefinedTemplates", Type.getDescriptor(List.class));
_IConst_(mv, subTemplateIndex);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(List.class), "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv to
}
mv.visitVarInsn(ASTORE, STRINGBILDER); // store parent argv to
// argv
mv.visitVarInsn(ASTORE, ARGV); // store parent argv to
// argv
}
context.locals--;// localSubArgv // 2-1 = 1
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(StringBuilder.class), "toString", "()Ljava/lang/String;");
context.locals--; // localStringBuilder 1-1=0
return String.class;
}
static int cnt = 0;
private void doCall(String thisClassName, ClassWriter cw, final MethodVisitor mv, CompilerContext context, int localStringBuilder, int localSubArgv,
int localFirstArg) {
{
FieldVisitor fv;
String templateTemplateFieldName = "temp_" + subTemplateIndex + "_" + cnt;
fv = cw.visitField(ACC_PRIVATE, templateTemplateFieldName, Type.getDescriptor(CompiledST.class), null, null);
fv.visitEnd();
String templateActionFieldName = "action_" + subTemplateIndex + "_" + cnt;
fv = cw.visitField(ACC_PRIVATE, templateActionFieldName, Type.getDescriptor(Action.class), null, null);
fv.visitEnd();
String templateClzFieldName = "clz_" + subTemplateIndex + "_" + cnt++;
fv = cw.visitField(ACC_PRIVATE, templateClzFieldName, "Ljava/lang/Class;", "Ljava/lang/Class<*>;", null);
fv.visitEnd();
// prepare first arg
mv.visitVarInsn(ALOAD, localFirstArg);
Label lblAllEnd = new Label();
{
Label lblNotNull = new Label();
mv.visitJumpInsn(IFNULL, lblNotNull); // if(o != null)
{
// this.clz1
mv.visitVarInsn(ALOAD, THIS);
mv.visitFieldInsn(GETFIELD, thisClassName, templateClzFieldName, "Ljava/lang/Class;");
// arg1.getClass()
mv.visitVarInsn(ALOAD, localFirstArg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
// if this.clz1 = arg1.getClas()
Label lblIfNotEq = new Label();
mv.visitJumpInsn(IF_ACMPNE, lblIfNotEq);
{
// template1.exec(group, template, out, argv);
doInvokeActionExec(mv, thisClassName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
mv.visitJumpInsn(GOTO, lblAllEnd);
}
// else
mv.visitLabel(lblIfNotEq);
{
{// this.clz1 = arg1.getClass()
mv.visitVarInsn(ALOAD, THIS);
mv.visitVarInsn(ALOAD, localFirstArg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
mv.visitFieldInsn(PUTFIELD, thisClassName, templateClzFieldName, "Ljava/lang/Class;");
}
{// template1Action =
// template.get(o.getClass().getName(),
// tempalte1LeadingClass);
mv.visitVarInsn(ALOAD, THIS);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, TEMPLATE);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(CompiledST.class), "implicitlyDefinedTemplates",
Type.getDescriptor(List.class));
_IConst_(mv, subTemplateIndex);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(List.class), "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
// cost this
mv.visitFieldInsn(PUTFIELD, thisClassName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));
mv.visitInsn(DUP); // dup this
mv.visitFieldInsn(GETFIELD, thisClassName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));// cost
// this
{// arg1.getClass().getName
mv.visitVarInsn(ALOAD, localFirstArg);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;");
}
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(CompiledST.class), "get", "(Ljava/lang/String;[Ljava/lang/Object;)"
+ Type.getDescriptor(Action.class));
mv.visitFieldInsn(PUTFIELD, thisClassName, templateActionFieldName, Type.getDescriptor(Action.class));
}
doInvokeActionExec(mv, thisClassName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
}
mv.visitJumpInsn(GOTO, lblAllEnd);
}
mv.visitLabel(lblNotNull);
{// if (this.clz1 == Void.class) {
mv.visitVarInsn(ALOAD, THIS);
mv.visitFieldInsn(GETFIELD, thisClassName, templateClzFieldName, "Ljava/lang/Class;");
mv.visitLdcInsn(Type.getType("Ljava/lang/Void;"));
Label _notEq = new Label();
mv.visitJumpInsn(IF_ACMPNE, _notEq);
{
doInvokeActionExec(mv, thisClassName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
mv.visitJumpInsn(GOTO, lblAllEnd);
}
mv.visitLabel(_notEq);
{
{ // this.clz1 = Void.class;
mv.visitVarInsn(ALOAD, THIS);
mv.visitLdcInsn(Type.getType("Ljava/lang/Void;"));
mv.visitFieldInsn(PUTFIELD, thisClassName, templateClzFieldName, "Ljava/lang/Class;");
}
{
// template1 =
// template.get(Void.class.getName(), );
mv.visitVarInsn(ALOAD, THIS);
mv.visitInsn(DUP);// dup this
mv.visitVarInsn(ALOAD, TEMPLATE);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(CompiledST.class), "implicitlyDefinedTemplates",
Type.getDescriptor(List.class));
_IConst_(mv, subTemplateIndex);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(List.class), "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
// cost this
mv.visitFieldInsn(PUTFIELD, thisClassName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));
mv.visitInsn(DUP); // dup this
mv.visitFieldInsn(GETFIELD, thisClassName, templateTemplateFieldName, Type.getDescriptor(CompiledST.class));// cost
// this
// Void.class.getName
mv.visitLdcInsn(Type.getType("Ljava/lang/Void;"));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;");
mv.visitVarInsn(ALOAD, localSubArgv);
// template.get
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(CompiledST.class), "get", "(Ljava/lang/String;[Ljava/lang/Object;)"
+ Type.getDescriptor(Action.class));
mv.visitFieldInsn(PUTFIELD, thisClassName, templateActionFieldName, Type.getDescriptor(Action.class));
}
doInvokeActionExec(mv, thisClassName, templateActionFieldName, templateTemplateFieldName, localStringBuilder, localSubArgv);
}
}
}
mv.visitLabel(lblAllEnd);
}
}
private void doInvokeActionExec(MethodVisitor mv, String thisClassName, String tempalteActionFieldName, String tempalteTemplateFieldName,
int localStringBuilder, int localSubArgv) {
// action
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, thisClassName, tempalteActionFieldName, Type.getDescriptor(Action.class));
// stgroup
mv.visitVarInsn(ALOAD, 1);
// template
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, thisClassName, tempalteTemplateFieldName, Type.getDescriptor(CompiledST.class));
mv.visitVarInsn(ALOAD, localStringBuilder);
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Action.class), "exec",
"(" + Type.getDescriptor(STGroup.class) + Type.getDescriptor(CompiledST.class) + Type.getDescriptor(StringBuilder.class)
+ "[Ljava/lang/Object;)V");
}
public Class<?> compileInlineAppend(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
_IConst_(mv, args.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
List<Class<?>> clzes = Lists.newArrayList();
Class<?> firstType = null;
if (args.size() > 0) {
mv.visitInsn(DUP);
_IConst_(mv, 0);
firstType = evalValue(clzName, args.get(0).value, cw, mv, context);
clzes.add(firstType);
mv.visitInsn(AASTORE);
for (int i = 1; i < args.size(); i++) {
mv.visitInsn(DUP);
_IConst_(mv, i);
Class<?> clz = evalValue(clzName, args.get(i).value, cw, mv, context);
mv.visitInsn(AASTORE);
clzes.add(clz);
}
}
int localSubArgv = context.locals++; // 1 + 1 = 2
mv.visitVarInsn(ASTORE, localSubArgv); // store sub argv to argv
if (args.size() == 0) {
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
CompiledST subImpl = context.impl.implicitlyDefinedTemplates.get(subTemplateIndex);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
mv.visitInsn(DUP);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(CompiledST.class), "implicitlyDefinedTemplates", Type.getDescriptor(List.class));
_IConst_(mv, subTemplateIndex);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(List.class), "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv to
}
mv.visitVarInsn(ASTORE, ARGV); // store parent argv to
// argv
} else if (List.class.isAssignableFrom(firstType)) {
int localStringBuilder = 3; // 0 + 1 = 1
int listLocal = context.locals++; // 2+1=3
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, listLocal);
Label forCompare = new Label();
mv.visitJumpInsn(GOTO, forCompare);
Label forBegin = new Label();
mv.visitLabel(forBegin);
{
// subArgv[0] = list.next()
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object.class));
mv.visitInsn(AASTORE);
mv.visitVarInsn(ALOAD, localSubArgv);
_IConst_(mv, 0);
mv.visitInsn(AALOAD);
int localFirstArg = context.locals++; // 3+1 =4
{
mv.visitVarInsn(ASTORE, localFirstArg);
doCall(clzName, cw, mv, context, localStringBuilder, localSubArgv, localFirstArg);
}
context.locals--;// localFirstArg 4-1 = 3
}
mv.visitLabel(forCompare);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, forBegin);
context.locals--;// listLocal 3-1 = 2
} else {
mv.visitVarInsn(ALOAD, ARGV); // store parent argv to argv
mv.visitVarInsn(ALOAD, localSubArgv);
mv.visitVarInsn(ASTORE, ARGV);
CompiledST subImpl = context.impl.implicitlyDefinedTemplates.get(subTemplateIndex);
boolean needSubTemplate = subImpl.implicitlyDefinedTemplates != null && subImpl.implicitlyDefinedTemplates.size() > 0;
if (needSubTemplate) {
mv.visitVarInsn(ALOAD, TEMPLATE);
mv.visitInsn(DUP);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(CompiledST.class), "implicitlyDefinedTemplates", Type.getDescriptor(List.class));
_IConst_(mv, subTemplateIndex);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(List.class), "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(CompiledST.class));
mv.visitVarInsn(ASTORE, TEMPLATE);
}
CompilerContext subContext = new CompilerContext(subImpl, clzes);
subContext.locals = context.locals;
subImpl.code.compile(clzName, cw, mv, subContext);
if (needSubTemplate) {
mv.visitVarInsn(ASTORE, TEMPLATE); // store parent argv to
}
mv.visitVarInsn(ASTORE, ARGV); // store parent argv to
// argv
}
context.locals--;// localSubArgv // 2-1 = 1
return Void.class;
}
@Override
public Object eval(Object root) {
return null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(template.toString());
sb.append("[" + this.subTemplateIndex + "]");
if (args != null && args.size() > 0) {
sb.append("(");
for (Argument arg : args) {
sb.append(arg.value.toString());
sb.append(',');
}
sb.setCharAt(sb.length() - 1, ')');
} else {
sb.append("()");
}
return sb.toString();
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static abstract class Const<T> extends Expression<T> {
}
static class LongCst extends Const<Long> {
final Long value;
LongCst(String text) {
this.value = Long.parseLong(text);
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
mv.visitLdcInsn(value);
return Long.TYPE;
}
@Override
public Long eval(Object root) {
return this.value;
}
@Override
public String toString(CompilerContext context) {
return String.valueOf(value);
}
}
static class Name extends Expression<String> {
final String value;
Name(String value) {
this.value = value;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
mv.visitLdcInsn(value);
return String.class;
}
@Override
public String eval(Object root) {
return this.value;
}
@Override
public String toString() {
return "'" + value.toString() + "'";
}
@Override
public String toString(CompilerContext context) {
return "'" + value.toString() + "'";
}
}
static class Not extends Expression<Boolean> {
final Expr<Boolean> e1;
Not(Expr<Boolean> e1) {
this.e1 = e1;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
// computes !e1 by evaluating 1 - e1
mv.visitInsn(ICONST_1);
e1.compile(clzName, cw, mv, context);
mv.visitInsn(ISUB);
return Boolean.TYPE;
}
@Override
public String toString() {
return "(!" + e1.toString() + ")";
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static class EvalBoolean extends Expression<Boolean> {
final Expr<?> e1;
EvalBoolean(Expr<?> e1) {
this.e1 = e1;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
Class<?> clz = e1.compile(clzName, cw, mv, context);
if (e1 instanceof FieldOf && clz.isPrimitive() ) {
Label ifFalse = new Label();
Label ifEnd = new Label();
mv.visitJumpInsn(IFNULL, ifFalse);
{
if (clz == Boolean.TYPE) {
mv.visitInsn(NOP);
} else {
mv.visitInsn(POP);
mv.visitInsn(ICONST_1);
}
mv.visitJumpInsn(GOTO, ifEnd);
}
mv.visitLabel(ifFalse);
{
mv.visitInsn(POP);
mv.visitInsn(ICONST_0);
}
mv.visitLabel(ifEnd);
} else {
if (clz == Boolean.TYPE) {
return clz;
} else {
Label ifFalse = new Label();
Label ifEnd = new Label();
mv.visitJumpInsn(IFNULL, ifFalse);
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, ifEnd);
mv.visitLabel(ifFalse);
mv.visitInsn(ICONST_0);
mv.visitLabel(ifEnd);
}
}
return Boolean.TYPE;
}
@Override
public String toString() {
return "(" + e1.toString() + ")";
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static class If implements Opcodes, Statement {
final List<Expr<?>> conditions;
final List<Statement> statements;
final Statement blockelse;
If(List<Expr<?>> conditions, List<Statement> statements, Statement blockelse) {
this.conditions = conditions;
this.statements = statements;
this.blockelse = blockelse;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
Label ifEnd = new Label();
Label ifFalse = null;
int cnt = conditions.size();
if (blockelse != null) {
cnt++;
}
for (int i = 0; i < conditions.size(); i++) {
if (cnt > 1) {
ifFalse = new Label();
} else {
ifFalse = ifEnd;
}
Expr<?> e = conditions.get(i);
Statement block = statements.get(i);
Class<?> clz = e.compile(clzName, cw, mv, context);
if (clz == Boolean.TYPE) {
mv.visitJumpInsn(IFEQ, ifFalse);
block.compile(clzName, cw, mv, context);
} else {
mv.visitJumpInsn(IFNULL, ifFalse);
block.compile(clzName, cw, mv, context);
}
if (cnt > 1) {
mv.visitJumpInsn(GOTO, ifEnd);
mv.visitLabel(ifFalse);
}
cnt--;
}
if (blockelse != null) {
blockelse.compile(clzName, cw, mv, context);
}
mv.visitLabel(ifEnd);
return Boolean.TYPE;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < conditions.size(); i++) {
Expr<?> e = conditions.get(i);
Statement block = statements.get(i);
sb.append("if");
sb.append("(");
sb.append(e.toString());
sb.append(")");
sb.append(block.toString());
}
if (blockelse != null) {
sb.append("else");
sb.append(blockelse.toString());
}
return sb.toString();
}
@Override
public String toString(CompilerContext context) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < conditions.size(); i++) {
Expr<?> e = conditions.get(i);
Statement block = statements.get(i);
sb.append("if");
sb.append("(");
sb.append(e.toString(context));
sb.append(")");
sb.append(block.toString(context));
}
if (blockelse != null) {
sb.append(blockelse.toString(context));
}
return sb.toString();
}
@Override
public void exec(Object root) {
// TODO Auto-generated method stub
}
}
// CompilerContext context;
static class Output implements Opcodes, Statement {
final Expr<?> expr;
final Expr<?> sb;
Output(Expr<?> sb, Expr<?> expr) {
this.sb = sb;
this.expr = expr;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
if (expr instanceof Const) {
sb.compile(clzName, cw, mv, context);// push sb
Class<?> clz = expr.compile(clzName, cw, mv, context);
output(clz, clzName, cw, mv, context);
mv.visitInsn(POP); // pop sb
} else if (expr instanceof IncludeSubTemplate) {
IncludeSubTemplate sub = (IncludeSubTemplate) expr;
sub.compileInlineAppend(clzName, cw, mv, context);
} else if (expr instanceof ArgRefer) {
sb.compile(clzName, cw, mv, context);
Class<?> clz = expr.compile(clzName, cw, mv, context);
if (clz.isPrimitive()) {
output(clz, clzName, cw, mv, context);
} else {
Label ifFalse = new Label();
Label ifEnd = new Label();
mv.visitInsn(DUP);
mv.visitJumpInsn(IFNULL, ifFalse);
{
output(clz, clzName, cw, mv, context);
mv.visitJumpInsn(GOTO, ifEnd);
}
mv.visitLabel(ifFalse);
{
mv.visitInsn(POP); // pop object
}
mv.visitLabel(ifEnd);
}
mv.visitInsn(POP); // pop Stringbuilder
} else if (expr instanceof FieldOf) {
sb.compile(clzName, cw, mv, context);
Label ifEnd = new Label();
Label ifNull = new Label();
Class<?> clz = ((FieldOf) expr).compile(ifNull, clzName, cw, mv, context);
{
output(clz, clzName, cw, mv, context);
mv.visitJumpInsn(GOTO, ifEnd);
}
mv.visitLabel(ifNull);
{
mv.visitInsn(POP); // pop object
}
mv.visitLabel(ifEnd);
mv.visitInsn(POP); // pop Stringbuilder
} else {
sb.compile(clzName, cw, mv, context);
Class<?> clz = expr.compile(clzName, cw, mv, context);
Label ifFalse = new Label();
Label ifEnd = new Label();
mv.visitInsn(DUP);
mv.visitJumpInsn(IFNULL, ifFalse);
{
output(clz, clzName, cw, mv, context);
mv.visitJumpInsn(GOTO, ifEnd);
}
mv.visitLabel(ifFalse);
{
mv.visitInsn(POP); // pop object
}
mv.visitLabel(ifEnd);
mv.visitInsn(POP); // pop Stringbuilder
}
return Void.TYPE;
}
private void output(Class<?> clz, String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
if (String.class == clz) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(String.class));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
} else if (clz.isPrimitive()) {
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + Type.getDescriptor(clz) + ")Ljava/lang/StringBuilder;");
} else if (StringBuilder.class == clz) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(StringBuilder.class));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + Type.getDescriptor(clz) + ")Ljava/lang/StringBuilder;");
} else if (List.class.isAssignableFrom(clz)) {
int locals = context.locals;
int listLocal = locals++;
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(List.class));
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, listLocal);
Label forCompare = new Label();
mv.visitJumpInsn(GOTO, forCompare);
Label forBegin = new Label();
mv.visitLabel(forBegin);
{
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object.class));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;");
}
mv.visitLabel(forCompare);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, forBegin);
// }
} else if (Map.class.isAssignableFrom(clz)) {
int locals = context.locals;
int listLocal = locals++;
// // initial
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Map.class));
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "values", "()Ljava/util/Collection;");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Collection", "iterator", "()Ljava/util/Iterator;");
mv.visitVarInsn(ASTORE, listLocal);
Label forCompare = new Label();
mv.visitJumpInsn(GOTO, forCompare);
Label forBegin = new Label();
mv.visitLabel(forBegin);
{
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;");
}
mv.visitLabel(forCompare);
mv.visitVarInsn(ALOAD, listLocal);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
mv.visitJumpInsn(IFNE, forBegin);
} else if (ST.class.isAssignableFrom(clz)) {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ST.class));
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(ST.class), "render", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + Type.getDescriptor(String.class) + ")Ljava/lang/StringBuilder;");
} else {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object.class));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + Type.getDescriptor(Object.class) + ")Ljava/lang/StringBuilder;");
}
}
@Override
public void exec(Object root) {
expr.eval(root);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(1024);
sb.append("\tsb.append(");
sb.append(expr);
sb.append(");\n");
return sb.toString();
}
@Override
public String toString(CompilerContext context) {
StringBuilder sb = new StringBuilder(1024);
sb.append("\tsb.append(");
sb.append(expr.toString(context));
sb.append(");\n");
return sb.toString();
}
}
static class StringCst extends Const<String> {
final String value;
StringCst(String value) {
this.value = value;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
mv.visitLdcInsn(value);
return String.class;
}
@Override
public String eval(Object root) {
return this.value;
}
@Override
public String toString() {
return "\"" + value + "\"";
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static class VarRefer extends Expression<Object> {
final Var var;
VarRefer(final Var var) {
this.var = var;
}
@Override
public Class<?> compile(String clzName, ClassWriter cw, MethodVisitor mv, CompilerContext context) {
mv.visitVarInsn(ALOAD, var.index);
return null;
}
@Override
public String toString() {
return var.name;
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
static class YesnoCst extends Const<Integer> {
final int value;
YesnoCst(boolean v) {
if (v) this.value = 1;
else this.value = 0;
}
public Class<?> compile(String clzName, ClassWriter cw, final MethodVisitor mv, CompilerContext context) {
mv.visitLdcInsn(value);
return Boolean.TYPE;
}
@Override
public Integer eval(Object root) {
return this.value;
}
@Override
public String toString() {
return value == 1 ? "Yes" : "No";
}
@Override
public String toString(CompilerContext context) {
return this.toString();
}
}
public final static int CONTEXT = 1;
public static final Map<String, String> funcs = ImmutableMap.of();
static Log log = LogFactory.getLog(Compiler.class);
public final static int PARAMS = 4;
public final static int REPOS = 2;
public final static int SYSTEM_THIS = 0;
public final static int THIS = 3;
ActionComplier actionComplier = ActionComplier.DEFAULT;
Compiler() {
if (log.isDebugEnabled()) {
if (!new File("tmp").exists()) new File("tmp/").mkdir();
}
}
// public Action compile(Class<?> rootClass, String name, Statement
// statement) {
// CompilerContext context = new CompilerContext(rootClass);
// return actionComplier.compile(context, name, statement);
// }
public void opAddArgument(List<Argument> list, Argument arg) {
arg.index = list.size();
list.add(arg);
}
public void opAddArgument(final Var argv, final List<Argument> list, List<Var> srcArgs) {
for (Var sa : srcArgs) {
// boolean find = false;
// for (Argument da : list) {
// if(da.name.equals(sa.name) ){
// find=true;
// break;
// }
// }
// if(!find){
Argument da = new Argument(new ArgRefer(argv, sa));
list.add(da);
// }
}
// TODO
}
public Expr<Object> opArg(Var argv, Var var) {
Preconditions.checkNotNull(var);
return new ArgRefer(argv, var);
}
public Argument opArgument(Expr<Object> arg) {
return new Argument(arg);
}
public Argument opArgument(String name, Argument arg) {
arg.name = name;
return arg;
}
public Expr<Boolean> opConditional(Operator op, Expr<Boolean> e1, Expr<Boolean> e2) {
return new Conditional(op, e1, e2);
}
public Expr<BigDecimal> opDecimalCst(String value) {
Preconditions.checkNotNull(value);
return new DecimalCst(value);
}
public Expr<Object> opFieldOf(Expr<Object> e1, String name) {
Preconditions.checkNotNull(e1);
return new FieldOf(e1, name);
}
public Expr<Object> opInclude(Expr<Object> group, Expr<String> name, Expr<Object> target, List<Argument> args) {
if (args == null) {
args = Lists.newArrayList();
}
args.add(0, new Argument(target));
return new Include(group, name, args);
}
public Expr<Object> opInclude(Expr<Object> group, Expr<String> name, List<Argument> args) {
if (args == null) {
args = Lists.newArrayList();
}
return new Include(group, name, args);
}
@SuppressWarnings("rawtypes")
public Expr<Object> opInclude(Expr<Object> group, Expr<String> name, List<Expr> target, List<Argument> args) {
if (args == null) {
args = Lists.newArrayList();
}
List<Argument> leading = Lists.newArrayList();
for (Expr<Object> e : target) {
leading.add(new Argument(e));
}
args.addAll(0, leading);
return new Include(group, name, args);
}
public Expr<Object> opIncludeSub(Var argv, Var sb, Expr<Object> template, int subTemplateIndex) {
List<Argument> leading = Lists.newArrayList();
return new IncludeSubTemplate(argv, sb, template, subTemplateIndex, leading);
}
public Expr<Object> opIncludeSub(Var argv, Var sb, Expr<Object> template, int subTemplateIndex, Expr<Object> target) {
List<Argument> leading = Lists.newArrayList();
leading.add(new Argument(target));
return new IncludeSubTemplate(argv, sb, template, subTemplateIndex, leading);
}
@SuppressWarnings("rawtypes")
public Expr<Object> opIncludeSub(Var argv, Var sb, Expr<Object> template, int subTemplateIndex, List<Expr> target) {
List<Argument> leading = Lists.newArrayList();
for (Expr<Object> e : target) {
leading.add(new Argument(e));
}
return new IncludeSubTemplate(argv, sb, template, subTemplateIndex, leading);
}
public Expr<Object> opLocal(Var var) {
Preconditions.checkNotNull(var);
return new VarRefer(var);
}
public Expr<Long> opLongCst(String value) {
Preconditions.checkNotNull(value);
return new LongCst(value);
}
public Expr<String> opName(String value) {
Preconditions.checkNotNull(value);
return new Name(value);
}
public Expr<Boolean> opNot(Expr<Boolean> e1) {
return new Not(e1);
}
public Expr<Boolean> opBoolean(Expr<?> e1) {
Expr<Boolean> eBoolean = new EvalBoolean(e1);
return eBoolean;
}
public Expr<String> opStringCst(String value) {
Preconditions.checkNotNull(value);
return new StringCst(value);
}
public Expr<Integer> opYesnoCst(boolean b) {
return new YesnoCst(b);
}
public Statement stBlock(List<Statement> statements) {
Preconditions.checkNotNull(statements);
return new Block(statements);
}
public Statement stIf(List<Expr<?>> condition, List<Statement> statements, Statement blockElse) {
return new If(condition, statements, blockElse);
}
public Statement stOutput(Expr<?> sb, Expr<?> expr) {
Preconditions.checkNotNull(expr);
return new Output(sb, expr);
}
public void tpReferTemplate(STGroup group, String name, CompiledST template) {
template.name = name;
group.templates.put(name, template);
}
public CompiledST tpTemplate(STGroup group, Statement statement, List<Var> arges, List<CompiledST> implicitlyDefinedTemplates) {
Preconditions.checkNotNull(group);
Preconditions.checkNotNull(statement);
List<String> argNames = Lists.newArrayList();
for (Var var : arges) {
argNames.add(var.name);
}
if (implicitlyDefinedTemplates.size() > 0) {
return new CompiledST(group, statement, argNames, implicitlyDefinedTemplates);
} else {
return new CompiledST(group, statement, argNames);
}
}
public Statement trimLastNEWLINE(Statement t1) {
Block block = (Block) t1;
if (block.statements.size() == 0) return t1;
int last = block.statements.size() - 1;
Statement lastStatement = block.statements.get(last);
if (!(lastStatement instanceof Output)) return t1;
Output output = (Output) lastStatement;
if (!(output.expr instanceof StringCst)) return t1;
StringCst s = (StringCst) output.expr;
if (!("\n".equals(s.value) || "\r\n".equals(s))) return t1;
block.statements.remove(block.statements.size() - 1);
return t1;
}
}