/** * 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; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.googlecode.aviator.AviatorEvaluator; import com.googlecode.aviator.Expression; import com.googlecode.aviator.LiteralExpression; import com.googlecode.aviator.code.asm.ASMCodeGenerator; import com.googlecode.aviator.lexer.token.DelegateToken; import com.googlecode.aviator.lexer.token.DelegateToken.DelegateTokenType; import com.googlecode.aviator.lexer.token.NumberToken; import com.googlecode.aviator.lexer.token.OperatorToken; import com.googlecode.aviator.lexer.token.OperatorType; import com.googlecode.aviator.lexer.token.PatternToken; import com.googlecode.aviator.lexer.token.StringToken; import com.googlecode.aviator.lexer.token.Token; import com.googlecode.aviator.lexer.token.Token.TokenType; import com.googlecode.aviator.lexer.token.Variable; import com.googlecode.aviator.runtime.type.AviatorBoolean; import com.googlecode.aviator.runtime.type.AviatorNil; import com.googlecode.aviator.runtime.type.AviatorNumber; import com.googlecode.aviator.runtime.type.AviatorObject; import com.googlecode.aviator.runtime.type.AviatorPattern; import com.googlecode.aviator.runtime.type.AviatorString; /** * Optimized code generator * * @author dennis * */ public class OptimizeCodeGenerator implements CodeGenerator { private final ASMCodeGenerator asmCodeGenerator; private final List<Token<?>> tokenList = new ArrayList<Token<?>>(); private boolean trace = false; public OptimizeCodeGenerator(ClassLoader classLoader, OutputStream traceOutStream, boolean trace) { this.asmCodeGenerator = new ASMCodeGenerator(AviatorEvaluator.getAviatorClassLoader(), traceOutStream, trace); this.trace = trace; } private Map<Integer, DelegateTokenType> getIndex2DelegateTypeMap(OperatorType opType) { Map<Integer, DelegateTokenType> result = new HashMap<Integer, DelegateTokenType>(); switch (opType) { case AND: result.put(2, DelegateTokenType.And_Left); break; case OR: result.put(2, DelegateTokenType.Join_Left); break; case TERNARY: result.put(4, DelegateTokenType.Ternary_Boolean); result.put(2, DelegateTokenType.Ternary_Left); break; } return result; } private int execute() { int exeCount = 0; final int size = this.tokenList.size(); this.printTokenList(); for (int i = 0; i < size; i++) { Token<?> token = this.tokenList.get(i); if (token.getType() == TokenType.Operator) { final OperatorToken op = (OperatorToken) token; final OperatorType operatorType = op.getOperatorType(); final int operandCount = operatorType.getOperandCount(); switch (operatorType) { case FUNC: case INDEX: // Could not optimize function and index call break; default: Map<Integer, DelegateTokenType> index2DelegateType = this.getIndex2DelegateTypeMap(operatorType); final int result = this.executeOperator(i, operatorType, operandCount, index2DelegateType); if (result < 0) { this.compactTokenList(); return exeCount; } exeCount += result; break; } } } this.compactTokenList(); return exeCount; } private int executeOperator(int operatorIndex, final OperatorType operatorType, int operandCount, Map<Integer, DelegateTokenType> index2DelegateType) { Token<?> token = null; operandCount += index2DelegateType.size(); // check if literal expression can be executed boolean canExecute = true; // operand count int count = 0; // operand start index int operandStartIndex = -1; for (int j = operatorIndex - 1; j >= 0; j--) { token = this.tokenList.get(j); if (token == null) { // we must compact token list and retry executing return -1; } final TokenType tokenType = token.getType(); // Check if operand is a literal operand if (!this.isLiteralOperand(token, tokenType, count + 1, index2DelegateType)) { canExecute = false; break; } count++; if (count == operandCount) { operandStartIndex = j; break; } } // if we can execute it on compile if (canExecute) { // arguments AviatorObject[] args = new AviatorObject[operandCount]; int index = 0; for (int j = operandStartIndex; j < operatorIndex; j++) { token = this.tokenList.get(j); if (token.getType() == TokenType.Delegate) { this.tokenList.set(j, null); continue; } args[index++] = this.getAviatorObjectFromToken(token); // set argument token to null this.tokenList.set(j, null); } // execute it now AviatorObject result = operatorType.eval(args); // set result as token to tokenList for next executing this.tokenList.set(operatorIndex, this.getTokenFromOperand(result)); return 1; } return 0; } private boolean isLiteralOperand(Token<?> token, final TokenType tokenType, int index, Map<Integer, DelegateTokenType> index2DelegateType) { switch (tokenType) { case Variable: return token == Variable.TRUE || token == Variable.FALSE || token == Variable.NIL; case Delegate: DelegateTokenType targetDelegateTokenType = index2DelegateType.get(index); if (targetDelegateTokenType != null) { return targetDelegateTokenType == ((DelegateToken) token).getDelegateTokenType(); } break; case Char: case Number: case Pattern: case String: return true; } return false; } private boolean isLiteralToken(Token<?> token) { switch (token.getType()) { case Variable: return token == Variable.TRUE || token == Variable.FALSE || token == Variable.NIL; case Char: case Number: case Pattern: case String: return true; } return false; } /** * Get token from executing result * * @param operand * @return */ private Token<?> getTokenFromOperand(AviatorObject operand) { Token<?> token = null; switch (operand.getAviatorType()) { case Boolean: token = operand.booleanValue(null) ? Variable.TRUE : Variable.FALSE; break; case Nil: token = Variable.NIL; break; case BigInt: case Decimal: case Double: case Long: final Number value = (Number) operand.getValue(null); token = new NumberToken(value, value.toString()); break; case String: final String str = (String) operand.getValue(null); token = new StringToken(str, -1); break; case Pattern: token = new PatternToken(((AviatorPattern) operand).getPattern().pattern(), -1); break; } return token; } private void compactTokenList() { Iterator<Token<?>> it = this.tokenList.iterator(); while (it.hasNext()) { if (it.next() == null) { it.remove(); } } } private AviatorObject getAviatorObjectFromToken(Token<?> lookhead) { AviatorObject result = null; switch (lookhead.getType()) { case Number: // load numbers NumberToken numberToken = (NumberToken) lookhead; Number num = numberToken.getNumber(); result = AviatorNumber.valueOf(num); break; case String: // load string result = new AviatorString((String) lookhead.getValue(null)); break; case Pattern: // load pattern result = new AviatorPattern((String) lookhead.getValue(null)); break; case Variable: if (lookhead == Variable.TRUE) { result = AviatorBoolean.TRUE; } else if (lookhead == Variable.FALSE) { result = AviatorBoolean.FALSE; } else if (lookhead == Variable.NIL) { result = AviatorNil.NIL; } break; case Char: result = new AviatorString(String.valueOf(lookhead.getValue(null))); break; } return result; } @Override public Expression getResult() { // execute literal expression while (this.execute() > 0) { ; } Map<String, Integer/* counter */> variables = new LinkedHashMap<String, Integer>(); Map<String, Integer/* counter */> methods = new HashMap<String, Integer>(); for (Token<?> token : this.tokenList) { switch (token.getType()) { case Variable: String varName = token.getLexeme(); if (!variables.containsKey(varName)) { variables.put(varName, 1); } else { variables.put(varName, variables.get(varName) + 1); } break; case Delegate: DelegateToken delegateToken = (DelegateToken) token; if (delegateToken.getDelegateTokenType() == DelegateTokenType.Method_Name) { Token<?> realToken = delegateToken.getToken(); if (realToken.getType() == TokenType.Variable) { String methodName = token.getLexeme(); if (!methods.containsKey(methodName)) { methods.put(methodName, 1); } else { methods.put(methodName, methods.get(methodName) + 1); } } } else if (delegateToken.getDelegateTokenType() == DelegateTokenType.Array) { Token<?> realToken = delegateToken.getToken(); if (realToken.getType() == TokenType.Variable) { varName = token.getLexeme(); if (!variables.containsKey(varName)) { variables.put(varName, 1); } else { variables.put(varName, variables.get(varName) + 1); } } } break; } } // call asm to generate byte codes this.callASM(variables, methods); // Last token is a literal token,then return a LiteralExpression if (this.tokenList.size() <= 1) { if (this.tokenList.isEmpty()) { return new LiteralExpression(null, new ArrayList<String>(variables.keySet())); } final Token<?> lastToken = this.tokenList.get(0); if (this.isLiteralToken(lastToken)) { return new LiteralExpression(this.getAviatorObjectFromToken(lastToken).getValue(null), new ArrayList<String>(variables.keySet())); } } // get result from asm return this.asmCodeGenerator.getResult(); } private void callASM(Map<String, Integer/* counter */> variables, Map<String, Integer/* counter */> methods) { this.asmCodeGenerator.initVariables(variables); this.asmCodeGenerator.initMethods(methods); this.asmCodeGenerator.start(); for (int i = 0; i < this.tokenList.size(); i++) { Token<?> token = this.tokenList.get(i); switch (token.getType()) { case Operator: OperatorToken op = (OperatorToken) token; switch (op.getOperatorType()) { case ADD: this.asmCodeGenerator.onAdd(token); break; case SUB: this.asmCodeGenerator.onSub(token); break; case MULT: this.asmCodeGenerator.onMult(token); break; case DIV: this.asmCodeGenerator.onDiv(token); break; case MOD: this.asmCodeGenerator.onMod(token); break; case EQ: this.asmCodeGenerator.onEq(token); break; case NEQ: this.asmCodeGenerator.onNeq(token); break; case LT: this.asmCodeGenerator.onLt(token); break; case LE: this.asmCodeGenerator.onLe(token); break; case GT: this.asmCodeGenerator.onGt(token); break; case GE: this.asmCodeGenerator.onGe(token); break; case NOT: this.asmCodeGenerator.onNot(token); break; case NEG: this.asmCodeGenerator.onNeg(token); break; case AND: this.asmCodeGenerator.onAndRight(token); break; case OR: this.asmCodeGenerator.onJoinRight(token); break; case FUNC: this.asmCodeGenerator.onMethodInvoke(token); break; case INDEX: this.asmCodeGenerator.onArrayIndexEnd(token); break; case MATCH: this.asmCodeGenerator.onMatch(token); break; case TERNARY: this.asmCodeGenerator.onTernaryRight(token); break; case BIT_AND: this.asmCodeGenerator.onBitAnd(token); break; case BIT_OR: this.asmCodeGenerator.onBitOr(token); break; case BIT_XOR: this.asmCodeGenerator.onBitXor(token); break; case BIT_NOT: this.asmCodeGenerator.onBitNot(token); break; case SHIFT_LEFT: this.asmCodeGenerator.onShiftLeft(token); break; case SHIFT_RIGHT: this.asmCodeGenerator.onShiftRight(token); break; case U_SHIFT_RIGHT: this.asmCodeGenerator.onUnsignedShiftRight(token); break; } break; case Delegate: DelegateToken delegateToken = (DelegateToken) token; final Token<?> realToken = delegateToken.getToken(); switch (delegateToken.getDelegateTokenType()) { case And_Left: this.asmCodeGenerator.onAndLeft(realToken); break; case Join_Left: this.asmCodeGenerator.onJoinLeft(realToken); break; case Array: this.asmCodeGenerator.onArray(realToken); break; case Index_Start: this.asmCodeGenerator.onArrayIndexStart(realToken); break; case Ternary_Boolean: this.asmCodeGenerator.onTernaryBoolean(realToken); break; case Ternary_Left: this.asmCodeGenerator.onTernaryLeft(realToken); break; case Method_Name: this.asmCodeGenerator.onMethodName(realToken); break; case Method_Param: this.asmCodeGenerator.onMethodParameter(realToken); break; } break; default: this.asmCodeGenerator.onConstant(token); break; } } } private void printTokenList() { if (this.trace) { for (Token<?> t : this.tokenList) { System.out.print(t.getLexeme() + " "); } System.out.println(); } } @Override public void onAdd(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.ADD)); } @Override public void onAndLeft(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.And_Left)); } @Override public void onAndRight(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.AND)); } @Override public void onConstant(Token<?> lookhead) { this.tokenList.add(lookhead); } @Override public void onDiv(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.DIV)); } @Override public void onArrayIndexStart(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Index_Start)); } @Override public void onArrayIndexEnd(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.INDEX)); } @Override public void onArray(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Array)); } @Override public void onEq(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.EQ)); } @Override public void onGe(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.GE)); } @Override public void onGt(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.GT)); } @Override public void onJoinLeft(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Join_Left)); } @Override public void onJoinRight(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.OR)); } @Override public void onLe(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.LE)); } @Override public void onLt(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.LT)); } @Override public void onMatch(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.MATCH)); } @Override public void onMethodInvoke(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.FUNC)); } @Override public void onMethodName(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Method_Name)); } @Override public void onMethodParameter(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Method_Param)); } @Override public void onMod(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.MOD)); } @Override public void onMult(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.MULT)); } @Override public void onNeg(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.NEG)); } @Override public void onNeq(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.NEQ)); } @Override public void onNot(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.NOT)); } @Override public void onSub(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.SUB)); } @Override public void onTernaryBoolean(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Ternary_Boolean)); } @Override public void onTernaryLeft(Token<?> lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Ternary_Left)); } @Override public void onTernaryRight(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.TERNARY)); } @Override public void onBitAnd(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_AND)); } @Override public void onBitNot(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_NOT)); } @Override public void onBitOr(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_OR)); } @Override public void onShiftLeft(Token<?> lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.SHIFT_LEFT)); } @Override public void onShiftRight(Token<?> lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.SHIFT_RIGHT)); } @Override public void onUnsignedShiftRight(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.U_SHIFT_RIGHT)); } @Override public void onBitXor(Token<?> lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_XOR)); } }