001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.transformer.bytecode.util;
020
021import java.util.HashMap;
022import java.util.Map;
023
024import lucee.commons.lang.CFTypes;
025import lucee.commons.lang.ExceptionUtil;
026import lucee.commons.lang.StringUtil;
027import lucee.runtime.op.Caster;
028import lucee.transformer.bytecode.BodyBase;
029import lucee.transformer.bytecode.BytecodeContext;
030import lucee.transformer.bytecode.BytecodeException;
031import lucee.transformer.bytecode.Position;
032import lucee.transformer.bytecode.Statement;
033import lucee.transformer.bytecode.expression.ExprString;
034import lucee.transformer.bytecode.expression.Expression;
035import lucee.transformer.bytecode.literal.LitString;
036import lucee.transformer.bytecode.visitor.OnFinally;
037import lucee.transformer.bytecode.visitor.TryFinallyVisitor;
038
039import org.objectweb.asm.Opcodes;
040import org.objectweb.asm.Type;
041import org.objectweb.asm.commons.GeneratorAdapter;
042import org.objectweb.asm.commons.Method;
043
044public final class ExpressionUtil {
045        
046        public static final Method START = new Method(
047                        "exeLogStart",
048                        Types.VOID,
049                        new Type[]{Types.INT_VALUE,Types.STRING});
050        public static final Method END = new Method(
051                        "exeLogEnd",
052                        Types.VOID,
053                        new Type[]{Types.INT_VALUE,Types.STRING});
054
055        public static final Method CURRENT_LINE = new Method(
056                        "currentLine",
057                        Types.VOID,
058                        new Type[]{Types.INT_VALUE});
059        
060
061        private static Map<String,String> last=new HashMap<String,String>();
062
063        public static void writeOutExpressionArray(BytecodeContext bc, Type arrayType, Expression[] array) throws BytecodeException {
064        GeneratorAdapter adapter = bc.getAdapter();
065        adapter.push(array.length);
066        adapter.newArray(arrayType);
067        for (int i = 0; i < array.length; i++) {
068            adapter.dup();
069            adapter.push(i);
070            array[i].writeOut(bc, Expression.MODE_REF);
071            adapter.visitInsn(Opcodes.AASTORE);
072        }
073    }
074
075    /**
076     * visit line number
077     * @param adapter
078     * @param line
079     * @param silent id silent this is ignored for log
080     */
081    public static synchronized void visitLine(BytecodeContext bc, Position pos) {
082        if(pos!=null){
083                visitLine(bc, pos.line);
084        }
085   }
086    private static synchronized void visitLine(BytecodeContext bc, int line) {
087        if(line>0){
088                
089                /*Type[] methodTypes = bc.getMethod().getArgumentTypes();
090                        if(methodTypes!=null && methodTypes.length>0 && methodTypes[0].equals(Types.PAGE_CONTEXT)) {
091                        GeneratorAdapter adapter = bc.getAdapter();
092                adapter.loadArg(0);
093                adapter.checkCast(Types.PAGE_CONTEXT_IMPL);
094                adapter.push(line);
095                    adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL,CURRENT_LINE );
096                        }*/
097                
098                if(!(""+line).equals(last.get(bc.getClassName()+":"+bc.getId()))){
099                        
100                        
101                        
102                        bc.visitLineNumber(line);
103                        last.put(bc.getClassName()+":"+bc.getId(),""+line);
104                        last.put(bc.getClassName(),""+line);
105                }
106        }
107   }
108
109        public static synchronized void lastLine(BytecodeContext bc) {
110        int line = Caster.toIntValue(last.get(bc.getClassName()),-1);
111        visitLine(bc, line);
112    }
113
114        /**
115         * write out expression without LNT
116         * @param value
117         * @param bc
118         * @param mode
119         * @throws BytecodeException
120         */
121        public static void writeOutSilent(Expression value, BytecodeContext bc, int mode) throws BytecodeException {
122                Position start = value.getStart();
123                Position end = value.getEnd();
124                value.setStart(null);
125                value.setEnd(null);
126                value.writeOut(bc, mode);
127                value.setStart(start);
128                value.setEnd(end);
129        }
130        public static void writeOut(Expression value, BytecodeContext bc, int mode) throws BytecodeException {
131                value.writeOut(bc, mode);
132        }
133
134        public static void writeOut(final Statement s, BytecodeContext bc) throws BytecodeException {
135                if(ExpressionUtil.doLog(bc)) {
136                final String id=BodyBase.id();
137                TryFinallyVisitor tfv=new TryFinallyVisitor(new OnFinally() {
138                        public void _writeOut(BytecodeContext bc) {
139                                ExpressionUtil.callEndLog(bc, s,id);
140                        }
141                },null);
142                
143                tfv.visitTryBegin(bc);
144                        ExpressionUtil.callStartLog(bc, s,id);
145                        s.writeOut(bc);
146                tfv.visitTryEnd(bc)     ;
147        }
148        else s.writeOut(bc);
149        }
150
151        public static short toShortType(ExprString expr,boolean alsoAlias, short defaultValue) {
152                if(expr instanceof LitString){
153                        return CFTypes.toShort(((LitString)expr).getString(),alsoAlias,defaultValue);
154                }
155                return defaultValue;
156        }
157
158        public static void callStartLog(BytecodeContext bc, Statement s, String id) {
159                call_Log(bc, START, s.getStart(),id);
160        }
161        public static void callEndLog(BytecodeContext bc, Statement s, String id) {
162                call_Log(bc, END, s.getEnd(),id);
163        }
164
165        private static void call_Log(BytecodeContext bc, Method method, Position pos, String id) {
166        if(!bc.writeLog() || pos==null || (StringUtil.indexOfIgnoreCase(bc.getMethod().getName(),"call")==-1))return;
167        try{
168                GeneratorAdapter adapter = bc.getAdapter();
169                adapter.loadArg(0);
170                //adapter.checkCast(Types.PAGE_CONTEXT_IMPL);
171                adapter.push(pos.pos);
172                adapter.push(id);
173                    adapter.invokeVirtual(Types.PAGE_CONTEXT, method);
174                }
175                catch(Throwable t) {
176                        ExceptionUtil.rethrowIfNecessary(t);
177                        t.printStackTrace();
178                }               
179        }
180
181        public static boolean doLog(BytecodeContext bc) {
182                return bc.writeLog() && StringUtil.indexOfIgnoreCase(bc.getMethod().getName(),"call")!=-1;
183        }
184}