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 }