/**
* Copyright (C) 2010 dennis zhuang (killme2008@gmail.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
**/
package com.googlecode.aviator.code.asm;
import static com.googlecode.aviator.asm.Opcodes.ACC_FINAL;
import static com.googlecode.aviator.asm.Opcodes.ACC_PRIVATE;
import static com.googlecode.aviator.asm.Opcodes.ACC_PUBLIC;
import static com.googlecode.aviator.asm.Opcodes.ACC_SUPER;
import static com.googlecode.aviator.asm.Opcodes.ACONST_NULL;
import static com.googlecode.aviator.asm.Opcodes.ALOAD;
import static com.googlecode.aviator.asm.Opcodes.ARETURN;
import static com.googlecode.aviator.asm.Opcodes.ASTORE;
import static com.googlecode.aviator.asm.Opcodes.CHECKCAST;
import static com.googlecode.aviator.asm.Opcodes.DUP;
import static com.googlecode.aviator.asm.Opcodes.GETFIELD;
import static com.googlecode.aviator.asm.Opcodes.GETSTATIC;
import static com.googlecode.aviator.asm.Opcodes.GOTO;
import static com.googlecode.aviator.asm.Opcodes.IFEQ;
import static com.googlecode.aviator.asm.Opcodes.IFGE;
import static com.googlecode.aviator.asm.Opcodes.IFGT;
import static com.googlecode.aviator.asm.Opcodes.IFLE;
import static com.googlecode.aviator.asm.Opcodes.IFLT;
import static com.googlecode.aviator.asm.Opcodes.IFNE;
import static com.googlecode.aviator.asm.Opcodes.INVOKEINTERFACE;
import static com.googlecode.aviator.asm.Opcodes.INVOKESPECIAL;
import static com.googlecode.aviator.asm.Opcodes.INVOKESTATIC;
import static com.googlecode.aviator.asm.Opcodes.INVOKEVIRTUAL;
import static com.googlecode.aviator.asm.Opcodes.NEW;
import static com.googlecode.aviator.asm.Opcodes.PUTFIELD;
import static com.googlecode.aviator.asm.Opcodes.RETURN;
import static com.googlecode.aviator.asm.Opcodes.SWAP;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicLong;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.asm.ClassVisitor;
import com.googlecode.aviator.asm.ClassWriter;
import com.googlecode.aviator.asm.Label;
import com.googlecode.aviator.asm.MethodVisitor;
import com.googlecode.aviator.asm.Opcodes;
import com.googlecode.aviator.asm.util.CheckClassAdapter;
import com.googlecode.aviator.asm.util.TraceClassVisitor;
import com.googlecode.aviator.code.CodeGenerator;
import com.googlecode.aviator.exception.CompileExpressionErrorException;
import com.googlecode.aviator.lexer.token.NumberToken;
import com.googlecode.aviator.lexer.token.Token;
import com.googlecode.aviator.lexer.token.Variable;
import com.googlecode.aviator.parser.AviatorClassLoader;
import com.googlecode.aviator.utils.TypeUtils;
/**
* Code generator using asm
*
* @author dennis
*
*/
public class ASMCodeGenerator implements CodeGenerator {
private static final String FIELD_PREFIX = "var_";
// Class Writer to generate class
private final ClassWriter classWriter;
// Trace visitor
private ClassVisitor traceClassVisitor;
// Check visitor
private ClassVisitor checkClassAdapter;
// Method visitor
private MethodVisitor mv;
// Class name
private final String className;
// Class loader to define generated class
private final AviatorClassLoader classLoader;
private static final AtomicLong CLASS_COUNTER = new AtomicLong();
/**
* Operands count to check stack frames
*/
private int operandsCount = 0;
private int maxStacks = 0;
private int maxLocals = 2;
private int fieldCounter = 0;
private final Map<String/* variable name */, String/* inner var name */> innerVarMap =
new HashMap<String, String>();
private final Map<String/* method name */, String/* inner method name */> innerMethodMap =
new HashMap<String, String>();
private Map<String, Integer/* counter */> varTokens = new LinkedHashMap<String, Integer>();
private Map<String, Integer/* counter */> methodTokens = new HashMap<String, Integer>();
private final Map<Label, Map<String/* inner name */, Integer/* local index */>> labelNameIndexMap =
new HashMap<Label, Map<String, Integer>>();
private static final Label START_LABEL = new Label();
private Label currentLabel = START_LABEL;
private void setMaxStacks(int newMaxStacks) {
if (newMaxStacks > this.maxStacks) {
this.maxStacks = newMaxStacks;
}
}
public ASMCodeGenerator(AviatorClassLoader classLoader, OutputStream traceOut, boolean trace) {
this.classLoader = classLoader;
// Generate inner class name
this.className = "Script_" + System.currentTimeMillis() + "_" + CLASS_COUNTER.getAndIncrement();
// Auto compute frames
this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
if (trace) {
this.traceClassVisitor = new TraceClassVisitor(this.classWriter, new PrintWriter(traceOut));
this.checkClassAdapter = new CheckClassAdapter(this.traceClassVisitor);
}
else {
this.checkClassAdapter = new CheckClassAdapter(this.classWriter);
}
this.visitClass();
}
public void start() {
this.makeConstructor();
this.startVisitMethodCode();
}
private void startVisitMethodCode() {
this.mv =
this.checkClassAdapter.visitMethod(ACC_PUBLIC + +ACC_FINAL, "execute0",
"(Ljava/util/Map;)Ljava/lang/Object;",
"(Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)Ljava/lang/Object;", null);
this.mv.visitCode();
}
private void endVisitMethodCode() {
if (this.operandsCount > 0) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "getValue",
"(Ljava/util/Map;)Ljava/lang/Object;");
this.mv.visitInsn(ARETURN);
this.popOperand();
this.popOperand();
}
else {
this.mv.visitInsn(ACONST_NULL);
this.mv.visitInsn(ARETURN);
pushOperand();
this.popOperand();
}
if (this.operandsCount > 0) {
throw new CompileExpressionErrorException("operand stack is not empty,count=" + this.operandsCount);
}
this.mv.visitMaxs(this.maxStacks, this.maxLocals);
this.mv.visitEnd();
}
private void endVisitClass() {
this.checkClassAdapter.visitEnd();
}
/**
* Make a default constructor
*/
private void makeConstructor() {
{
this.mv = this.checkClassAdapter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/util/List;)V", null, null);
this.mv.visitCode();
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitVarInsn(ALOAD, 1);
this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/ClassExpression", "<init>",
"(Ljava/util/List;)V");
if (!this.innerVarMap.isEmpty()) {
for (Map.Entry<String, String> entry : this.innerVarMap.entrySet()) {
String outterName = entry.getKey();
String innerName = entry.getValue();
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorJavaType");
this.mv.visitInsn(DUP);
this.mv.visitLdcInsn(outterName);
this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorJavaType",
"<init>", "(Ljava/lang/String;)V");
this.mv.visitFieldInsn(PUTFIELD, this.className, innerName,
"Lcom/googlecode/aviator/runtime/type/AviatorJavaType;");
}
}
if (!this.innerMethodMap.isEmpty()) {
for (Map.Entry<String, String> entry : this.innerMethodMap.entrySet()) {
String outterName = entry.getKey();
String innerName = entry.getValue();
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitLdcInsn(outterName);
this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/AviatorEvaluator", "getFunction",
"(Ljava/lang/String;)Lcom/googlecode/aviator/runtime/type/AviatorFunction;");
this.mv.visitFieldInsn(PUTFIELD, this.className, innerName,
"Lcom/googlecode/aviator/runtime/type/AviatorFunction;");
}
}
this.mv.visitInsn(RETURN);
this.mv.visitMaxs(4, 1);
this.mv.visitEnd();
}
}
private void visitClass() {
this.checkClassAdapter.visit(AviatorEvaluator.BYTECODE_VER, ACC_PUBLIC + ACC_SUPER, this.className, null,
"com/googlecode/aviator/ClassExpression", null);
}
/**
* Make a label
*
* @return
*/
private Label makeLabel() {
return new Label();
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onAdd(com.googlecode.aviator
* .lexer.token.Token)
*/
@Override
public void onAdd(Token<?> lookhead) {
this.doArthOperation("add");
}
/**
* Do arithmetic operation
*
* @param methodName
*/
private void doArthOperation(String methodName) {
this.loadEnv();
this.mv
.visitMethodInsn(
INVOKEVIRTUAL,
"com/googlecode/aviator/runtime/type/AviatorObject",
methodName,
"(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand();
this.popOperand();
}
/**
* Pop a operand from stack
*/
private void popOperand() {
this.operandsCount--;
}
/**
* Pop a operand from stack
*/
private void popOperand(int n) {
this.operandsCount -= n;
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onSub(com.googlecode.aviator
* .lexer.token.Token)
*/
@Override
public void onSub(Token<?> lookhead) {
this.doArthOperation("sub");
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onMult(com.googlecode.aviator
* .lexer.token.Token)
*/
@Override
public void onMult(Token<?> lookhead) {
this.doArthOperation("mult");
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onDiv(com.googlecode.aviator
* .lexer.token.Token)
*/
@Override
public void onDiv(Token<?> lookhead) {
this.doArthOperation("div");
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onMod(com.googlecode.aviator
* .lexer.token.Token)
*/
@Override
public void onMod(Token<?> lookhead) {
this.doArthOperation("mod");
}
/**
* Do logic operation "&&" left operand
*/
@Override
public void onAndLeft(Token<?> lookhead) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
"(Ljava/util/Map;)Z");
Label l0 = this.makeLabel();
this.pushLabel0(l0);
this.mv.visitJumpInsn(IFEQ, l0);
this.popOperand(); // boolean object
this.popOperand(); // environment
}
private void pushLabel0(Label l0) {
this.l0stack.push(l0);
}
/**
* Do logic operation "&&" right operand
*/
@Override
public void onAndRight(Token<?> lookhead) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
"(Ljava/util/Map;)Z");
this.mv.visitJumpInsn(IFEQ, this.peekLabel0());
// Result is true
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
Label l1 = this.makeLabel();
this.mv.visitJumpInsn(GOTO, l1);
this.visitLabel(this.popLabel0());
// Result is false
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
this.visitLabel(l1);
this.popOperand(); // boolean object
this.popOperand(); // environment
pushOperand();
}
/**
* Label stack for ternary operator
*/
private final Stack<Label> l0stack = new Stack<Label>();
private final Stack<Label> l1stack = new Stack<Label>();
@Override
public void onTernaryBoolean(Token<?> lookhead) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
"(Ljava/util/Map;)Z");
Label l0 = this.makeLabel();
Label l1 = this.makeLabel();
this.pushLabel0(l0);
this.pushLabel1(l1);
this.mv.visitJumpInsn(IFEQ, l0);
this.popOperand();
this.popOperand();
this.pushOperand(1); // add two booleans
this.popOperand(); // pop the last result
}
private void pushLabel1(Label l1) {
this.l1stack.push(l1);
}
@Override
public void onTernaryLeft(Token<?> lookhead) {
this.mv.visitJumpInsn(GOTO, this.peekLabel1());
this.visitLabel(this.popLabel0());
this.popOperand(); // pop one boolean
}
private Label peekLabel1() {
return this.l1stack.peek();
}
@Override
public void onTernaryRight(Token<?> lookhead) {
this.visitLabel(this.popLabel1());
this.popOperand(); // pop one boolean
}
private Label popLabel1() {
return this.l1stack.pop();
}
/**
* Do logic operation "||" right operand
*/
@Override
public void onJoinRight(Token<?> lookhead) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
"(Ljava/util/Map;)Z");
Label l1 = this.makeLabel();
this.mv.visitJumpInsn(IFNE, this.peekLabel0());
// Result is False
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
this.mv.visitJumpInsn(GOTO, l1);
this.visitLabel(this.popLabel0());
// Result is True
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
this.visitLabel(l1);
this.popOperand();
this.popOperand();
pushOperand();
}
private void visitLabel(Label label) {
this.mv.visitLabel(label);
this.currentLabel = label;
}
private Label peekLabel0() {
return this.l0stack.peek();
}
private Label popLabel0() {
return this.l0stack.pop();
}
/**
* Do logic operation "||" left operand
*/
@Override
public void onJoinLeft(Token<?> lookhead) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
"(Ljava/util/Map;)Z");
Label l0 = this.makeLabel();
this.pushLabel0(l0);
this.mv.visitJumpInsn(IFNE, l0);
this.popOperand();
this.popOperand();
}
@Override
public void onEq(Token<?> lookhead) {
this.doCompareAndJump(IFNE);
}
@Override
public void onMatch(Token<?> lookhead) {
this.mv.visitInsn(SWAP);
this.loadEnv();
this.mv
.visitMethodInsn(
INVOKEVIRTUAL,
"com/googlecode/aviator/runtime/type/AviatorObject",
"match",
"(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand();
this.popOperand();
this.popOperand();
pushOperand();
}
@Override
public void onNeq(Token<?> lookhead) {
this.doCompareAndJump(IFEQ);
}
private void doCompareAndJump(int ints) {
this.loadEnv();
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "compare",
"(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)I");
Label l0 = this.makeLabel();
Label l1 = this.makeLabel();
this.mv.visitJumpInsn(ints, l0);
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
this.mv.visitJumpInsn(GOTO, l1);
this.visitLabel(l0);
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
this.visitLabel(l1);
this.popOperand();
this.popOperand();
this.popOperand();
pushOperand();
}
@Override
public void onGe(Token<?> lookhead) {
this.doCompareAndJump(IFLT);
}
@Override
public void onGt(Token<?> lookhead) {
this.doCompareAndJump(IFLE);
}
@Override
public void onLe(Token<?> lookhead) {
this.doCompareAndJump(IFGT);
}
@Override
public void onLt(Token<?> lookhead) {
this.doCompareAndJump(IFGE);
}
/**
*
* @param extras
* 额外的栈空间大小
*/
public void pushOperand(int extras) {
this.operandsCount++;
this.operandsCount += extras;
this.setMaxStacks(this.operandsCount);
}
/**
* Logic operation '!'
*/
@Override
public void onNot(Token<?> lookhead) {
pushOperand();
this.mv.visitTypeInsn(CHECKCAST, "com/googlecode/aviator/runtime/type/AviatorObject");
this.mv.visitVarInsn(ALOAD, 1);
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "not",
"(Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand();
this.popOperand();
pushOperand();
}
/**
* Bit operation '~'
*/
@Override
public void onBitNot(Token<?> lookhead) {
pushOperand();
this.mv.visitTypeInsn(CHECKCAST, "com/googlecode/aviator/runtime/type/AviatorObject");
this.mv.visitVarInsn(ALOAD, 1);
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "bitNot",
"(Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand();
this.popOperand();
pushOperand();
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onNeg(com.googlecode.aviator
* .lexer.token.Token, int)
*/
@Override
public void onNeg(Token<?> lookhead) {
pushOperand();
this.mv.visitTypeInsn(CHECKCAST, "com/googlecode/aviator/runtime/type/AviatorObject");
this.mv.visitVarInsn(ALOAD, 1);
this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "neg",
"(Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand();
this.popOperand();
pushOperand();
}
/*
* (non-Javadoc)
*
* @see com.googlecode.aviator.code.CodeGenerator#getResult()
*/
@Override
public Expression getResult() {
this.end();
byte[] bytes = this.classWriter.toByteArray();
try {
Class<?> defineClass = this.classLoader.defineClass(this.className, bytes);
Constructor<?> constructor = defineClass.getConstructor(List.class);
return (Expression) constructor.newInstance(new ArrayList<String>(this.varTokens.keySet()));
}
catch (Exception e) {
throw new CompileExpressionErrorException("define class error", e);
}
}
private void end() {
this.endVisitMethodCode();
this.endVisitClass();
}
/*
* (non-Javadoc)
*
* @see
* com.googlecode.aviator.code.CodeGenerator#onConstant(com.googlecode.aviator
* .lexer.token.Token)
*/
@Override
public void onConstant(Token<?> lookhead) {
if (lookhead == null) {
return;
}
// load token to stack
switch (lookhead.getType()) {
case Number:
// load numbers
NumberToken numberToken = (NumberToken) lookhead;
Number number = numberToken.getNumber();
if (TypeUtils.isBigInt(number)) {
this.mv.visitLdcInsn(numberToken.getLexeme());
this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/type/AviatorBigInt", "valueOf",
"(Ljava/lang/String;)Lcom/googlecode/aviator/runtime/type/AviatorBigInt;");
}
else if (TypeUtils.isDecimal(number)) {
this.mv.visitLdcInsn(numberToken.getLexeme());
this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/type/AviatorDecimal", "valueOf",
"(Ljava/lang/String;)Lcom/googlecode/aviator/runtime/type/AviatorDecimal;");
}
else if (TypeUtils.isDouble(number)) {
this.mv.visitLdcInsn(number);
this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/type/AviatorDouble", "valueOf",
"(D)Lcom/googlecode/aviator/runtime/type/AviatorDouble;");
}
else {
this.mv.visitLdcInsn(number);
this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/type/AviatorLong", "valueOf",
"(J)Lcom/googlecode/aviator/runtime/type/AviatorLong;");
}
pushOperand();
// this.popOperand();
// this.popOperand();
break;
case String:
// load string
this.mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorString");
this.mv.visitInsn(DUP);
this.mv.visitLdcInsn(lookhead.getValue(null));
this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorString", "<init>",
"(Ljava/lang/String;)V");
this.pushOperand(2);
this.popOperand();
this.popOperand();
break;
case Pattern:
// load pattern
this.mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorPattern");
this.mv.visitInsn(DUP);
this.mv.visitLdcInsn(lookhead.getValue(null));
this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorPattern", "<init>",
"(Ljava/lang/String;)V");
this.pushOperand(2);
this.popOperand();
this.popOperand();
break;
case Variable:
// load variable
Variable variable = (Variable) lookhead;
if (variable.equals(Variable.TRUE)) {
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
pushOperand();
}
else if (variable.equals(Variable.FALSE)) {
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
"Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
pushOperand();
}
else if (variable.equals(Variable.NIL)) {
this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorNil", "NIL",
"Lcom/googlecode/aviator/runtime/type/AviatorNil;");
pushOperand();
}
else {
String outterVarName = variable.getLexeme();
String innerVarName = this.innerVarMap.get(outterVarName);
if (innerVarName != null) {
// Is it stored in local?
Map<String, Integer> name2Index = this.labelNameIndexMap.get(this.currentLabel);
if (name2Index != null && name2Index.get(innerVarName) != null) {
int localIndex = name2Index.get(innerVarName);
this.mv.visitVarInsn(ALOAD, localIndex);
pushOperand();
}
else {
// Get field at first time
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitFieldInsn(GETFIELD, this.className, innerVarName,
"Lcom/googlecode/aviator/runtime/type/AviatorJavaType;");
// Variable is used more than once,store it to local
if (this.varTokens.get(outterVarName) > 1) {
this.mv.visitInsn(DUP);
int localIndex = this.getLocalIndex();
this.mv.visitVarInsn(ASTORE, localIndex);
if (name2Index == null) {
name2Index = new HashMap<String, Integer>();
this.labelNameIndexMap.put(this.currentLabel, name2Index);
}
name2Index.put(innerVarName, localIndex);
this.pushOperand(2);
this.popOperand();
this.popOperand();
}
else {
this.pushOperand(1);
this.popOperand();
}
}
}
else {
this.mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorJavaType");
this.mv.visitInsn(DUP);
this.mv.visitLdcInsn(outterVarName);
this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorJavaType",
"<init>", "(Ljava/lang/String;)V");
this.pushOperand(2);
this.popOperand();
this.popOperand();
}
}
break;
}
}
public void initVariables(Map<String, Integer/* counter */> varTokens) {
this.varTokens = varTokens;
for (String outterVarName : varTokens.keySet()) {
// Use inner variable name instead of outter variable name
String innerVarName = this.getInnerName(outterVarName);
this.innerVarMap.put(outterVarName, innerVarName);
this.checkClassAdapter.visitField(ACC_PRIVATE + ACC_FINAL, innerVarName,
"Lcom/googlecode/aviator/runtime/type/AviatorJavaType;", null, null).visitEnd();
}
}
public void initMethods(Map<String, Integer/* counter */> methods) {
this.methodTokens = methods;
for (String outterMethodName : methods.keySet()) {
// Use inner method name instead of outter method name
String innerMethodName = this.getInnerName(outterMethodName);
this.innerMethodMap.put(outterMethodName, innerMethodName);
this.checkClassAdapter.visitField(ACC_PRIVATE + ACC_FINAL, innerMethodName,
"Lcom/googlecode/aviator/runtime/type/AviatorFunction;", null, null).visitEnd();
}
}
private String getInnerName(String varName) {
return FIELD_PREFIX + this.fieldCounter++;
}
private String getInvokeMethodDesc(int paramCount) {
StringBuilder sb = new StringBuilder("(Ljava/util/Map;");
if (paramCount <= 20) {
for (int i = 0; i < paramCount; i++) {
sb.append("Lcom/googlecode/aviator/runtime/type/AviatorObject;");
}
}
else {
for (int i = 0; i < 20; i++) {
sb.append("Lcom/googlecode/aviator/runtime/type/AviatorObject;");
}
// variadic params as an array
sb.append("[Lcom/googlecode/aviator/runtime/type/AviatorObject;");
}
sb.append(")Lcom/googlecode/aviator/runtime/type/AviatorObject;");
return sb.toString();
}
@Override
public void onMethodInvoke(Token<?> lookhead) {
final MethodMetaData methodMetaData = this.methodMetaDataStack.pop();
final int parameterCount = methodMetaData.parameterCount;
if (parameterCount >= 20) {
if (parameterCount == 20) {
// pop the list
this.mv.visitInsn(Opcodes.POP);
this.popOperand();
}
else {
// to array
this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I");
this.mv.visitTypeInsn(Opcodes.ANEWARRAY, "com/googlecode/aviator/runtime/type/AviatorObject");
int arrayIndex = getLocalIndex();
this.mv.visitVarInsn(ASTORE, arrayIndex);
this.mv.visitVarInsn(ALOAD, methodMetaData.variadicListIndex);
this.mv.visitVarInsn(ALOAD, arrayIndex);
this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "toArray",
"([Ljava/lang/Object;)[Ljava/lang/Object;");
this.mv.visitTypeInsn(CHECKCAST, "[Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand(); // pop list to get size
this.pushOperand(); // new array, store and load it
this.pushOperand(); // load list
this.popOperand(); // list.toArray
}
}
this.mv.visitMethodInsn(INVOKEINTERFACE, "com/googlecode/aviator/runtime/type/AviatorFunction", "call",
this.getInvokeMethodDesc(parameterCount));
this.popOperand(); // method object
this.popOperand(); // env map
// pop operands
if (parameterCount <= 20) {
this.popOperand(parameterCount);
}
else {
// 20 params + one array
this.popOperand(21);
}
// push result
pushOperand();
}
@Override
public void onMethodParameter(Token<?> lookhead) {
MethodMetaData currentMethodMetaData = this.methodMetaDataStack.peek();
if (currentMethodMetaData.parameterCount >= 20) {
// Add last param to variadic param list
assert (currentMethodMetaData.variadicListIndex >= 0);
this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
this.mv.visitInsn(Opcodes.POP);
this.mv.visitVarInsn(ALOAD, currentMethodMetaData.variadicListIndex);
this.popOperand(); // pop list
this.popOperand(); // pop param
this.pushOperand(); // list.add result
this.popOperand(); // pop last result
this.pushOperand(); // load list
}
currentMethodMetaData.parameterCount++;
if (currentMethodMetaData.parameterCount == 20) {
// create variadic params list for further params
this.mv.visitTypeInsn(NEW, "java/util/ArrayList");
this.mv.visitInsn(DUP);
this.mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
int listIndex = getLocalIndex();
this.mv.visitVarInsn(ASTORE, listIndex);
this.mv.visitVarInsn(ALOAD, listIndex);
currentMethodMetaData.variadicListIndex = listIndex;
this.pushOperand(); //new list
}
// // add parameter to list
// this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add",
// "(Ljava/lang/Object;)Z");
// // pop boolean
// this.mv.visitInsn(POP);
// this.mv.visitVarInsn(ALOAD,
// this.methodMetaDataStack.peek().parameterListIndex);
}
private void pushOperand() {
this.pushOperand(0);
}
private static class MethodMetaData {
int parameterCount = 0;
int variadicListIndex = -1;
public MethodMetaData(String methodName) {
super();
}
}
private final Stack<MethodMetaData> methodMetaDataStack = new Stack<MethodMetaData>();
@Override
public void onArray(Token<?> lookhead) {
this.onConstant(lookhead);
}
@Override
public void onArrayIndexStart(Token<?> token) {
this.loadEnv();
}
@Override
public void onArrayIndexEnd(Token<?> lookhead) {
this.mv
.visitMethodInsn(
INVOKEVIRTUAL,
"com/googlecode/aviator/runtime/type/AviatorObject",
"getElement",
"(Ljava/util/Map;Lcom/googlecode/aviator/runtime/type/AviatorObject;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
this.popOperand();
this.popOperand();
this.popOperand();
pushOperand();
}
public int getLocalIndex() {
return this.maxLocals++;
}
@Override
public void onMethodName(Token<?> lookhead) {
String outtterMethodName = lookhead.getLexeme();
String innerMethodName = this.innerMethodMap.get(outtterMethodName);
if (innerMethodName != null) {
this.loadAviatorFunction(outtterMethodName, innerMethodName);
}
else {
this.createAviatorFunctionObject(outtterMethodName);
}
this.loadEnv();
this.methodMetaDataStack.push(new MethodMetaData(outtterMethodName));
}
private void loadAviatorFunction(String outterMethodName, String innerMethodName) {
Map<String, Integer> name2Index = this.labelNameIndexMap.get(this.currentLabel);
// Is it stored in local?
if (name2Index != null && name2Index.containsKey(innerMethodName)) {
int localIndex = name2Index.get(innerMethodName);
this.mv.visitVarInsn(ALOAD, localIndex);
pushOperand();
}
else {
this.mv.visitVarInsn(ALOAD, 0);
this.mv.visitFieldInsn(GETFIELD, this.className, innerMethodName,
"Lcom/googlecode/aviator/runtime/type/AviatorFunction;");
// Method is used more than once,store it to local for reusing
if (this.methodTokens.get(outterMethodName) > 1) {
this.mv.visitInsn(DUP);
int localIndex = this.getLocalIndex();
this.mv.visitVarInsn(ASTORE, localIndex);
if (name2Index == null) {
name2Index = new HashMap<String, Integer>();
this.labelNameIndexMap.put(this.currentLabel, name2Index);
}
name2Index.put(innerMethodName, localIndex);
this.pushOperand(1);
this.popOperand();
}
else {
pushOperand();
}
}
}
// private int createArugmentList() {
// // create argument list
// this.pushOperand(0);
// this.pushOperand(0);
// this.mv.visitTypeInsn(NEW, "java/util/ArrayList");
// this.mv.visitInsn(DUP);
// this.popOperand();
// this.mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>",
// "()V");
// // store to local variable
// final int parameterLocalIndex = this.getLocalIndex();
// this.mv.visitVarInsn(ASTORE, parameterLocalIndex);
// this.mv.visitVarInsn(ALOAD, parameterLocalIndex);
// return parameterLocalIndex;
// }
private void loadEnv() {
// load env
pushOperand();
this.mv.visitVarInsn(ALOAD, 1);
}
private void createAviatorFunctionObject(String methodName) {
pushOperand();
this.mv.visitLdcInsn(methodName);
this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/AviatorEvaluator", "getFunction",
"(Ljava/lang/String;)Lcom/googlecode/aviator/runtime/type/AviatorFunction;");
this.popOperand();
pushOperand();
}
@Override
public void onBitAnd(Token<?> lookhead) {
this.doArthOperation("bitAnd");
}
@Override
public void onBitOr(Token<?> lookhead) {
this.doArthOperation("bitOr");
}
@Override
public void onBitXor(Token<?> lookhead) {
this.doArthOperation("bitXor");
}
@Override
public void onShiftLeft(Token<?> lookhead) {
this.doArthOperation("shiftLeft");
}
@Override
public void onShiftRight(Token<?> lookhead) {
this.doArthOperation("shiftRight");
}
@Override
public void onUnsignedShiftRight(Token<?> lookhead) {
this.doArthOperation("unsignedShiftRight");
}
}