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