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.op;
020
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024
025import lucee.runtime.op.Elvis;
026import lucee.transformer.bytecode.BytecodeContext;
027import lucee.transformer.bytecode.BytecodeException;
028import lucee.transformer.bytecode.Literal;
029import lucee.transformer.bytecode.expression.Expression;
030import lucee.transformer.bytecode.expression.ExpressionBase;
031import lucee.transformer.bytecode.expression.var.DataMember;
032import lucee.transformer.bytecode.expression.var.Member;
033import lucee.transformer.bytecode.expression.var.Variable;
034import lucee.transformer.bytecode.util.ASMUtil;
035import lucee.transformer.bytecode.util.ExpressionUtil;
036import lucee.transformer.bytecode.util.Types;
037import lucee.transformer.bytecode.visitor.ArrayVisitor;
038
039import org.objectweb.asm.Label;
040import org.objectweb.asm.Opcodes;
041import org.objectweb.asm.Type;
042import org.objectweb.asm.commons.GeneratorAdapter;
043import org.objectweb.asm.commons.Method;
044
045public final class OpElvis extends ExpressionBase {
046
047        
048
049    private static final Type ELVIS=Type.getType(Elvis.class);
050    public static final Method INVOKE_STR = new Method(
051                "operate",
052                Types.BOOLEAN_VALUE,
053                new Type[]{Types.PAGE_CONTEXT,Types.DOUBLE_VALUE,Types.STRING_ARRAY});
054        
055    public static final Method INVOKE_KEY = new Method(
056                "operate",
057                Types.BOOLEAN_VALUE,
058                new Type[]{Types.PAGE_CONTEXT,Types.DOUBLE_VALUE,Types.COLLECTION_KEY_ARRAY});
059        
060        private Variable left;
061    private Expression right;
062
063    /**
064     *
065     * @see lucee.transformer.bytecode.expression.ExpressionBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter, int)
066     */
067    public Type _writeOut(BytecodeContext bc, int mode) throws BytecodeException {
068        if(ASMUtil.hasOnlyDataMembers(left))return _writeOutPureDataMember(bc, mode);
069        
070        
071        
072        
073        
074        
075        
076        
077        
078        Label notNull = new Label();
079        Label end = new Label();
080        
081        GeneratorAdapter ga = bc.getAdapter();
082        
083        int l = ga.newLocal(Types.OBJECT);
084        ExpressionUtil.visitLine(bc, left.getStart());
085        left.writeOut(bc, MODE_REF);
086        ExpressionUtil.visitLine(bc, left.getEnd());
087        ga.dup();
088        ga.storeLocal(l);
089        
090        ga.visitJumpInsn(Opcodes.IFNONNULL, notNull);
091        ExpressionUtil.visitLine(bc, right.getStart());
092        right.writeOut(bc, MODE_REF);
093        ExpressionUtil.visitLine(bc, right.getEnd());
094        ga.visitJumpInsn(Opcodes.GOTO, end);
095        ga.visitLabel(notNull);
096        ga.loadLocal(l);
097        ga.visitLabel(end);
098        
099        return Types.OBJECT;
100    }
101    
102    
103    public Type _writeOutPureDataMember(BytecodeContext bc, int mode) throws BytecodeException {
104        // TODO use function isNull for this
105        GeneratorAdapter adapter = bc.getAdapter();
106        
107        Label yes = new Label();
108        Label end = new Label();
109        
110        List<Member> members = left.getMembers();
111        
112        
113        
114        // to array
115        Iterator<Member> it = members.iterator();
116        List<DataMember> list=new ArrayList<DataMember>();
117        while(it.hasNext()){
118                list.add((DataMember) it.next());
119        }
120        DataMember[] arr = list.toArray(new DataMember[members.size()]);
121        
122        ExpressionUtil.visitLine(bc, left.getStart());
123        
124    // public static boolean call(PageContext pc , double scope,String[] varNames)
125        // pc
126        adapter.loadArg(0);
127        // scope
128                adapter.push((double)left.getScope());
129                //varNames
130                
131                // all literal string?
132                boolean allLiteral=true;
133                for(int i=0;i<arr.length;i++){
134                        if(!(arr[i].getName() instanceof Literal)) allLiteral=false;
135                }
136                
137                ArrayVisitor av=new ArrayVisitor();
138                if(!allLiteral) {
139                        // String Array
140                av.visitBegin(adapter,Types.STRING,arr.length);
141                for(int i=0;i<arr.length;i++){
142                                av.visitBeginItem(adapter, i);
143                                        arr[i].getName().writeOut(bc, MODE_REF); 
144                                av.visitEndItem(adapter);
145                }
146                }
147                else {
148                        // Collection.Key Array
149                av.visitBegin(adapter,Types.COLLECTION_KEY,arr.length);
150                for(int i=0;i<arr.length;i++){
151                                av.visitBeginItem(adapter, i);
152                                        Variable.registerKey(bc, arr[i].getName());
153                                av.visitEndItem(adapter);
154                }
155                }
156        av.visitEnd();
157                
158        
159        // allowNull
160        //adapter.push(false);
161                
162        
163        
164                //ASMConstants.NULL(adapter);
165                
166        // call IsDefined.invoke
167        adapter.invokeStatic(ELVIS, allLiteral?INVOKE_KEY:INVOKE_STR);
168                ExpressionUtil.visitLine(bc, left.getEnd());
169        
170        
171        adapter.visitJumpInsn(Opcodes.IFEQ, yes);
172        
173        // left
174        ExpressionUtil.visitLine(bc, left.getStart());
175        left.writeOut(bc, MODE_REF);
176        ExpressionUtil.visitLine(bc, left.getEnd());
177        adapter.visitJumpInsn(Opcodes.GOTO, end);
178        
179        // right
180        ExpressionUtil.visitLine(bc, right.getStart());
181        adapter.visitLabel(yes);
182        right.writeOut(bc, MODE_REF);
183        ExpressionUtil.visitLine(bc, right.getEnd());
184        adapter.visitLabel(end);
185        
186        return Types.OBJECT;
187        
188    }
189    
190
191    
192    
193    
194    
195    
196    private OpElvis(Variable left, Expression right) {
197        super(left.getStart(),right.getEnd());
198        this.left=left;
199        this.right=right;  
200    }
201    
202
203    public static Expression toExpr(Variable left, Expression right) {
204        return new OpElvis(left,right);
205    }
206}
207