package nebula.lang;
import java.io.File;
import java.util.List;
import java.util.Map;
import nebula.data.Entity;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import util.NamesEncoding;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
public class AsmCompiler implements Opcodes, CompilerBase {
static Map<String, OperatorExpr> opTypes = Maps.newHashMap();
static OperatorExpr resolveOperator(Type type) {
OperatorExpr op = opTypes.get(type.getName());
if (op == null && type.getSuperType() != null) op = resolveOperator(type.getSuperType());
Preconditions.checkNotNull(op);
return op;
}
final ClassWriter cw;
// final CompilerContext context;
final MethodVisitor mv;
public AsmCompiler(ClassWriter cw, MethodVisitor mv) {
this.cw = cw;
this.mv = mv;
// this.context = context;
opTypes.put(RawTypes.Long.name(), new LongOperator());
// if (log.isDebugEnabled()) {
if (!new File("tmp").exists()) new File("tmp/").mkdir();
// }
}
@Override
public void arithmetic(Operator op, Expr<Object> e1, Expr<Object> e2) {
resolveOperator(e1.getType()).arithmetic(this, op, e1, e2);
}
@Override
public void block(List<Statement> statements) {
for (Statement st : statements) {
st.compile(this);
}
}
@Override
public void call(Expr<Object> value) {
value.compile(this);
}
@Override
public void callMethod(Expr<Entity> e1, String actionName) {
Type type = e1.getType();
type = type.getActionByName(actionName).getResideType();
String name = EntityAction.class.getSimpleName() + "_" + NamesEncoding.encode(type.getFullName(), false) + "_"
+ NamesEncoding.encode(actionName, false);
String internalName = name.replace('.', '/');
mv.visitFieldInsn(GETSTATIC, internalName, "instance", "Lnebula/lang/EntityAction;");
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
e1.compile(this);
mv.visitMethodInsn(INVOKEINTERFACE, "nebula/lang/EntityAction", "exec", "(Lnebula/lang/RuntimeContext;Lnebula/data/DataRepos;Lnebula/data/Entity;)V");
}
@Override
public void conditional(Operator op, Expr<Boolean> e1, Expr<Boolean> e2) {
// compiles e1
e1.compile(this);
// 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(this);
// if e1 is false, e1 && e2 is equal to e1:
// we jump directly to this label, without evaluating e2
mv.visitLabel(end);
}
@Override
public void constDate(long value) {
constTempral(value);
}
@Override
public void constDatetime(long value) {
constTempral(value);
}
@Override
public void constDecimal(String text) {
mv.visitTypeInsn(NEW, "java/math/BigDecimal");
mv.visitInsn(DUP);
mv.visitLdcInsn(text);
mv.visitMethodInsn(INVOKESPECIAL, "java/math/BigDecimal", "<init>", "(Ljava/lang/String;)V");
}
@Override
public void constLong(Long value) {
mv.visitLdcInsn(value);
}
@Override
public void constString(String value) {
mv.visitLdcInsn(value);
}
private void constTempral(long value) {
mv.visitTypeInsn(NEW, "org/joda/time/DateTime");
mv.visitInsn(DUP);
mv.visitLdcInsn(value);
mv.visitMethodInsn(INVOKESPECIAL, "org/joda/time/DateTime", "<init>", "(J)V");
}
@Override
public void constTime(long value) {
constTempral(value);
}
@Override
public void constTimestamp(long value) {
constTempral(value);
}
@Override
public void constYesno(int value) {
mv.visitLdcInsn(value);
}
@Override
public void datastoreGet(Expr<Object> repos, String name) {
repos.compile(this);
mv.visitLdcInsn(org.objectweb.asm.Type.getType("Ljava/lang/String;"));
mv.visitLdcInsn(org.objectweb.asm.Type.getType("Lnebula/data/Entity;"));
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/DataRepos", "define", "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Lnebula/data/DataStore;");
mv.visitTypeInsn(CHECKCAST, "nebula/data/DataStore");
mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/DataStore", "listAll", "()Ljava/util/List;");
}
@Override
public void decrement(Expr<Object> e1) {
resolveOperator(e1.getType()).decrement(this, e1);
}
protected void fromObject(final MethodVisitor mv, Type type) {
switch (type.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;
}
}
@Override
public void get(Expr<Object> list, Expr<Object> index) {
list.compile(this);
mv.visitTypeInsn(CHECKCAST, "java/util/List");
// index.index.compile(this);
index.compile(this);
mv.visitInsn(L2I);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, "nebula/data/Entity");
// mv.visitInsn(POP);
}
@Override
public void getField(Expr<Object> entity, String name, Type fieldType) {
entity.compile(this);
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/Entity", "get", "(Ljava/lang/String;)Ljava/lang/Object;");
fromObject(mv, fieldType);
}
@Override
public void increment(Expr<Object> e1) {
resolveOperator(e1.getType()).increment(this, e1);
}
@Override
public void listFilter(Expr<Object> list, List<Expr<Object>> ranges) {
list.compile(this);
mv.visitIntInsn(BIPUSH, ranges.size());
mv.visitTypeInsn(ANEWARRAY, "nebula/lang/Range");
for (int i = 0; i < ranges.size(); i++) {
mv.visitInsn(DUP);
mv.visitIntInsn(BIPUSH, i);
ranges.get(i).compile(this);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESTATIC, "nebula/lang/NebulaNative", "filter", "(Ljava/util/List;[Lnebula/lang/Range;)Ljava/util/List;");
}
@Override
public void listFilterByClause(Expr<Object> list, Expr<Object> clause, List<Expr<Object>> params) {
list.compile(this);
String clauseName = EntityClauseComplier.DEFAULT.compile(list.getType(), clause); // (clause,
// context);
mv.visitTypeInsn(NEW, clauseName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, clauseName, "<init>", "()V");
mv.visitIntInsn(BIPUSH, params.size());
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
for (int i = 0; i < params.size(); i++) {
mv.visitInsn(DUP);
mv.visitIntInsn(BIPUSH, i);
params.get(i).compile(this);
toObject(mv, params.get(i).getType());
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESTATIC, "nebula/lang/NebulaNative", "filter", "(Ljava/util/List;Lnebula/lang/Clause;[Ljava/lang/Object;)Ljava/util/List;");
}
@Override
public void listGetItem(Expr<Object> list, int index) {
// list TODO
mv.visitVarInsn(ALOAD, index);
}
public void longArithmetic(Expr<Object> e1, Expr<Object> e2, int op) {
e1.compile(this);
e2.compile(this);
mv.visitInsn(op);
}
public void longDecrement(Expr<Object> e1) {
e1.compile(this);
mv.visitInsn(LCONST_1);
mv.visitInsn(LSUB);
}
public void longIncrement(Expr<Object> e1) {
e1.compile(this);
mv.visitInsn(LCONST_1);
mv.visitInsn(LADD);
}
public void longNegates(Expr<Object> e1) {
e1.compile(this);
mv.visitInsn(LNEG);
}
public <V> void longRelational(final AsmCompiler compiler, Expr<V> e1, Expr<V> e2, int op) {
e1.compile(compiler);
e2.compile(compiler);
mv.visitInsn(LCMP);
Label ifFalse = new Label();
mv.visitJumpInsn(op, ifFalse);
mv.visitInsn(ICONST_1);
Label end = new Label();
mv.visitJumpInsn(GOTO, end);
mv.visitLabel(ifFalse);
mv.visitInsn(ICONST_0);
mv.visitLabel(end);
}
@Override
public void makeRange_0_To(Expr<Object> to) {
to.compile(this);
mv.visitInsn(L2I);
mv.visitMethodInsn(INVOKESTATIC, "nebula/lang/Range", "atMost", "(I)Lnebula/lang/Range;");
}
@Override
public void makeRange_From(Expr<Object> from) {
from.compile(this);
mv.visitInsn(L2I);
mv.visitMethodInsn(INVOKESTATIC, "nebula/lang/Range", "atLeast", "(I)Lnebula/lang/Range;");
}
@Override
public void makeRange_From_To(Expr<Object> from, Expr<Object> to) {
from.compile(this);
mv.visitInsn(L2I);
to.compile(this);
mv.visitInsn(L2I);
mv.visitMethodInsn(INVOKESTATIC, "nebula/lang/Range", "closed", "(II)Lnebula/lang/Range;");
}
@Override
public void makeRangeIndex(Expr<Object> index) {
index.compile(this);
mv.visitInsn(L2I);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESTATIC, "nebula/lang/Range", "closed", "(II)Lnebula/lang/Range;");
}
@Override
public void negates(Expr<Object> e1) {
resolveOperator(e1.getType()).negates(this, e1);
}
@Override
public void not(Expr<Boolean> e1) {
// computes !e1 by evaluating 1 - e1
mv.visitLdcInsn(new Integer(1));
e1.compile(this);
mv.visitInsn(ISUB);
}
@Override
public void paramsRefer(Expr<Object> in, int params, int index) {
mv.visitVarInsn(ALOAD, params);
mv.visitIntInsn(SIPUSH, index);
mv.visitInsn(AALOAD);
fromObject(mv, in.getType());
}
@Override
public void positive(Expr<Object> e1) {
resolveOperator(e1.getType()).positive(this, e1);
}
@Override
public void putVar(Var var, Expr<Object> initExpr) {
// compiles e1, e2, and adds an instruction to multiply the two
// values
// mv.visitLdcInsn(value);
}
@Override
public <V> void relational(final Operator op, Expr<V> e1, Expr<V> e2) {
resolveOperator(e1.getType()).relational(this, op, e1, e2);
}
@Override
public void setField(Expr<Object> parent, String name, Type fieldType, Expr<Object> value) {
parent.compile(this);
mv.visitLdcInsn(name);
value.compile(this);
toObject(mv, value.getType());
mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/Entity", "put", "(Ljava/lang/String;Ljava/lang/Object;)V");
}
static protected void print(final 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 protected void toObject(final MethodVisitor mv, Type type) {
switch (type.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;
}
}
// @Override
// public void typeRefer(String name) {
// mv.visitVarInsn(ALOAD, Compiler.REPOS);
// mv.visitLdcInsn(org.objectweb.asm.Type.getType("Ljava/lang/String;"));
// mv.visitLdcInsn(org.objectweb.asm.Type.getType("Lnebula/data/Entity;"));
// mv.visitLdcInsn(name);
// mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/DataRepos", "define",
// "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Lnebula/data/Broker;");
// mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/Broker", "get",
// "()Ljava/lang/Object;");
// mv.visitTypeInsn(CHECKCAST, "nebula/data/DataStore");
//
// //
// //
// // mv.visitVarInsn(ALOAD, 3);
// // mv.visitLdcInsn("wangshilian");
// // mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/DataStore",
// // "get", "(Ljava/lang/Object;)Lnebula/data/Timable;");
// // mv.visitTypeInsn(CHECKCAST, "nebula/data/Entity");
// // mv.visitVarInsn(ASTORE, 4);
// //
// //
// // mv.visitVarInsn(ALOAD, 1);
// // mv.visitLdcInsn("Age");
// // mv.visitVarInsn(ALOAD, 4);
// // mv.visitLdcInsn("Age");
// // mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/Entity", "get",
// // "(Ljava/lang/String;)Ljava/lang/Object;");
// // mv.visitMethodInsn(INVOKEINTERFACE, "nebula/data/Entity", "put",
// // "(Ljava/lang/String;Ljava/lang/Object;)V");
//
// }
public void varRefer(int index) {
mv.visitVarInsn(ALOAD, index);
}
@Override
public void varRefer(Var var) {
mv.visitVarInsn(ALOAD, var.index);
}
@Override
public void stIf(Expr<Object> expr, Statement stIf, Statement stElse) {
expr.compile(this);
Label ifFalse = new Label();
Label ifEnd = new Label();
mv.visitJumpInsn(IFEQ, ifFalse); // if false goto iffalse
stIf.compile(this);
mv.visitJumpInsn(GOTO, ifEnd);
mv.visitLabel(ifFalse);
stElse.compile(this);
mv.visitLabel(ifEnd);
}
@Override
public void stIf(Expr<Object> expr, Statement stIf) {
expr.compile(this);
Label ifEnd = new Label();
mv.visitJumpInsn(IFEQ, ifEnd); // if false goto iffalse
stIf.compile(this);
mv.visitLabel(ifEnd);
}
}