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.Iterator;
022import java.util.List;
023
024import lucee.runtime.interpreter.VariableInterpreter;
025import lucee.transformer.bytecode.BytecodeContext;
026import lucee.transformer.bytecode.BytecodeException;
027import lucee.transformer.bytecode.Position;
028import lucee.transformer.bytecode.cast.CastDouble;
029import lucee.transformer.bytecode.cast.CastString;
030import lucee.transformer.bytecode.expression.ExprDouble;
031import lucee.transformer.bytecode.expression.Expression;
032import lucee.transformer.bytecode.expression.ExpressionBase;
033import lucee.transformer.bytecode.expression.var.DataMember;
034import lucee.transformer.bytecode.expression.var.Member;
035import lucee.transformer.bytecode.expression.var.Variable;
036import lucee.transformer.bytecode.literal.LitString;
037import lucee.transformer.bytecode.util.Methods;
038import lucee.transformer.bytecode.util.Types;
039import lucee.transformer.bytecode.visitor.ArrayVisitor;
040
041import org.objectweb.asm.Type;
042import org.objectweb.asm.commons.GeneratorAdapter;
043import org.objectweb.asm.commons.Method;
044
045public class OPUnary extends ExpressionBase implements ExprDouble {
046
047        public static final short POST = 1;
048        public static final short PRE = 2;
049        
050        public static final int CONCAT = 1001314342;
051        public static final int PLUS = OpDouble.PLUS;
052        public static final int MINUS = OpDouble.MINUS;
053        public static final int DIVIDE = OpDouble.DIVIDE;
054        public static final int MULTIPLY = OpDouble.MULTIPLY;
055        
056
057
058        final static Method UNARY_POST_PLUS_1= new Method("unaryPoPl",
059                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
060        final static Method UNARY_POST_PLUS_N= new Method("unaryPoPl",
061                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.DOUBLE_VALUE});
062        
063        final static Method UNARY_POST_MINUS_N= new Method("unaryPoMi",
064                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.DOUBLE_VALUE});
065        final static Method UNARY_POST_MINUS_1= new Method("unaryPoMi",
066                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
067        
068        
069        final static Method UNARY_PRE_PLUS_N= new Method("unaryPrPl",
070                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.DOUBLE_VALUE});
071        final static Method UNARY_PRE_PLUS_1= new Method("unaryPrPl",
072                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
073        
074
075        final static Method UNARY_PRE_MINUS_N= new Method("unaryPrMi",
076                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.DOUBLE_VALUE});
077        final static Method UNARY_PRE_MINUS_1= new Method("unaryPrMi",
078                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
079
080        final static Method UNARY_PRE_MULTIPLY_N= new Method("unaryPrMu",
081                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.DOUBLE_VALUE});
082        final static Method UNARY_PRE_MULTIPLY_1= new Method("unaryPrMu",
083                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
084
085        final static Method UNARY_PRE_DIVIDE_N= new Method("unaryPrDi",
086                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.DOUBLE_VALUE});
087        final static Method UNARY_PRE_DIVIDE_1= new Method("unaryPrDi",
088                        Types.DOUBLE_VALUE, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
089        
090        final static Method UNARY_PRE_CONCAT_N= new Method("unaryPreConcat",
091                        Types.STRING, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY_ARRAY,Types.STRING});
092        final static Method UNARY_PRE_CONCAT_1= new Method("unaryPreConcat",
093                        Types.STRING, new Type[]{Types.PAGE_CONTEXT,Types.COLLECTION_KEY,Types.STRING});
094        
095        
096        
097        
098        
099        final static Method UNARY_POST_PLUS2= new Method("unaryPoPl",
100                        Types.DOUBLE_VALUE, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
101        
102        final static Method UNARY_POST_MINUS2= new Method("unaryPoMi",
103                        Types.DOUBLE_VALUE, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
104        
105        
106        final static Method UNARY_PRE_PLUS2= new Method("unaryPrPl",
107                        Types.DOUBLE_VALUE, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
108        
109
110        final static Method UNARY_PRE_MINUS2= new Method("unaryPrMi",
111                        Types.DOUBLE_VALUE, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
112
113        final static Method UNARY_PRE_MULTIPLY2= new Method("unaryPrMu",
114                        Types.DOUBLE_VALUE, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
115
116        final static Method UNARY_PRE_DIVIDE2= new Method("unaryPrDi",
117                        Types.DOUBLE_VALUE, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.DOUBLE_VALUE});
118        
119        final static Method UNARY_PRE_CONCAT2= new Method("unaryPreConcat",
120                        Types.STRING, new Type[]{Types.COLLECTION,Types.COLLECTION_KEY,Types.STRING});
121        
122        
123        
124        
125        private final Variable var;
126        private Expression value;
127        private final short type;
128        private final int operation;
129
130        public OPUnary(Variable var, Expression value, short type, int operation, Position start, Position end) { 
131                super(start, end);
132                this.var=var;
133                this.value=value;
134                this.type=type;
135                this.operation=operation;
136        }
137
138        @Override
139        public Type _writeOut(BytecodeContext bc, int mode) throws BytecodeException {
140                GeneratorAdapter adapter = bc.getAdapter();
141                // convert value
142                if(operation==CONCAT) value=CastString.toExprString(value);
143                else value=CastDouble.toExprDouble(value);
144                
145                List<Member> members = var.getMembers();
146                int size=members.size();
147                
148                
149                String scope=VariableInterpreter.scopeInt2String(var.getScope());
150                
151                /*
152                 *  (susi.sorglos++ or variables.susi++)
153                 */
154                if((scope==null && size>1) || (scope!=null && size>0)) {
155                        Member last = var.removeMember(members.size()-1);
156                        if(!(last instanceof DataMember)) 
157                                throw new BytecodeException("you cannot use a unary operator with a function "+last.getClass().getName(), getStart());
158                        
159                        
160                        // write the variable
161                        var.setAsCollection(Boolean.TRUE);
162                        var._writeOut(bc, mode);
163                        
164                        
165                        // write out last Key
166                        Variable.registerKey(bc,((DataMember) last).getName());
167                        
168                        // write out value
169                        value.writeOut(bc, MODE_VALUE);
170
171                        
172                        if(type==POST) {
173                                if(operation!=OpDouble.PLUS && operation!=OpDouble.MINUS ) 
174                                        throw new BytecodeException("Post only possible with plus or minus "+operation, value.getStart());
175                                
176                                if(operation==PLUS) adapter.invokeStatic(Types.OPERATOR, UNARY_POST_PLUS2);
177                                else if(operation==MINUS) adapter.invokeStatic(Types.OPERATOR, UNARY_POST_MINUS2);
178                        }
179                        else if(type==PRE) {
180                                
181                                if(operation==PLUS) adapter.invokeStatic(Types.OPERATOR, UNARY_PRE_PLUS2);
182                                else if(operation==MINUS) adapter.invokeStatic(Types.OPERATOR, UNARY_PRE_MINUS2);
183                                else if(operation==DIVIDE) adapter.invokeStatic(Types.OPERATOR, UNARY_PRE_DIVIDE2);
184                                else if(operation==MULTIPLY) adapter.invokeStatic(Types.OPERATOR, UNARY_PRE_MULTIPLY2);
185                                else if(operation==CONCAT) adapter.invokeStatic(Types.OPERATOR, UNARY_PRE_CONCAT2);
186                        }
187                        
188                        if(operation==CONCAT) return Types.STRING;
189                        
190                        // convert from Double to double (if necessary)
191                        if(mode==MODE_REF) {
192                    adapter.invokeStatic(Types.CASTER,Methods.METHOD_TO_DOUBLE_FROM_DOUBLE);
193                    return Types.DOUBLE;
194                }
195                return Types.DOUBLE_VALUE;
196                }
197                
198                
199
200                /*
201                 *  undefined scope only with one key (susi++;)
202                 */
203                
204                // PageContext instance
205                adapter.loadArg(0);
206                
207                
208                // Collection key Array
209                int arrSize=scope!=null?members.size()+1:members.size();
210                boolean useArray = arrSize>1 || scope!=null;
211                if(useArray) {
212                        ArrayVisitor av=new ArrayVisitor();
213                        int index=0;
214                        av.visitBegin(adapter, Types.COLLECTION_KEY, arrSize);
215                                Iterator<Member> it = members.iterator();
216                                Member m;DataMember dm;
217                                
218                                if(scope!=null) {
219                                        av.visitBeginItem(adapter, index++);
220                                        Variable.registerKey(bc,LitString.toExprString(scope));
221                                        av.visitEndItem(adapter);
222                                }
223                                
224                                while(it.hasNext()){
225                                        av.visitBeginItem(adapter, index++);
226                                        m = it.next();
227                                        if(!(m instanceof DataMember)) throw new BytecodeException("you cannot use a unary operator with a function "+m.getClass().getName(), getStart());
228                                        Variable.registerKey(bc,((DataMember) m).getName());
229                                        av.visitEndItem(adapter);
230                                }
231                        av.visitEnd();
232                }
233                else {
234                        Member m = members.iterator().next();
235                        if(!(m instanceof DataMember)) throw new BytecodeException("you cannot use a unary operator with a function "+m.getClass().getName(), getStart());
236                        Variable.registerKey(bc,((DataMember) m).getName());    
237                }
238                
239                if(type==POST) {
240                        if(operation!=OpDouble.PLUS && operation!=OpDouble.MINUS ) throw new BytecodeException("Post only possible with plus or minus "+operation, value.getStart());
241                        
242                        value.writeOut(bc, MODE_VALUE);
243                        if(operation==PLUS) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_POST_PLUS_N:UNARY_POST_PLUS_1);
244                        else if(operation==MINUS) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_POST_MINUS_N:UNARY_POST_MINUS_1);
245                }
246                else if(type==PRE) {
247                        value.writeOut(bc, MODE_VALUE);
248
249                        if(operation==PLUS) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_PRE_PLUS_N:UNARY_PRE_PLUS_1);
250                        else if(operation==MINUS) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_PRE_MINUS_N:UNARY_PRE_MINUS_1);
251                        else if(operation==DIVIDE) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_PRE_DIVIDE_N:UNARY_PRE_DIVIDE_1);
252                        else if(operation==MULTIPLY) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_PRE_MULTIPLY_N:UNARY_PRE_MULTIPLY_1);
253                        else if(operation==CONCAT) adapter.invokeStatic(Types.OPERATOR, useArray?UNARY_PRE_CONCAT_N:UNARY_PRE_CONCAT_1);
254                }
255                
256                if(operation==CONCAT) return Types.STRING;
257                
258                // convert from double to Double (if necessary)
259                if(mode==MODE_REF) {
260            adapter.invokeStatic(Types.CASTER,Methods.METHOD_TO_DOUBLE_FROM_DOUBLE);
261            return Types.DOUBLE;
262        }
263        return Types.DOUBLE_VALUE;
264        }
265}