001    package railo.transformer.bytecode.statement.java;
002    
003    import org.objectweb.asm.Type;
004    
005    import railo.commons.lang.ClassException;
006    import railo.commons.lang.ClassUtil;
007    import railo.runtime.reflection.Reflector;
008    import railo.transformer.bytecode.BytecodeContext;
009    import railo.transformer.bytecode.BytecodeException;
010    import railo.transformer.bytecode.expression.Expression;
011    import railo.transformer.bytecode.expression.ExpressionBase;
012    import railo.transformer.bytecode.expression.var.NullExpression;
013    import railo.transformer.bytecode.literal.LitDouble;
014    import railo.transformer.bytecode.literal.LitFloat;
015    import railo.transformer.bytecode.literal.LitInteger;
016    import railo.transformer.bytecode.util.ASMUtil;
017    
018    public class Assign extends ExpressionBase {
019    
020            private String name;
021            private Object value;
022            private DataBag db;
023            private String operator;
024    
025            public Assign(int line,String name,Object value, String operator, DataBag db) {
026                    super(line);
027                    this.name=name;
028                    this.value=value;
029                    this.operator=operator;
030                    this.db = db;
031            }
032    
033            public Type _writeOut(BytecodeContext bc, int mode) throws BytecodeException {
034                    
035                    Integer local=db.locals.get(name);
036                    if(local==null){
037                            throw new BytecodeException("there is no variable declaration for ["+name+"]", getLine());
038                    }
039                    Type t = bc.getAdapter().getLocalType(local.intValue());
040                    if("assign".equals(operator))writeOut(db,bc,t,mode,value,getLine(),false);
041                    else{
042                            new Operation(getLine(), name, value, operator, db).writeOut(bc, mode);
043                    }
044                    dup(bc,t);
045                    bc.getAdapter().storeLocal(local.intValue(),t);
046                    
047                    return t;
048            }
049    
050            public static void dup(BytecodeContext bc, Type t) {
051                    String cn=t.getClassName();
052                    if(cn.equals("long") || cn.equals("double")) bc.getAdapter().dup2();
053                    else bc.getAdapter().dup();
054            }
055    
056            public static Type writeOut(DataBag db,BytecodeContext bc, Type to, int mode,Object value, int line, boolean castExplicit) throws BytecodeException {
057                    Type from;
058                    if(value instanceof Expression)
059                            from=((Expression)value).writeOut(bc, mode);
060                    else {
061                            Integer var=db.locals.get(value);
062                            if(var==null)
063                                    throw new BytecodeException("there is no variable with name ["+value+"] in the enviroment", line);
064                            from=bc.getAdapter().getLocalType(var.intValue());
065                            bc.getAdapter().loadLocal(var.intValue(),from);
066                            
067                    }
068                    if(to!=null && !from.equals(to)){
069                            boolean isRefFrom = ASMUtil.isRefType(from);
070                            boolean isRefTo = ASMUtil.isRefType(to);
071                            
072                            if(castExplicit) {
073                                    Class fc=null,tc=null;
074                            
075                                    if(!isRefFrom && !isRefTo){
076                                            fc = ASMUtil.getValueTypeClass(from,null);
077                                            tc = ASMUtil.getValueTypeClass(to,null);
078                                    }
079                                    else {
080                                            try {
081                                                    fc = ClassUtil.loadClass(from.getClassName());
082                                                    tc = ClassUtil.loadClass(to.getClassName());
083                                            }
084                                            catch (ClassException e) {
085                                                    throw new BytecodeException(e, line);
086                                            }
087                                    }
088                                    if(((tc==boolean.class && fc!=boolean.class))||(fc==boolean.class && tc!=boolean.class))
089                                            throw new BytecodeException("cannot cast from ["+fc.getName()+"] to ["+tc.getName()+"]", line);
090                                    else
091                                            bc.getAdapter().cast(from, to);
092                            }
093                            else {
094                                    
095                                    // unbox
096                                    if(isRefFrom && !isRefTo){
097                                            bc.getAdapter().unbox(to);
098                                            from=ASMUtil.toValueType(from);
099                                            isRefFrom=false;
100                                    }
101                                    // box
102                                    else if(!isRefFrom && isRefTo){
103                                            bc.getAdapter().box(from);
104                                            from=ASMUtil.toRefType(from);
105                                            isRefFrom=true;
106                                    }
107                                    
108                                    
109                                    
110                                    
111                                    // value types
112                                    if(!isRefFrom && !isRefTo){
113                                            Class fc = ASMUtil.getValueTypeClass(from,null);
114                                            Class tc = ASMUtil.getValueTypeClass(to,null);
115                                            if(Reflector.canConvert(fc, tc))
116                                                    bc.getAdapter().cast(from, to);
117                                            else {
118                                                    boolean doThrow=true;
119                                                    if(value instanceof LitDouble){
120                                                            double d=((LitDouble)value).getDoubleValue();
121                                                            if(canConvert(d, tc)){
122                                                                    bc.getAdapter().cast(from, to);
123                                                                    doThrow=false;
124                                                            }
125                                                    }
126                                                    if(value instanceof LitFloat){
127                                                            float f=((LitFloat)value).getFloatValue();
128                                                            if(canConvert(f, tc)){
129                                                                    bc.getAdapter().cast(from, to);
130                                                                    doThrow=false;
131                                                            }
132                                                    }
133                                                    if(value instanceof LitInteger){
134                                                            int i=((LitInteger)value).geIntValue();
135                                                            if(canConvert(i, tc)){
136                                                                    bc.getAdapter().cast(from, to);
137                                                                    doThrow=false;
138                                                            }
139                                                    }
140                                                    
141                                                    if(doThrow)throw new BytecodeException("cannot convert from ["+fc.getName()+"] to ["+tc.getName()+"]", line);
142                                            }
143                                    }
144                                    
145                                    // ref types
146                                    else {
147                                            
148                                            try {
149                                                    Class fc = ClassUtil.loadClass(from.getClassName());
150                                                    Class tc = ClassUtil.loadClass(to.getClassName());
151                                                    if(value instanceof NullExpression || Reflector.isInstaneOf(fc, tc))
152                                                            bc.getAdapter().cast(from, to);
153                                                    else 
154                                                            throw new BytecodeException("cannot convert from ["+fc.getName()+"] to ["+tc.getName()+"]", line);
155                                            } 
156                                            catch (ClassException e) {
157                                                    throw new BytecodeException(e, line);
158                                            }
159                                    }
160                            }
161                    }
162                    return from;
163            }
164            
165            
166    
167            private static boolean canConvert(double d, Class trg) {
168                    if(trg==double.class) return true;
169                    if(trg==float.class) return d==(float)d;
170                    if(trg==long.class) return d==(long)d;
171                    if(trg==int.class) return d==(int)d;
172                    if(trg==char.class) return d==(char)d;
173                    if(trg==short.class) return d==(short)d;
174                    if(trg==byte.class) return d==(byte)d;
175                    
176                    return false;
177            }
178            
179            private static boolean canConvert(float f, Class trg) {
180                    if(trg==double.class) return true;
181                    if(trg==float.class) return f==(float)f;
182                    if(trg==long.class) return f==(long)f;
183                    if(trg==int.class) return f==(int)f;
184                    if(trg==char.class) return f==(char)f;
185                    if(trg==short.class) return f==(short)f;
186                    if(trg==byte.class) return f==(byte)f;
187                    
188                    return false;
189            }
190            
191            private static boolean canConvert(int i, Class trg) {
192                    if(trg==double.class) return true;
193                    if(trg==float.class) return i==(float)i;
194                    if(trg==long.class) return i==(long)i;
195                    if(trg==int.class) return i==(int)i;
196                    if(trg==char.class) return i==(char)i;
197                    if(trg==short.class) return i==(short)i;
198                    if(trg==byte.class) return i==(byte)i;
199                    
200                    return false;
201            }
202    }